/* 3 march 2011. danielh@crosslink.net
OS/2 script to send email via SMTP

Notes on user configurable parameters:

   
   You probably need to set the SENDER_NAME and SENDER_PASSWORD parameters.

   You should set the SENDER_EMAIL paraemter
        end_message='-------------'
        end_message_2=1
   If you set end_message='' then SNDMAILX will use a heuristic to 
   determine when there is no  more "message body" coming through standard 
   input (it will wait a  few seconds).

*/


/****** Begin USER CONFIGURABLE PARAMETERS   ****/


/* Required. An SMTP gateway. */
GATEWAY='smtp.crosslink.net' 

/* optional: name of sender. If left blank, or =0, a generic name is used  */

SENDER_EMAIL='danielh@crosslink.net' 

AUTH_PASSWORD='bowwow'
AUTH_NAME='danielh'

defto='danielh@crosslink.net'

/* optional: set verbose=1 to echo status stuff, 2 more stuff, 0 for quiet operation */
VERBOSE=20


/****** End USER CONFIGURABLE PARAMETERS   ****/

/* ----  */
call charout,"Recipient email: " ; parse pull address_list
if address_list='' then address_list=defto

say "Message (end with a . on an otherwise empty line) ";
amess=''
do m= 1 to 1000
   call charout, ': '
   parse pull aline
   amess=amess||aline||'0d0a'x
   if strip(aline)='.' then leave

end 


foo=dsvyMailit(address_list,amess, gateway,sender_email,auth_name,auth_password,verbose)
if foo='1' then say 'ok'

if (foo<>'1') then say " Problem: "  foo


exit



/* ============================================================================ 
dsvyMailit: A  REXX procedure to mail a note to several recipients.
 
   Called as:  status=dsvyMailit(address_list,message_string,smtp_gateway,optional_senders_address,
                                 sender_name,sender_password,verbose)
 
  address_list: A space delimited list of addresses
  message_string: A message string, that may contain CRLFs (and may be rather long)
  smtp_gateway: The address of your SMTP server 
  optional_senders_address: The sender's address (optional)
         If sender's address is not given, it will be generated using the servername() function.
  sender_name and sender_password: used for authentication
  verbose : error reporting (0 is least)
 
  Example:
  status=mailit("BOB@HIS.NET" , "This is my message ", "MAIL.MY.ORG",'me1@my.org','me','pwd1',2 )

  Note: message should contain headers and the message body, seperated by a blank line
 
Status:
   0 error message - a problem
   1  - success 

    
  Note on recipient addresses:
     The message will be sent to each address.  Since the addresses are
     sent and then the message, this allows for fairly rapid mailing to multiple
     individuals.
 
 Acknowledgment:
      This is a modification of a routine written by Dave Briccetti     daveb@davebsoft.com, http://www.davebsoft.com
      It has been modified by Daniel Hellerstein (last revision March 2011)
     
 
============================================================================ */

dsvyMailit:procedure 
parse arg whotos , themessage  , smtpaddress ,  sender_address, sender_name, sender_password,verbose

err_message=''

smtpaddress=strip(smtpaddress)
sender_address=strip(sender_address)

/* set some constants */
CRLF                    = '0d0a'x
TRUE                    = 1
FALSE                   = 0
REPLYTYPE_OK            = '2'   /* SMTP reply code first byte */
REPLY_START_MAIL_INPUT  = '354' /* SMTP reply code */

if sender_address="" then do
   sendinghost=sre_servername()
   sendinguser="WebServer"
   sender_address=sendinguser||'@'||sendinghost
end
 
MailServer  = smtpaddress  /* Mail server, leave blank if you are connected through a LAN */

if \RxFuncQuery("SockLoadFuncs") then do 
   nop
end 
else do
   call RxFuncAdd "SockLoadFuncs","rxSock","SockLoadFuncs"
   call SockLoadFuncs
end

if EstablishProtocol() = FALSE then do
    err_message=err_message||' || protocol could not be established'
    if verbose>1 then do
        call sre_pmprintf('#MailIt: protocol could not be established')
    end 
    return 0||' '||err_message
end 

/* The protocol initiated, we'll now send the message to each
   recipient */
isgood=1
call SendMsgToEachRecipient
/* QUIT ends the protocol */
 
CmdReply = TransactSmtpCommand(socket, 'QUIT')

/* Close the socket */
    
call SockSoClose socket
if isgood=1 then err_message=''
return isgood||' '||err_message



/* ========================================================================= */
EstablishProtocol:
/* ========================================================================= */

