/* * 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 xlib/mail/send_mail_type.anubis read app/app_loggers.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 String xlib = "Xlib". public define LogMask send_mail_mask = logMask("send_mail"). define Int _sendmail_time_out = 300. //5 minutes of timeout public type SendMailResult: ok, error, reply(Int code, String enhanced_status, List(String) lines). public type Smtp_Recipient: smtp_recipient(String email, //email address of the recipient Send_Tries nb_tries, Int uid, //parameter used by handle_status (mail_uid) Int uid2 //same (contact_id) ). public define Smtp_Recipient smtp_recipient ( String recipient )= smtp_recipient(recipient, send_tries(0), 0,0) . public type Send_Param: send_param( Str_HostName host_name, //domain of the sender like "mail.calexium.com" Str_Sender sender, //sender email address List(Data_IO) mail_parts, //all parts of the email to send List(Smtp_Recipient) recipients, //recipient email address 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, //TCP connection Str_Sender sender, //email address of the sender List(Data_IO) mail_parts, //all parts to send Smtp_Recipient recipient, //contain email address and uid SmtpClientSession sm_session, //smtp session (Int) -> One progress_report, //don't know what it is for the moment (LogLevel, String) -> One logger //logger ) = with enhanced_status = sm_session.enhanced_status, since recipient is smtp_recipient(rcpt_address, try, uid, uid2), //send MAIL FROM if send_mail_from(conn, sender.str, sm_session, _sendmail_time_out, logger) is { error(result) then error(result), ok(_) then //send RCPT TO if send_recipient(conn, rcpt_address, sm_session, _sendmail_time_out, logger) is { error(result) then error(result), ok(_) then //send DATA if send_data(conn, sm_session, _sendmail_time_out, logger) is { error(result) then error(result), ok(_) then if rewind(mail_parts) then if send_content(conn, mail_parts, sm_session, _sendmail_time_out, progress_report, logger) is { error(result) then error(result), ok(_) then //send_quit(conn, sm_session, _sendmail_time_out, logger); logger(logTrace, "SENT Mail FROM "+sender.str+" TO "+rcpt_address); ok(unique) } else error(error) } } }. define Result(SmtpClientResult, One) send_to_recipients_list ( RWStream conn, //TCP connection Send_Param param, List(Smtp_Recipient) recipients, //recipients with email to send SmtpClientSession sm_session, //smtp session (Int) -> One progress_report, //don't know what it is for the moment (LogLevel, String) -> One logger, //logger (Result(SmtpClientResult, One), Smtp_Recipient) -> One handle_result, Bool unique_recipient )= if recipients is { [] then ok(unique), [ rcpt . tail ] then with result = sm_MAIL_FROM(conn, param.sender, param.mail_parts, rcpt, sm_session, progress_report, logger), //here we apply the function provided by application to handle the result handle_result(result, rcpt); //if there is another recipient we send a RSET and we are able to send the //email to the next recipient within the same smtp session if length(tail) > 0 then if send_rset(conn, sm_session, _sendmail_time_out, logger) is { error(_result) then error(_result), ok(_) then send_to_recipients_list(conn, param, tail, sm_session, progress_report, logger, handle_result, unique_recipient) } else if unique_recipient then result else 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, (Result(SmtpClientResult, One), Smtp_Recipient) -> One handle_result ) = // // Our connection to the SMTP server is opened. We just have to apply the protocol. // if receive_reply(conn, false, _sendmail_time_out, logger) is { error then error(error), timeout then error(timeout), no_auth_method then error(no_auth_method), bad_reply then error(bad_reply), reply(code, status, lines) then if code = 220 then //send EHLO if send_ehlo(conn, param.host_name.str, _sendmail_time_out, logger) is { error(result) then error(result), ok(sm_session) then if do_auth(conn, param.auth, sm_session, _sendmail_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 with result = if length(new_param.recipients) > 1 then send_to_recipients_list(conn, new_param, new_param.recipients, sm_session, progress_report, logger, handle_result, false) else send_to_recipients_list(conn, new_param, new_param.recipients, sm_session, progress_report, logger, handle_result, true), send_quit(conn, sm_session, _sendmail_time_out, logger); result } } } 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, (Result(SmtpClientResult, One), Smtp_Recipient) -> One handle_result ) = send_mail(conn, param, prepare_mail_callback, (Int _) |-> unique, (LogLevel level, String txt) |-> if level is logTrace then log(xlib, logDebug, send_mail_log, txt) else log(xlib, level, send_mail_log, txt), handle_result ). public define Result(SmtpClientResult, One) send_mail ( RWStream conn, Send_Param param, (Result(SmtpClientResult, One), Smtp_Recipient) -> One handle_result ) = send_mail(conn, param, (SmtpClientSession sm_session, Send_Param parameter) |-> ok(parameter), handle_result ). public define Result(SmtpClientResult, One) send_mail ( String server, Word32 port, Send_Param param, )= if resolve_address(server) is { failure then //println("IP address NOT found for Mail server ["+server+"]"); logWarning(xlib, 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(xlib, send_mail_log, send_mail_mask, "send_mail: Trying to connect to "+ip_txt); //println("send_mail: Trying to connect to "+ip_txt); if (Result(NetworkConnectError,RWStream))connect(ip_addr, port) is { error(e) then logWarning(xlib, send_mail_log, "send_mail: Can't connect to "+ip_txt); error(error), ok(server_conn) then send_mail(server_conn, param, (Result(SmtpClientResult, One) dum, Smtp_Recipient my) |-> println("send_mail Result(SmtpClientResult, One) ignored ")), } }.