From 633954dff0f45cafb1179f749f63744fa3a2d32a Mon Sep 17 00:00:00 2001 From: Cedric RICARD Date: Sun, 8 Feb 2009 14:59:44 +0000 Subject: [PATCH] Better handling for error during sending very big mails --- calexium_lib/mail/send_mail.anubis | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------- 1 file changed, 156 insertions(+), 49 deletions(-) diff --git a/calexium_lib/mail/send_mail.anubis b/calexium_lib/mail/send_mail.anubis index 17680be..fa3d395 100644 --- a/calexium_lib/mail/send_mail.anubis +++ b/calexium_lib/mail/send_mail.anubis @@ -77,17 +77,23 @@ public type Send_Mail_Session: Int size ). -define Maybe(String) +type Command_Result: + failure, + timeout, + success(String). + +define Command_Result receive_command ( - RWStream conn, + RStream conn, List(Word8) so_far, - Word8 previous + Word8 previous, + Int time_out ) = - if read_network_byte(weaken(conn), smtp_time_out) is + if read_network_byte(conn, time_out) is { failure then logTrace(send_mail_log, send_mail_mask, "receive_command read_network_byte 0 failure");failure, - timeout then logWarning(send_mail_log, "[send_mail] receive_command timeout"); failure, + timeout then logWarning(send_mail_log, "[send_mail] receive_command timeout"); timeout, success(c) then if c = 10 & previous = 13 then // with result = implode(reverse(so_far)), @@ -95,27 +101,29 @@ define Maybe(String) success(result) else if previous = 13 then - receive_command(conn, so_far, c) + receive_command(conn, so_far, c, time_out) else - receive_command(conn, [previous . so_far], c) + receive_command(conn, [previous . so_far], c, time_out) }. -define Maybe(String) +define Command_Result receive_command ( - RWStream conn, + RStream conn, List(Word8) so_far, + Int time_out ) = - if read_network_byte(weaken(conn), smtp_time_out) is + if read_network_byte(conn, time_out) is { failure then logTrace(send_mail_log, send_mail_mask, "receive_command read_network_byte 0 failure"); failure, - timeout then logWarning(send_mail_log, "[send_mail] receive_command timeout"); failure, - success(c) then receive_command(conn,[], c) + timeout then (if time_out = 0 then unique else logWarning(send_mail_log, "[send_mail] receive_command timeout")); timeout, + success(c) then receive_command(conn,[], c, time_out) }. type Reply_Result: error, + timeout, reply(Int code, List(String) lines). define SendMailResult @@ -140,12 +148,14 @@ define SendMailResult define Reply_Result receive_reply ( - RWStream conn, - List(String) so_far + RStream conn, + List(String) so_far, + Int time_out ) = - if receive_command(conn, []) is + if receive_command(conn, [], time_out) is { failure then logError(send_mail_log, "receive_reply: error receiving command"); error, + timeout then logError(send_mail_log, "receive_reply: timeout receiving command"); timeout, success(line) then //check if we must read another line by presence of hyphen after the reply code //220-bla bla bla @@ -161,7 +171,7 @@ define Reply_Result failure then logError(send_mail_log, "receive_reply: error getting 4th character from '" + line +"'"); error, success(char) then if char = '-' then - receive_reply(conn, [line . so_far]) + receive_reply(conn, [line . so_far], time_out) else //decode the code if sub_string(line, 0, 3) is @@ -182,7 +192,7 @@ define Reply_Result ( RWStream conn, ) = - receive_reply(conn, []). + receive_reply(weaken(conn), [], smtp_time_out). Sending a piece of text (String) from the begining. @@ -229,6 +239,7 @@ define Reply_Result with rep = if receive_reply(conn) is { error then logError(send_mail_log, "send_ehlo: can't get reply"); error, + timeout then logError(send_mail_log, "send_ehlo: timeout getting reply"); timeout, reply(code, lines) then //we manage the 500 error, that mean the remote server is not ESMTP //hence we try we with HELO, the old manner RFC 821 @@ -314,6 +325,86 @@ define Reply_Result The same for the content of the message. We need base64 encoding. read tools/base64.anubis +type SendContentResult: + smtp_reply(Reply_Result), + copy_error(Int written), + copy_ok(Int written). + +define Maybe(One) + sm_flush + ( + ByteArray buffer, + WStream target + )= + if write( target , buffer) is + { + failure then failure, + success(nb_write) then + with buffer_size = length(buffer), + if nb_write = buffer_size then + success(unique) + else + with new_buffer = extract(buffer, nb_write, buffer_size), + sm_flush(new_buffer, target) + }. + +public define SendContentResult + sm_copy_Data_IO_to_Stream + ( + Data_IO source, + RWStream target, + Int so_far + ) = + if receive_reply(weaken(target), [], smtp_time_out) is + { + error then copy_error(so_far), + timeout then + if read_bytes(source, 65536) is + { + failure then copy_error(so_far), + time_out then copy_error(so_far), + success(buffer) then + if sm_flush( buffer, weaken(target) ) is + { + failure then copy_error(so_far), + success(_) then sm_copy_Data_IO_to_Stream(source, target, so_far + 65536) + }, + + truncated(buffer) then + with len = length(buffer), + if len = 0 then + copy_ok(so_far) + else + if sm_flush( buffer, weaken(target) ) is + { + failure then copy_error(so_far), + success(_) then copy_ok(so_far + len) + } + }, + reply(code, lines) then + smtp_reply(reply(code, lines)) + }. + +define SendContentResult + sm_copy_Data_IO_List_to_Stream + ( + List(Data_IO) io_list, + RWStream target, + Int so_far + )= + if io_list is + { + [] then copy_ok(so_far), + [ h . t ] then + if rewind(h)(unique) then + with result = sm_copy_Data_IO_to_Stream(h, target, 0), + if result is copy_ok(written) then + sm_copy_Data_IO_List_to_Stream(t, target, so_far + written) + else + result + else + copy_error(so_far) + }. define Reply_Result send_content @@ -322,14 +413,23 @@ define Reply_Result List(Data_IO) mail_part ) = //open the message file from the drive - if copy_Data_IO_List_to_Stream(mail_part, weaken(conn)) is copy_ok(_) then - if reliable_write(tcp(conn), [crlf_dot_crlf]) is - { - failure then logError(send_mail_log, "send_content: error writing CRLF.CRLF"); error, - success(_) then receive_reply(conn) - } - else - logError(send_mail_log, "send_content: error sending data"); error. + if sm_copy_Data_IO_List_to_Stream(mail_part, conn, 0) is + { + smtp_reply(reply) then reply + copy_error(written) then + logError(send_mail_log, "send_content: error sending data"); + if written > 1024000 then + logInfo(send_mail_log, "send_content: may be due to over sized mail. Convert it to 452 error."); + reply(452, ["Error sending big email (more than 1 Mb)"]) + else + error, + copy_ok(_) then + if reliable_write(tcp(conn), [crlf_dot_crlf]) is + { + failure then logError(send_mail_log, "send_content: error writing CRLF.CRLF"); error, + success(_) then receive_reply(conn) + } + }. Almost the same for 'QUIT': @@ -353,37 +453,38 @@ define SendMailResult Send_Mail_Session sm_session ) = with enhanced_status = sm_session.enhanced_status, - //send MAIL FROM - if send_mail_from(conn, param.sender.str) is + //send MAIL FROM + if send_mail_from(conn, param.sender.str) is + { + error then error, // already logged + timeout then error, + reply(code, lines) then + if code = 250 then + //send RCPT TO + if send_recipient(conn,param.recipient.str) is { error then error, // already logged + timeout then error, reply(code, lines) then if code = 250 then - //send RCPT TO - if send_recipient(conn,param.recipient.str) is + //send DATA + if send_data(conn) is { - error then error, // already logged + error then error, // already logged + timeout then error, reply(code, lines) then - if code = 250 then - //send DATA - if send_data(conn) is + if code = 354 then + if send_content(conn, param.mail_parts) is { - error then error, // already logged + error then error, // already logged + timeout then error, reply(code, lines) then - if code = 354 then - if send_content(conn, param.mail_parts) is - { - error then error, // already logged - reply(code, lines) then - if code = 250 then - send_quit(conn); - logTrace(send_mail_log,send_mail_mask,"SENT Mail FROM "+param.sender.str+" TO "+param.recipient.str); - ok - else - reply_handling(code, lines, enhanced_status) - } - else - reply_handling(code, lines, enhanced_status) + if code = 250 then + send_quit(conn); + logTrace(send_mail_log,send_mail_mask,"SENT Mail FROM "+param.sender.str+" TO "+param.recipient.str); + ok + else + reply_handling(code, lines, enhanced_status) } else reply_handling(code, lines, enhanced_status) @@ -391,6 +492,9 @@ define SendMailResult else reply_handling(code, lines, enhanced_status) } + else + reply_handling(code, lines, enhanced_status) + } . define List(String) @@ -435,7 +539,8 @@ define SendMailResult success(_) then if receive_reply(conn) is { - error then error // already logged + error then error // already logged + timeout then error, reply(code, lines) then if code = 235 then ok @@ -524,12 +629,14 @@ public define SendMailResult if receive_reply(conn) is { error then error,// already logged + timeout then error, reply(code, lines) then if code = 220 then //send EHLO if send_ehlo(conn, param.host_name.str) is //"mail."+force_Type(get_main_domain(db), "mailfountain.net")) is { error then error, // already logged + timeout then error, reply(code, lines) then if code = 250 then // parse all available options here, and build a smtp context -- libgit2 0.21.4