socket = ConnectToMailServer(MailServer)    
if socket <= 0 then do
    err_message=err_message||' || Could not connect to mail server: 'socket
    if verbose>0 then do
      call sre_pmprintf( '#MailIt. Could not connect to mail server: 'socket)
    end
    return FALSE
end

CmdReply = GetCmdReply(socket)

if left(CmdReply, 1) \= REPLYTYPE_OK then do
    rrr=space(translate(cmdreply,' ','0d0a09'x))
    err_message=err_message||' || Could not establish protocol: 'rrr
    if verbose>0 then  do
       call sre_pmprintf( '#MailIt: Could not establish protocol: 'rrr)
    end
    return FALSE
end


/* Send the extended hello, in case this SMTP server supports
   SMTP extensions */
        
CmdReply = TransactSmtpCommand(socket, 'EHLO '||smtpaddress)
SizeExtensionSupported = 0
simpleAuthSupported=0
        
if left(CmdReply, 1) = REPLYTYPE_OK then do
    /* That worked, so enable extended SMTP processing.  If
       the response to the EHLO indicates support for SIZE,
       enable our use of that feature */
              
    SmtpExtensionsSupported = TRUE
    if pos('250 SIZE', CmdReply) > 0 | pos('250-SIZE', CmdReply) > 0 then
        SizeExtensionSupported = 1

/* and check for auth login */
    if pos('AUTH PLAIN ', CmdReply) > 0 | pos('AUTH=PLAIN ', CmdReply) > 0 then
        simpleAuthSupported = 1


end
else do
    /* The server didn't recognize the EHLO so we'll go with
       the regular HELO */
    rrr=space(translate(cmdreply,' ','0d0a09'x))
    err_message=err_message||' || EHLO not recognized:' rrr 
    if verbose>1 then do
        call sre_pmprintf("#MailIt: EHLO not recognized:" rrr)
    end
    SmtpExtensionsSupported = FALSE
    SizeExtensionSupported  = FALSE
    CmdReply = TransactSmtpCommand(socket, 'HELO '||smtpaddress)

end
if left(CmdReply, 1) = REPLYTYPE_OK then  do
    return TRUE
end
else do
   rrr=space(translate(cmdreply,' ','0d0a09'x))
   err_message=err_message||' || HELO not recognized:' rrr 
    if verbose>1 then do
       call sre_pmprintf("#MailIt: HELO not recognized:" rrr)
    end
    return FALSE
end


/* ========================================================================= */
SendMsgBody: 
/* ========================================================================= */

/* DATA tells the server that the body of the message is coming.  It
   should reply with a code meaning "go ahead." */

CmdReply = TransactSmtpCommand(socket, 'DATA')
if substr(CmdReply, 1, 3) = REPLY_START_MAIL_INPUT then do
    /* Send the data, followed by a '.' on a line by itself to 
       indicate the end of the message */
    rrr=translate(cmdreply,' ','0d0a09'x)
    if verbose>2 then call sre_pmprintf('MailIt. Okay on DATA: 'rrr)
    CmdReply = TransactSmtpCommand(socket, MsgFileContents || CRLF || '.',1)
    rrr=translate(cmdreply,' ','0d0a09'x)
    if verbose>2 then call sre_pmprintf('MailIt. Okay on DATA body: 'rrr)
end
else do
   rrr=space(translate(cmdreply,' ','0d0a09'x))
   err_message=err_message||' || Problem on DATA: 'rrr
   if verbose>2 then do
      call sre_pmprintf('#MailIt. Problem on DATA: 'rrr)
   end
   isgood=0
end

return


/* ========================================================================= */
SendMsgToEachRecipient:
/* ========================================================================= */


/* send an AUTH */
if simpleAuthSupported=1 then do
  anauth=MKPACK64('00'x||sender_name||'00'x||sender_password)
  CmdReply = TransactSmtpCommand(socket, 'AUTH PLAIN '||anauth)
  cmdReply=strip(cmdreply)
  if left(cmdReply,1)<>'2' then do
       isgood=0
       err_message=cmdReply
       return 
   end

end

msgfilecontents=themessage
MsgFileContents = strip(MsgFileContents, 't', '1a'x) 
        
/* MAIL FROM identifies the sender.  The SIZE= extension
   provides the size of the message, to allow the 
   server to quickly refuse messages bigger than it wants. */

/* Note : many smtp gateways only accept mail from a limited set
          of domains. Make sure the domain portion of sender_address
          is one of these  */
           
MailFromCmd = 'MAIL FROM:' ||sender_address

if SizeExtensionSupported then
    MailFromCmd = MailFromCmd ' SIZE=' || length(MsgFileContents)
    
