/****************************************************************************/ /* SENDIT.CMD - an ka9q compatible OS/2 smtp client */ /* Copyright (C) 1995 Alex Chapman */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* */ /* Requires rxsock.zip from IBM Employee Written Software */ /* */ /* */ /* Last Modified: 18th July, 1995 */ Version = 1.23 /****************************************************************************/ /************************************************************/ /* Change History */ /************************************************************/ /* 0.1 950117 First version */ /* 0.11 950118 Fixed . --> .. conversion */ /* 0.12 950119 Added progress indicator for long notes */ /* 0.14 950127 Issue QUIT before closing socket */ /* 0.15 950129 Fixed dot transparency rfc821 4.5.2 */ /* 0.16 950131 Removed GNU license for purposes of testing */ /* 0.17 950204 Implement a control queue for a control prog*/ /* 0.50 950205 Final Beta Release */ /* 1.00 950211 First Release */ /* 1.01 950416 added logfile parameter */ /* 1.02 950418 added code to read KA9Q variable */ /* 1.03 950505 removed setting of send buffer */ /* 1.04 950505 correct logfile parameter to sendit.log */ /* 1.20 950510 read settings from sendit.ini */ /* 1.21 950521 moved call to readinifile */ /* 1.22 950621 use WARPDIS as rexx queue */ /* 1.23 950718 move queue settings into ini file */ /************************************************************/ arg gnu rest port = 25 /* SMTP port */ crlf = d2c(13)||d2c(10) /* CR + LF */ ControlQ = '' /* Control Queue */ CurrentQ = '' /* Current Queue */ Say 'SENDIT.CMD - OS/2 SMTP client (version' version')' Say 'Copyright (C) 1995 Alex Chapman' Say "SENDIT comes with ABSOLUTELY NO WARRANTY; for details type 'SENDIT w'." Say 'This is free software, and you are welcome to redistribute it under certain' Say "conditions; type `SENDIT c' for details." Say call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs' call SysLoadFuncs Call ReadINIFile 'SENDIT.INI', 'SENDIT' Select When gnu = 'C' Then Do Call ShowConditions Exit 0 End When gnu = 'W' Then Do Call ShowWarranty Exit 0 End When gnu = 'H' | gnu = '?' Then Do Exit 0 End When gnu = 'Q' Then Do Say 'The Q parameter is now obsolete, and has been superceded by the use of' Say 'the ini settings queue_messages and queue_name' Exit 0 End When gnu<>'' Then Do Say 'Invalid parameter. Process terminated.' Exit 0 End Otherwise End If queue_messages = 'YES' Then Do ControlQ = queue_name CurrentQ = RXQUEUE('Create', ControlQ) If CurrentQ<>ControlQ Then Do Call RXQUEUE 'Delete', CurrentQ End CurrentQ = RXQUEUE('Set', ControlQ) Call SendMsg ' START' End Call RxFuncAdd 'SockLoadFuncs', 'RxSock', 'SockLoadFuncs' Call SockLoadFuncs('QUIET') if Right(mqueue, 1)<>'\' Then mqueue = mqueue || '\' Call SysFileTree mqueue||'*.wrk', 'file', 'FO' If file.0 = 0 Then Do Say 'No mail queued' Call SendMsg ' STOP SENDIT 0' Exit 0 End If file.0 = 1 Then Do Say 'One mail item to deliver' End Else Do Say file.0 'mail items to deliver' End retcode = SockGetHostByName(server, 'host.!') If retcode = 0 Then Do Say 'SockGetHostByName()' errno Call SendMsg ' FAIL SOCK' errno Exit errno End server = host.!addr; Say 'SMTPSERVER' server heloplace = SockGetHostID() retcode = SockGetHostByAddr(heloplace, 'host.!') If retcode = 0 Then Do Say 'SockGetHostByName()' errno Call SendMsg ' FAIL SOCK' errno Exit errno End heloplace = host.!name Say 'local host' heloplace /* Open Socket */ socket = SockSocket('AF_INET', 'SOCK_STREAM', 0) If socket < 0 Then Do Say 'SockSocket()' errno Call SendMsg ' FAIL SOCK' errno Exit errno End /* I'm not sure why I need to do this, but it seems to be the only thing that will allow me to deliver large notes. Without this, SockSend() reports good return codes, even when post hasn't received anything. Consequently, I go ploughing on sending stuff, and post never seems to catch up. Anyway, setting SO_SNDBUF to 0 is a workaround */ /* Since applying the PPP upgrade (950501) attempting to set the send buffer to zero has caused the program to hang. Upon commenting out this line everything appears to work fine. If you have problems with this program try changing the value of sendbuffer_patch from 0 to 1 near the top */ If sendbuffer_patch = 1 Then Do retcode = SockSetSockOpt(socket, "SOL_SOCKET", "SO_SNDBUF", "0") If retcode < 0 Then Do Say 'SockSetSockOpt()' errno Call SendMsg ' FAIL SOCK' errno Exit errno End End signal on halt Call Log '-------------------------------------------------------------' Call Log 'SENDIT version' version 'started' date() time() Call Log 'local host' heloplace /* Connect Socket */ server.!family = 'AF_INET' server.!port = port server.!addr = server retcode = SockConnect(socket,'server.!') If retcode < 0 Then Do Say 'SockConnect()' errno Call SendMsg ' FAIL SOCK' errno Exit errno End /* Get response from connect */ reply = GetResponse(socket) If reply<>220 Then Do Say reply 'from server after connect. Expected 220.' Call SendMsg ' FAIL NNTP' reply Call halt End data = 'HELO' heloplace || crlf Call MySockSend socket, data reply = GetResponse(socket) If reply<>250 Then Do Say reply 'from server after HELO' heloplace' - Expected 250.' Call SendMsg ' FAIL NNTP' reply Call halt End Do i = 1 to file.0 Parse upper var file.i stem'.WRK' lock = stem || '.LCK' note = stem || '.TXT' work = stem || '.WRK' number = FileSpec("name", stem) If chars(lock)=0 Then Do /* The mail item isn't locked */ retcode = stream(lock, 'c', 'open write') retcode = Stream(work, 'c', 'open read') Parse value linein(work) with domain Parse value linein(work) with from Parse value linein(work) with to retcode = Stream(work, 'c', 'close') retcode = SendMail(socket, to, from, note) If retcode=0 Then Do /* mail was transmitted successfully */ Call SysFileDelete work Call SysFileDelete note Say 'Mail ('i'/'file.0') posted ('number')' from 'to' to Call Log 'Mail ('i'/'file.0') posted ('number')' from 'to' to End Else Do /* Presently can't get here */ Say 'Mail ('i'/'file.0') failed ('number') return 'RC from 'to' to Call Log 'Mail ('i'/'file.0') failed ('number') return 'RC from 'to' to End retcode = Stream(lock, 'c', 'close') Call SysFileDelete lock End Else Do Say 'Mail ('i'/'file.0') locked ('number')' from 'to' to Call Log 'Mail ('i'/'file.0') locked ('number')' from 'to' to End End Call SendMsg ' STOP SENDIT' file.0 Call Log 'process complete' /* Close socket */ halt: If CurrentQ <> '' Then Do Call RXQUEUE 'Set', CurrentQ End Say 'Quitting...' Call MySockSend socket, 'QUIT'crlf Say 'Closing socket...' retcode = SockSoClose(socket) If retcode < 0 Then Do Say 'SockSoClose()' errno Exit errno End Exit 0 SendMail: Procedure expose ControlQ CurrentQ crlf Parse arg socket, to, from, note retcode = Stream(note, 'c', 'open read') If retcode <> 'READY:' Then Do Say 'note' note 'missing' Return 1 End Else Do data = 'MAIL FROM:<'from'>'crlf Call MySockSend socket, data reply = GetResponse(socket) If reply<>250 Then Do Say reply 'from server after MAIL FROM:<'from'> - Expected 250.' Call halt End data = 'RCPT TO:<'to'>'crlf Call MySockSend socket, data reply = GetResponse(socket) If reply<>250 Then Do Say reply 'from server after RCPT TO:<'to'> - Expected 250.' Call halt End data = 'DATA'crlf Call MySockSend socket, data reply = GetResponse(socket) If reply<>354 Then Do Say reply 'from server after DATA - Expected 354.' Call halt End line = 0 Do While Lines(note)<>0 line = line + 1 data.line = LINEIN(note) End Parse value ProgressIndicator(0, line, row, col) with row, col Do i = 1 to line If Left(data.i, 1) = '.' Then data.i = '.' || data.i data = data.i || crlf Call MySockSend socket, data Parse value ProgressIndicator(i, line, row, col) with row, col End data = crlf||'.'||crlf Call MySockSend socket, data reply = GetResponse(socket) If reply<>250 Then Do Say reply 'from server after .CRLF - Expected 250.' Call halt End retcode = Stream(note, 'c', 'close') Return 0 End /* Shouldn't really get here */ Return 99 MySockSend: Procedure Parse arg socket, data retcode = 0 Do While retcode < Length(data) retcode = SockSend(socket, data) If retcode < 0 Then Do Say 'SockSend()' errno Call SendMsg ' FAIL SOCK' errno Exit errno End If retcode < Length(data) Then Do data = Substr(data, retcode + 1) retcode = 0 End End Return ProgressIndicator: Procedure arg current, total, row','col If current = 0 Then Do /* Initialise */ Say Say Parse value SysCurPos() with row col row = row - 2 Call SysCurState('OFF') End If current = total Then Do Call SysCurState('ON') End Call SysCurPos row,col progress = Trunc( current / total * 20) Say Copies(d2c(219), progress)||Copies(d2c(254), 20-progress)||, '['Trunc(current / total*100)'%]' Return row','col /* smtp should only ever send single line replies, and we're only really interested in the first reply code bit */ GetResponse: Procedure expose ControlQ CurrentQ crlf Parse arg socket . buffer = '' Do While Pos(crlf, buffer) = 0 retcode = SockRecv(socket, 'data', 10000) buffer = buffer || data End Parse var buffer reply . Return reply Log: Procedure expose logfile crlf ControlQ CurrentQ Parse arg line retcode = Stream(logfile, 'c', 'open write') retcode = LINEOUT(logfile, line) retcode = Stream(logfile, 'c', 'close') Return SendMsg: Procedure expose ControlQ CurrentQ Parse arg message If ControlQ <> '' & ControlQ <> 'CONTROLQ' Then Do Queue message End Return ReadINIFile: arg inifile, application file = SysSearchPath('PATH',inifile) If file = '' Then Do Say 'Unable to find' inifile Exit 1 End app = '' ini. = 0 retcode = Stream(file, 'c', 'open read') If retcode <> 'READY:' Then Do Say 'Unable to open' file Exit 2 End Do While Lines(file) <> 0 line = LINEIN(file) If Left(line, 1) = '[' Then Do Parse Upper var line '[' app ']' . End Else Do If line <> '' & Left(line, 1) <> '#' Then Do If app = '' Then Do Say 'Invalid line in' file 'expected [application_name]' Exit 1 End If app = application | app = 'DEFAULT' Then Do Parse var line varname '=' varvalue Parse Upper var varname varname varname = Strip(varname) varvalue = Strip(varvalue) If ini.varname = 0 | app = application Then Do retcode = Value(varname, varvalue) ini.varname = 1 End End End End End retcode = Stream(file, 'c', 'close') Return ShowWarranty: Say 'Because the program is licensed free of charge, there is no warranty' Say 'for the program, to the extent permitted by applicable law. Except when' Say 'otherwise stated in writing the copyright holders and/or other parties' Say 'provide the program "as is" without warranty of any kind, either expressed' Say 'or implied, including, but not limited to, the implied warranties of' Say 'merchantability and fitness for a particular purpose. The entire risk as' Say 'to the quality and performance of the program is with you. Should the' Say 'program prove defective, you assume the cost of all necessary servicing,' Say 'repair or correction.' Say Say 'Read the GNU PUBLIC LICENSE for full details' Return ShowConditions: Say 'You may copy and distribute verbatim copies of the Program''s' Say 'source code as you receive it, in any medium, provided that you' Say 'conspicuously and appropriately publish on each copy an appropriate' Say 'copyright notice and disclaimer of warranty; keep intact all the' Say 'notices that refer to this License and to the absence of any warranty;' Say 'and give any other recipients of the Program a copy of this License' Say 'along with the Program.' Say Say 'Read the GNU PUBLIC LICENSE for full details' Return