send_mail.anubis 6.46 KB
/*
 * Created by PyramIDE.
 * User: ricard
 * Date: 22/12/2008
 * Time: 17:49
 * 
 */


read tools/basis.anubis   
read tools/connections.anubis
read tools/printable_tree.anubis
read network/dns.anubis
read network/tools.anubis
read system/files.anubis
read system/string.anubis
read system/data_io.anubis
read system/logger.anubis

read calexium_lib/net_services_protocols/logger_service.anubis

read lexers/enhanced_status.anubis
read smtp_server_extensions.anubis
read authentication.anubis
read smtp_client.anubis

public define String send_mail_log    = "SendMail".
public define LogMask send_mail_mask  = logMask("send_mail").

define Int        smtp_time_out     = 300.  //5 minutes of timeout

  public type SendMailResult:
    ok,
    error,
    reply(Int code, String enhanced_status, List(String) lines).
  
public type Str_HostName: 
  str_host_name(String str).

public type Str_Sender: 
  str_sender(String str).
  
public type Str_Recipient:
  str_recipient(String str).

public type Str_UID:
  str_UID(String str).
  
public type Send_Param:
  send_param( Str_HostName  host_name,
              Str_Sender    sender,
              Str_Recipient recipient,
              List(Data_IO) mail_parts,
              SmtpAuth      auth,
              Str_UID       mail_uid,
              Int           last_smtp_code  //0 means it's the first time
              ).
            



define Result(SmtpClientResult, One)
  sm_MAIL_FROM
  (                                            
    RWStream                  conn,     
    Send_Param                param,
    SmtpClientSession         sm_session,
    (Int) -> One              progress_report,
    (LogLevel, String) -> One logger
  ) =
  with enhanced_status = sm_session.enhanced_status,
  //send MAIL FROM
  if send_mail_from(conn, param.sender.str, sm_session, smtp_time_out, logger) is
  {
    error(result)   then error(result), 
    ok(_)           then
      //send RCPT TO
      if send_recipient(conn, param.recipient.str, sm_session, smtp_time_out, logger) is
      {
        error(result)   then error(result), 
        ok(_)           then
          //send DATA
          if send_data(conn, sm_session, smtp_time_out, logger) is
          {
            error(result)   then error(result), 
            ok(_)           then
              if send_content(conn, param.mail_parts, sm_session, smtp_time_out, progress_report, logger) is
              {
                error(result)   then error(result), 
                ok(_)           then
                  send_quit(conn, sm_session, smtp_time_out, logger);
                  logger(logTrace, "SENT Mail FROM "+param.sender.str+" TO "+param.recipient.str);
                  ok(unique)
              }
          }
      }
  }.

  
   Apply the whole protocol:
   
   // we can have a list of part file in param type. This can be very useful when we want
   // to send file as mailing. For the mailing, in each mail, only the header is different of the 
   // other mails. The body part is the same. Then, the mailing sender can generate the header in one 
   // file and keep the body in other file wich can be given to send_mail function as last file in the list
public define Result(SmtpClientResult, One)
  send_mail
  (
    RWStream        conn,     
    Send_Param      param,
    (SmtpClientSession, Send_Param) -> Result(SmtpClientResult, Send_Param)   prepare_mail_callback,
    (Int) -> One    progress_report,
    (LogLevel, String) -> One logger
  ) =
   //
   // Our connection to the SMTP server is opened. We just have to apply the protocol.
   //
  if receive_reply(conn, false, smtp_time_out, logger) is
  {
    error               then  error(error),
    timeout             then  error(timeout),
    reply(code, status, lines)  then 
      if code = 220 then
        //send EHLO
        if send_ehlo(conn, param.host_name.str, smtp_time_out, logger) is
        {
          error(result)       then error(result),
          ok(sm_session) then
              if do_auth(conn, param.auth, sm_session, smtp_time_out, logger) is
              {
                error(auth_result) then error(auth_result),
                ok(_) then
                  if prepare_mail_callback(sm_session, param) is
                  {
                    error(prepare_result) then error(prepare_result),
                    ok(new_param)         then sm_MAIL_FROM(conn, new_param, sm_session, progress_report, logger)
                  }
              }
        }
      else if code = 550 then
        //some server answer "550 5.7.1 Client host rejected: cannot find your reverse hostname, [88.181.64.17]"
        //before anything, then we try to extract the enhanced status if exists for relaying the mail with the ISP
        logger(logDebug, "Converting a 550 error for connection to a 432 error as distant server may be temporary overloaded.");
        error(reply(432, status, lines))
      else 
        error(reply(code, status, lines))
  }.

public define Result(SmtpClientResult, One)
  send_mail
  (
    RWStream        conn,     
    Send_Param      param,
    (SmtpClientSession, Send_Param) -> Result(SmtpClientResult, Send_Param)   prepare_mail_callback,
  ) =
  send_mail(conn, 
            param, 
            prepare_mail_callback,
            (Int _) |-> unique,
            (LogLevel level, String txt) |-> if level is logTrace then logTrace(send_mail_log, send_mail_mask, txt)
                                                                  else log(level, send_mail_log, txt)
            ).

public define Result(SmtpClientResult, One)
  send_mail
  (
    RWStream        conn,     
    Send_Param      param,
  ) =
  send_mail(conn, 
            param, 
            (SmtpClientSession sm_session, Send_Param param) |-> ok(param)
            ).

public define Result(SmtpClientResult, One)
  send_mail
  (
    String          server,
    Word32          port,
    Send_Param      param,
  )=
  if resolve_address(server) is
  {
    failure           then  
      logWarning(send_mail_log,"IP address NOT found for Mail server ["+server+"]");
      error(error),

    success(ip_addr)  then
      with ip_txt = ip_addr_to_string(ip_addr),
      logTrace(send_mail_log, send_mail_mask, "send_mail: Trying to connect to "+ip_txt);

      if (Result(NetworkConnectError,RWStream))connect(ip_addr, port) is 
      {
        error(e)               then 
          logTrace(send_mail_log, send_mail_mask, "send_mail: Can't connect to "+ip_txt); 
          error(error),
      
        ok(server_conn)        then 
          send_mail(server_conn, param),
      }
  }.