CmdReply = TransactSmtpCommand(socket, MailFromCmd)
if left(CmdReply, 1) = REPLYTYPE_OK then do
  whotos=translate(whotos,' ','000d0a1a09'x)
  do while whotos<> ""
     parse var whotos whoto whotos
     RecipientEmailAddress = strip(whoto)
     if RecipientEmailAddress \= '' then do   /* RCPT identifies the intended recipient of the message */
         CmdReply = TransactSmtpCommand(socket,,
                         "RCPT TO:" || RecipientEmailAddress)
        if verbose>2 then do
          rrr=space(translate(cmdreply,' ','0d0a09'x))
          call sre_pmprintf("MailIt: RCPT TO: response is "rrr)
        end
     end
  end 
  call SendMsgBody
end
else do
  if verbose>1 then do
     rrr=space(translate(cmdreply,' ','0d0a09'x))
     call pmprintf("#MailIt: MAIL FROM rejected: " rrr)
  end
  isgood=0
end
return


/* ========================================================================= */
ConnectToMailServer: procedure expose verbose isgood err_message ehlopause
/* ========================================================================= */

parse arg MailServer
socket = 0
ehlopause=1    /*  1=add a pause on EHLO requests -- some smtp servers need this */

/* Open a socket to the mail server.  (The Sock* functions are
   documented in the REXX Socket book in the Information folder
   in the OS/2 System folder */

call SockInit
if SockGetHostByName(MailServer, 'host.!') = 0 then do
    err_message=err_message||'  Could not get host by name' errno' ' h_errno
    if verbose>0 then do
         call sre_pmprintf('#MailIt. Could not get host by name' errno' ' h_errno)
    end
end
else do
    socket = SockSocket('AF_INET','SOCK_STREAM',0)
    address.!family = 'AF_INET'
    address.!port = 25          /* the standard SMTP port */
    address.!addr = host.!addr
    if SockConnect(socket, 'address.!') = -1 then  do
      err_message=err_message||'   Could not connect socket' errno' ' h_errno
      if verbose>0 then do
         call sre_pmprintf( '#MailIt. Could not connect socket' errno' ' h_errno)
      end
    end
end
return socket


/* ========================================================================= */
GetCmdReply: procedure expose verbose  isgood err_message
/* ========================================================================= */

parse arg socket

CRLF = '0d0a'x

/* Receive the response to the SMTP command into a variable.  Use
   more than one socket read if necessary to collect the whole 
   response. */
if SockRecv(socket, 'CmdReply', 200) < 0 then do
    err_message=err_message||' Error reading from socket' errno' ' h_errno
    if verbose>0 then  do
       call sre_pmprintf('#MailIt. Error reading from socket' errno' ' h_errno)
    end
    exit 0
end
ReadCount = 1
MaxParts = 10

do while ReadCount < MaxParts & right(CmdReply, 2) \= CRLF  
    if SockRecv(socket, 'CmdReplyExtra', 200) < 0 then do
        if verbose>0 then  do
           call sre_pmprintf('#MailIt. Error reading from socket')
        end
        exit 0
    end
    CmdReply = CmdReply || CmdReplyExtra
    ReadCount = ReadCount + 1
end
/* a=sre_pmprintf(cmdReply,1) */
return CmdReply


/* ========================================================================= */
TransactSmtpCommand: 
/* ========================================================================= */

parse arg socket, Cmd,noverbose

/* Send a command to the SMTP server, echoing it to the display
   if requested */

if verbose>1  & noverbose<>1 then  call sre_pmprintf(" Command is "  Cmd)
rc = SockSend(socket, Cmd || CRLF)

if ehlopause=1 then do
  If (Pos('EHLO', Cmd) <> 0) Then Call SysSleep 1
end

op=GetCmdReply(socket)

return op



/* create a base64 packing of a message */
MKPACK64:procedure 
parse arg mess

biga=xrange('A','Z')||xrange('a','z')||xrange('0','9')||'+/'

s2=x2b(c2x(mess))

nith=trunc((length(s2)/6)+.9)
cont=copies(' ',nith)
oof=""
do mm=0 to 63
      oof=oof||x2c(b2x(right('00'||x2b(d2x(mm)),6)))
end /* do */
do ith=1 to nith 
  a1=substr(s2,(ith*6)-5,6,0)
  cont=overlay(x2c(b2x(a1)),cont,ith) 
end /* do */
pint=""
pint=translate(cont,biga,oof)
j1=length(pint)//4
if j1<>0 then pint=pint||copies('=',4-j1)
return pint






