home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / wrpdis20.zip / SENDIT.CMD < prev    next >
OS/2 REXX Batch file  |  1996-04-21  |  16KB  |  468 lines

  1. /****************************************************************************/
  2. /*  SENDIT.CMD - an ka9q compatible OS/2 smtp client                        */
  3. /*  Copyright (C) 1995,1996 Alex Chapman <alex@budgetweb.com>               */
  4. /*                                                                          */
  5. /*  This program is free software; you can redistribute it and/or modify    */
  6. /*  it under the terms of the GNU General Public License as published by    */
  7. /*  the Free Software Foundation; either version 2 of the License, or       */
  8. /*  (at your option) any later version.                                     */
  9. /*                                                                          */
  10. /*  This program is distributed in the hope that it will be useful,         */
  11. /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
  12. /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           */
  13. /*  GNU General Public License for more details.                            */
  14. /*                                                                          */
  15. /*  You should have received a copy of the GNU General Public License       */
  16. /*  along with this program; if not, write to the Free Software             */
  17. /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               */
  18. /*                                                                          */
  19. /*  Requires rxsock.zip from IBM Employee Written Software                  */
  20. /*  <ftp://src.doc.ic.ac.uk/packages/os2/ibm/ews/rxsock.zip>                */
  21. /*                                                                          */
  22. /*  Last Modified: 21st April, 1996                                          */
  23.     Version = 1.25
  24. /****************************************************************************/
  25.  
  26. /************************************************************/
  27. /* Change History                                           */
  28. /************************************************************/
  29. /* 0.1  950117  First version                               */
  30. /* 0.11 950118  Fixed . --> .. conversion                   */
  31. /* 0.12 950119  Added progress indicator for long notes     */
  32. /* 0.14 950127  Issue QUIT before closing socket            */
  33. /* 0.15 950129  Fixed dot transparency rfc821 4.5.2         */
  34. /* 0.16 950131  Removed GNU license for purposes of testing */
  35. /* 0.17 950204  Implement a control queue for a control prog*/
  36. /* 0.50 950205  Final Beta Release                          */
  37. /* 1.00 950211  First Release                               */
  38. /* 1.01 950416  added logfile parameter                     */
  39. /* 1.02 950418  added code to read KA9Q variable            */
  40. /* 1.03 950505  removed setting of send buffer              */
  41. /* 1.04 950505  correct logfile parameter to sendit.log     */
  42. /* 1.20 950510  read settings from sendit.ini               */
  43. /* 1.21 950521  moved call to readinifile                   */
  44. /* 1.22 950621  use WARPDIS as rexx queue                   */
  45. /* 1.23 950718  move queue settings into ini file           */
  46. /* 1.24 960421  handle multiple recipients in wrk file      */
  47. /* 1.25 960421  handle multi line responses from smtp daemon*/
  48. /************************************************************/
  49.  
  50. arg gnu rest
  51.  
  52. port = 25                                           /* SMTP port     */
  53. crlf = d2c(13)||d2c(10)                             /* CR + LF       */
  54. ControlQ = ''                                       /* Control Queue */
  55. CurrentQ = ''                                       /* Current Queue   */
  56.  
  57. Say 'SENDIT.CMD - OS/2 SMTP client (version' version')'
  58. Say 'Copyright (C) 1995 Alex Chapman'
  59. Say "SENDIT comes with ABSOLUTELY NO WARRANTY; for details type 'SENDIT w'."
  60. Say 'This is free software, and you are welcome to redistribute it under certain'
  61. Say "conditions; type `SENDIT c' for details."
  62. Say
  63.  
  64. call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
  65. call SysLoadFuncs
  66.  
  67. Call ReadINIFile 'SENDIT.INI', 'SENDIT'
  68.  
  69. Select
  70.   When gnu = 'C' Then Do
  71.     Call ShowConditions
  72.     Exit 0
  73.   End
  74.   When gnu = 'W' Then Do
  75.     Call ShowWarranty
  76.     Exit 0
  77.   End
  78.   When gnu = 'H' | gnu = '?' Then Do
  79.     Exit 0
  80.   End
  81.   When gnu = 'Q' Then Do
  82.     Say 'The Q parameter is now obsolete, and has been superceded by the use of'
  83.     Say 'the ini settings queue_messages and queue_name'
  84.     Exit 0
  85.   End
  86.   When gnu<>'' Then Do
  87.     Say 'Invalid parameter.  Process terminated.'
  88.     Exit 0
  89.   End
  90.   Otherwise
  91. End
  92.  
  93. If queue_messages = 'YES' Then Do
  94.   ControlQ = queue_name
  95.   CurrentQ = RXQUEUE('Create', ControlQ)
  96.   If CurrentQ<>ControlQ Then Do
  97.     Call RXQUEUE 'Delete', CurrentQ
  98.   End
  99.   CurrentQ = RXQUEUE('Set', ControlQ)
  100.   Call SendMsg '<SENDIT> START'
  101. End
  102.  
  103. Call RxFuncAdd 'SockLoadFuncs', 'RxSock', 'SockLoadFuncs'
  104. Call SockLoadFuncs('QUIET')
  105.  
  106. if Right(mqueue, 1)<>'\' Then mqueue = mqueue || '\'
  107. Call SysFileTree mqueue||'*.wrk', 'file', 'FO'
  108.  
  109. If file.0 = 0 Then Do
  110.   Say 'No mail queued'
  111.   Call SendMsg '<SENDIT> STOP SENDIT 0'
  112.   Exit 0
  113. End
  114.  
  115. If file.0 = 1 Then Do
  116.   Say 'One mail item to deliver'
  117. End
  118. Else Do
  119.   Say file.0 'mail items to deliver'
  120. End
  121.  
  122. retcode = SockGetHostByName(server, 'host.!')
  123. If retcode = 0 Then Do
  124.   Say 'SockGetHostByName()' errno
  125.   Call SendMsg '<SENDIT> FAIL SOCK' errno
  126.   Exit errno
  127. End
  128.  
  129. server = host.!addr;
  130.  
  131. Say 'SMTPSERVER' server
  132.  
  133. heloplace = SockGetHostID()
  134.  
  135. retcode = SockGetHostByAddr(heloplace, 'host.!')
  136. If retcode = 0 Then Do
  137.   Say 'SockGetHostByName()' errno
  138.   Call SendMsg '<SENDIT> FAIL SOCK' errno
  139.   Exit errno
  140. End
  141.  
  142. heloplace = host.!name
  143.  
  144. Say 'local host' heloplace
  145.  
  146. /* Open Socket */
  147. socket  = SockSocket('AF_INET', 'SOCK_STREAM', 0)
  148. If socket < 0 Then Do
  149.   Say 'SockSocket()' errno
  150.   Call SendMsg '<SENDIT> FAIL SOCK' errno
  151.   Exit errno
  152. End
  153.  
  154. /* I'm not sure why I need to do this, but it seems to be the only
  155.    thing that will allow me to deliver large notes.  Without this,
  156.    SockSend() reports good return codes, even when post hasn't received
  157.    anything.  Consequently, I go ploughing on sending stuff, and post
  158.    never seems to catch up. Anyway, setting SO_SNDBUF to 0 is a workaround */
  159.  
  160. /* Since applying the PPP upgrade (950501) attempting to set the send buffer
  161.    to zero has caused the program to hang.  Upon commenting out this line
  162.    everything appears to work fine.  If you have problems with this program
  163.    try changing the value of sendbuffer_patch from 0 to 1 near the top     */
  164.  
  165. If sendbuffer_patch = 1 Then Do
  166.   retcode = SockSetSockOpt(socket, "SOL_SOCKET", "SO_SNDBUF", "0")
  167.   If retcode < 0 Then Do
  168.     Say 'SockSetSockOpt()' errno
  169.     Call SendMsg '<SENDIT> FAIL SOCK' errno
  170.     Exit errno
  171.   End
  172. End
  173.  
  174. signal on halt
  175.  
  176. Call Log '-------------------------------------------------------------'
  177. Call Log 'SENDIT version' version 'started' date() time()
  178. Call Log 'local host' heloplace
  179.  
  180. /* Connect Socket */
  181. server.!family = 'AF_INET'
  182. server.!port   = port
  183. server.!addr   = server
  184.  
  185. retcode = SockConnect(socket,'server.!')
  186. If retcode < 0 Then Do
  187.   Say 'SockConnect()' errno
  188.   Call SendMsg '<SENDIT> FAIL SOCK' errno
  189.   Exit errno
  190. End
  191.  
  192. /* Get response from connect */
  193. reply = GetResponse(socket)
  194. If reply<>220 Then Do
  195.   Say reply 'from server after connect.  Expected 220.'
  196.   Call SendMsg '<SENDIT> FAIL NNTP' reply
  197.   Call halt
  198. End
  199.  
  200. data = 'HELO' heloplace || crlf
  201. Call MySockSend socket, data
  202.  
  203. reply = GetResponse(socket)
  204. If reply<>250 Then Do
  205.   Say reply 'from server after HELO' heloplace' - Expected 250.'
  206.   Call SendMsg '<SENDIT> FAIL NNTP' reply
  207.   Call halt
  208. End
  209.  
  210. Do i = 1 to file.0
  211.   Parse upper var file.i stem'.WRK'
  212.   lock = stem || '.LCK'
  213.   note = stem || '.TXT'
  214.   work = stem || '.WRK'
  215.   number = FileSpec("name", stem)
  216.   If chars(lock)=0 Then Do        /* The mail item isn't locked */
  217.     retcode = stream(lock, 'c', 'open write')
  218.     retcode = Stream(work, 'c', 'open read')
  219.     Parse value linein(work) with domain
  220.     Parse value linein(work) with from
  221.     numto = 0
  222.     Do While Lines(work) <> 0
  223.       numto = numto + 1
  224.       Parse value linein(work) with to.numto
  225.     End
  226.     retcode = Stream(work, 'c', 'close')
  227.     doneto = 0
  228.     Do j = 1 to numto
  229.       retcode = SendMail(socket, to.j, from, note)
  230.       If retcode=0 Then Do      /* mail was transmitted successfully */
  231.         Say 'Mail ('i'/'file.0') posted ('number')' from 'to' to.j
  232.         Call Log 'Mail ('i'/'file.0') posted ('number')' from 'to' to.j
  233.         doneto = doneto + 1
  234.       End
  235.       Else Do
  236.         Say 'Mail ('i'/'file.0') failed ('number') return 'RC from 'to' to
  237.         Call Log 'Mail ('i'/'file.0') failed ('number') return 'RC from 'to' to
  238.       End
  239.     End
  240.     If doneto = numto Then Do
  241.       Call SysFileDelete work
  242.       Call SysFileDelete note
  243.     End
  244.     retcode = Stream(lock, 'c', 'close')
  245.     Call SysFileDelete lock
  246.   End
  247.   Else Do
  248.     Say 'Mail ('i'/'file.0') locked ('number')' from 'to' to
  249.     Call Log 'Mail ('i'/'file.0') locked ('number')' from 'to' to
  250.   End
  251. End
  252.  
  253. Call SendMsg '<SENDIT> STOP SENDIT' file.0
  254. Call Log 'process complete'
  255.  
  256. /* Close socket */
  257. halt:
  258.   If CurrentQ <> '' Then Do
  259.     Call RXQUEUE 'Set', CurrentQ
  260.   End
  261.   Say 'Quitting...'
  262.   Call MySockSend socket, 'QUIT'crlf
  263.   Say 'Closing socket...'
  264.   retcode = SockSoClose(socket)
  265.   If retcode < 0 Then Do
  266.     Say 'SockSoClose()' errno
  267.     Exit errno
  268.   End
  269.   Exit 0
  270.  
  271. SendMail: Procedure expose ControlQ CurrentQ crlf
  272.  
  273.   Parse arg socket, to, from, note
  274.   retcode = Stream(note, 'c', 'open read')
  275.   If retcode <> 'READY:' Then Do
  276.     Say  'note' note 'missing'
  277.     Return 1
  278.   End
  279.   Else Do
  280.     data = 'MAIL FROM:<'from'>'crlf
  281.     Call MySockSend socket, data
  282.     reply = GetResponse(socket)
  283.     If reply<>250 Then Do
  284.       Say reply 'from server after MAIL FROM:<'from'> - Expected 250.'
  285.       Call halt
  286.     End
  287.     data = 'RCPT TO:<'to'>'crlf
  288.     Call MySockSend socket, data
  289.     reply = GetResponse(socket)
  290.     If reply<>250 Then Do
  291.       Say reply 'from server after RCPT TO:<'to'> - Expected 250.'
  292.       Call halt
  293.     End
  294.     data = 'DATA'crlf
  295.     Call MySockSend socket, data
  296.     reply = GetResponse(socket)
  297.     If reply<>354 Then Do
  298.       Say reply 'from server after DATA - Expected 354.'
  299.       Call halt
  300.     End
  301.     line = 0
  302.     Do While Lines(note)<>0
  303.       line = line + 1
  304.       data.line = LINEIN(note)
  305.     End
  306.     Parse value ProgressIndicator(0, line, row, col) with row, col
  307.     Do i = 1 to line
  308.       If Left(data.i, 1) = '.' Then data.i = '.' || data.i
  309.       data = data.i || crlf
  310.       Call MySockSend socket, data
  311.       Parse value ProgressIndicator(i, line, row, col) with row, col
  312.     End
  313.     data = crlf||'.'||crlf
  314.     Call MySockSend socket, data
  315.     reply = GetResponse(socket)
  316.     If reply<>250 Then Do
  317.       Say reply 'from server after .CRLF - Expected 250.'
  318.       Call halt
  319.     End
  320.     retcode = Stream(note, 'c', 'close')
  321.     Return 0
  322.   End
  323.   /* Shouldn't really get here */
  324.   Return 99
  325.  
  326. MySockSend: Procedure
  327.  
  328.   Parse arg socket, data
  329.   retcode = 0
  330.   Do While retcode < Length(data)
  331.     retcode = SockSend(socket, data)
  332.     If retcode < 0 Then Do
  333.       Say 'SockSend()' errno
  334.       Call SendMsg '<SENDIT> FAIL SOCK' errno
  335.       Exit errno
  336.     End
  337.     If retcode < Length(data) Then Do
  338.       data = Substr(data, retcode + 1)
  339.       retcode = 0
  340.     End
  341.   End
  342.   Return
  343.  
  344. ProgressIndicator:  Procedure
  345.  
  346.   arg current, total, row','col
  347.   If current = 0 Then Do /* Initialise */
  348.     Say
  349.     Say
  350.     Parse value SysCurPos() with row col
  351.     row = row - 2
  352.     Call SysCurState('OFF')
  353.   End
  354.   If current = total Then Do
  355.     Call SysCurState('ON')
  356.   End
  357.   Call SysCurPos row,col
  358.   progress = Trunc( current / total * 20)
  359.   Say Copies(d2c(219), progress)||Copies(d2c(254), 20-progress)||,
  360.       '['Trunc(current / total*100)'%]'
  361.   Return row','col
  362.  
  363. /* smtp should only ever send single line replies, and we're
  364.    only really interested in the first reply code bit        */
  365.  
  366. GetResponse: Procedure expose ControlQ CurrentQ crlf
  367.  
  368.   Parse arg socket .
  369.   buffer = ''
  370.   Do Until Datatype(reply)='NUM'
  371.     Do While Pos(crlf, buffer) = 0
  372.       retcode = SockRecv(socket, 'data', 10000)
  373.       If retcode < 0 Then Do
  374.         Say 'SockRecv()' errno
  375.         Call SendMsg '<SENDIT> FAIL SOCK' errno
  376.         Exit errno
  377.       End
  378.       buffer = buffer || data
  379.     End
  380.     Parse value Left(buffer, Pos(crlf, buffer) - 1) with reply .
  381.     buffer = Substr(buffer, Pos(crlf, buffer) + 2)
  382.   End
  383.   Return reply
  384.  
  385. Log: Procedure expose logfile crlf ControlQ CurrentQ
  386.  
  387.   Parse arg line
  388.   retcode = Stream(logfile, 'c', 'open write')
  389.   retcode = LINEOUT(logfile, line)
  390.   retcode = Stream(logfile, 'c', 'close')
  391.   Return
  392.  
  393. SendMsg: Procedure expose ControlQ CurrentQ
  394.  
  395.   Parse arg message
  396.   If ControlQ <> '' & ControlQ <> 'CONTROLQ' Then Do
  397.     Queue message
  398.   End
  399.   Return
  400.  
  401. ReadINIFile:
  402.  
  403.   arg inifile, application
  404.   file = SysSearchPath('PATH',inifile)
  405.   If file = '' Then Do
  406.     Say 'Unable to find' inifile
  407.     Exit 1
  408.   End
  409.   app = ''
  410.   ini. = 0
  411.   retcode = Stream(file, 'c', 'open read')
  412.   If retcode <> 'READY:' Then Do
  413.     Say 'Unable to open' file
  414.     Exit 2
  415.   End
  416.   Do While Lines(file) <> 0
  417.     line = LINEIN(file)
  418.     If Left(line, 1) = '[' Then Do
  419.       Parse Upper var line '[' app ']' .
  420.     End
  421.     Else Do
  422.       If line <> '' & Left(line, 1) <> '#' Then Do
  423.         If app = '' Then Do
  424.           Say 'Invalid line in' file 'expected [application_name]'
  425.           Exit 1
  426.         End
  427.         If app = application | app = 'DEFAULT' Then Do
  428.           Parse var line varname '=' varvalue
  429.           Parse Upper var varname varname
  430.           varname = Strip(varname)
  431.           varvalue = Strip(varvalue)
  432.           If ini.varname = 0 | app = application Then Do
  433.             retcode = Value(varname, varvalue)
  434.             ini.varname = 1
  435.           End
  436.         End
  437.       End
  438.     End
  439.   End
  440.   retcode = Stream(file, 'c', 'close')
  441.   Return
  442.  
  443. ShowWarranty:
  444.   Say 'Because the program is licensed free of charge, there is no warranty'
  445.   Say 'for the program, to the extent permitted by applicable law.  Except when'
  446.   Say 'otherwise stated in writing the copyright holders and/or other parties'
  447.   Say 'provide the program "as is" without warranty of any kind, either expressed'
  448.   Say 'or implied, including, but not limited to, the implied warranties of'
  449.   Say 'merchantability and fitness for a particular purpose.  The entire risk as'
  450.   Say 'to the quality and performance of the program is with you.  Should the'
  451.   Say 'program prove defective, you assume the cost of all necessary servicing,'
  452.   Say 'repair or correction.'
  453.   Say
  454.   Say 'Read the GNU PUBLIC LICENSE for full details'
  455.   Return
  456.  
  457. ShowConditions:
  458.   Say 'You may copy and distribute verbatim copies of the Program''s'
  459.   Say 'source code as you receive it, in any medium, provided that you'
  460.   Say 'conspicuously and appropriately publish on each copy an appropriate'
  461.   Say 'copyright notice and disclaimer of warranty; keep intact all the'
  462.   Say 'notices that refer to this License and to the absence of any warranty;'
  463.   Say 'and give any other recipients of the Program a copy of this License'
  464.   Say 'along with the Program.'
  465.   Say
  466.   Say 'Read the GNU PUBLIC LICENSE for full details'
  467.   Return
  468.