Commit 633954dff0f45cafb1179f749f63744fa3a2d32a

Authored by Cédric RICARD
1 parent b58c2692

Better handling for error during sending very big mails

Showing 1 changed file with 156 additions and 49 deletions   Show diff stats
calexium_lib/mail/send_mail.anubis
@@ -77,17 +77,23 @@ public type Send_Mail_Session: @@ -77,17 +77,23 @@ public type Send_Mail_Session:
77 Int size 77 Int size
78 ). 78 ).
79 79
80 -define Maybe(String) 80 +type Command_Result:
  81 + failure,
  82 + timeout,
  83 + success(String).
  84 +
  85 +define Command_Result
81 receive_command 86 receive_command
82 ( 87 (
83 - RWStream conn, 88 + RStream conn,
84 List(Word8) so_far, 89 List(Word8) so_far,
85 - Word8 previous 90 + Word8 previous,
  91 + Int time_out
86 ) = 92 ) =
87 - if read_network_byte(weaken(conn), smtp_time_out) is 93 + if read_network_byte(conn, time_out) is
88 { 94 {
89 failure then logTrace(send_mail_log, send_mail_mask, "receive_command read_network_byte 0 failure");failure, 95 failure then logTrace(send_mail_log, send_mail_mask, "receive_command read_network_byte 0 failure");failure,
90 - timeout then logWarning(send_mail_log, "[send_mail] receive_command timeout"); failure, 96 + timeout then logWarning(send_mail_log, "[send_mail] receive_command timeout"); timeout,
91 success(c) then 97 success(c) then
92 if c = 10 & previous = 13 then // <LF> 98 if c = 10 & previous = 13 then // <LF>
93 with result = implode(reverse(so_far)), 99 with result = implode(reverse(so_far)),
@@ -95,27 +101,29 @@ define Maybe(String) @@ -95,27 +101,29 @@ define Maybe(String)
95 success(result) 101 success(result)
96 else 102 else
97 if previous = 13 then 103 if previous = 13 then
98 - receive_command(conn, so_far, c) 104 + receive_command(conn, so_far, c, time_out)
99 else 105 else
100 - receive_command(conn, [previous . so_far], c) 106 + receive_command(conn, [previous . so_far], c, time_out)
101 }. 107 }.
102 108
103 -define Maybe(String) 109 +define Command_Result
104 receive_command 110 receive_command
105 ( 111 (
106 - RWStream conn, 112 + RStream conn,
107 List(Word8) so_far, 113 List(Word8) so_far,
  114 + Int time_out
108 ) = 115 ) =
109 - if read_network_byte(weaken(conn), smtp_time_out) is 116 + if read_network_byte(conn, time_out) is
110 { 117 {
111 failure then logTrace(send_mail_log, send_mail_mask, "receive_command read_network_byte 0 failure"); failure, 118 failure then logTrace(send_mail_log, send_mail_mask, "receive_command read_network_byte 0 failure"); failure,
112 - timeout then logWarning(send_mail_log, "[send_mail] receive_command timeout"); failure,  
113 - success(c) then receive_command(conn,[], c) 119 + timeout then (if time_out = 0 then unique else logWarning(send_mail_log, "[send_mail] receive_command timeout")); timeout,
  120 + success(c) then receive_command(conn,[], c, time_out)
114 }. 121 }.
115 122
116 123
117 type Reply_Result: 124 type Reply_Result:
118 error, 125 error,
  126 + timeout,
119 reply(Int code, List(String) lines). 127 reply(Int code, List(String) lines).
120 128
121 define SendMailResult 129 define SendMailResult
@@ -140,12 +148,14 @@ define SendMailResult @@ -140,12 +148,14 @@ define SendMailResult
140 define Reply_Result 148 define Reply_Result
141 receive_reply 149 receive_reply
142 ( 150 (
143 - RWStream conn,  
144 - List(String) so_far 151 + RStream conn,
  152 + List(String) so_far,
  153 + Int time_out
145 ) = 154 ) =
146 - if receive_command(conn, []) is 155 + if receive_command(conn, [], time_out) is
147 { 156 {
148 failure then logError(send_mail_log, "receive_reply: error receiving command"); error, 157 failure then logError(send_mail_log, "receive_reply: error receiving command"); error,
  158 + timeout then logError(send_mail_log, "receive_reply: timeout receiving command"); timeout,
149 success(line) then 159 success(line) then
150 //check if we must read another line by presence of hyphen after the reply code 160 //check if we must read another line by presence of hyphen after the reply code
151 //220-bla bla bla 161 //220-bla bla bla
@@ -161,7 +171,7 @@ define Reply_Result @@ -161,7 +171,7 @@ define Reply_Result
161 failure then logError(send_mail_log, "receive_reply: error getting 4th character from '" + line +"'"); error, 171 failure then logError(send_mail_log, "receive_reply: error getting 4th character from '" + line +"'"); error,
162 success(char) then 172 success(char) then
163 if char = '-' then 173 if char = '-' then
164 - receive_reply(conn, [line . so_far]) 174 + receive_reply(conn, [line . so_far], time_out)
165 else 175 else
166 //decode the code 176 //decode the code
167 if sub_string(line, 0, 3) is 177 if sub_string(line, 0, 3) is
@@ -182,7 +192,7 @@ define Reply_Result @@ -182,7 +192,7 @@ define Reply_Result
182 ( 192 (
183 RWStream conn, 193 RWStream conn,
184 ) = 194 ) =
185 - receive_reply(conn, []). 195 + receive_reply(weaken(conn), [], smtp_time_out).
186 196
187 197
188 Sending a piece of text (String) from the begining. 198 Sending a piece of text (String) from the begining.
@@ -229,6 +239,7 @@ define Reply_Result @@ -229,6 +239,7 @@ define Reply_Result
229 with rep = if receive_reply(conn) is 239 with rep = if receive_reply(conn) is
230 { 240 {
231 error then logError(send_mail_log, "send_ehlo: can't get reply"); error, 241 error then logError(send_mail_log, "send_ehlo: can't get reply"); error,
  242 + timeout then logError(send_mail_log, "send_ehlo: timeout getting reply"); timeout,
232 reply(code, lines) then 243 reply(code, lines) then
233 //we manage the 500 error, that mean the remote server is not ESMTP 244 //we manage the 500 error, that mean the remote server is not ESMTP
234 //hence we try we with HELO, the old manner RFC 821 245 //hence we try we with HELO, the old manner RFC 821
@@ -314,6 +325,86 @@ define Reply_Result @@ -314,6 +325,86 @@ define Reply_Result
314 The same for the content of the message. We need base64 encoding. 325 The same for the content of the message. We need base64 encoding.
315 326
316 read tools/base64.anubis 327 read tools/base64.anubis
  328 +type SendContentResult:
  329 + smtp_reply(Reply_Result),
  330 + copy_error(Int written),
  331 + copy_ok(Int written).
  332 +
  333 +define Maybe(One)
  334 + sm_flush
  335 + (
  336 + ByteArray buffer,
  337 + WStream target
  338 + )=
  339 + if write( target , buffer) is
  340 + {
  341 + failure then failure,
  342 + success(nb_write) then
  343 + with buffer_size = length(buffer),
  344 + if nb_write = buffer_size then
  345 + success(unique)
  346 + else
  347 + with new_buffer = extract(buffer, nb_write, buffer_size),
  348 + sm_flush(new_buffer, target)
  349 + }.
  350 +
  351 +public define SendContentResult
  352 + sm_copy_Data_IO_to_Stream
  353 + (
  354 + Data_IO source,
  355 + RWStream target,
  356 + Int so_far
  357 + ) =
  358 + if receive_reply(weaken(target), [], smtp_time_out) is
  359 + {
  360 + error then copy_error(so_far),
  361 + timeout then
  362 + if read_bytes(source, 65536) is
  363 + {
  364 + failure then copy_error(so_far),
  365 + time_out then copy_error(so_far),
  366 + success(buffer) then
  367 + if sm_flush( buffer, weaken(target) ) is
  368 + {
  369 + failure then copy_error(so_far),
  370 + success(_) then sm_copy_Data_IO_to_Stream(source, target, so_far + 65536)
  371 + },
  372 +
  373 + truncated(buffer) then
  374 + with len = length(buffer),
  375 + if len = 0 then
  376 + copy_ok(so_far)
  377 + else
  378 + if sm_flush( buffer, weaken(target) ) is
  379 + {
  380 + failure then copy_error(so_far),
  381 + success(_) then copy_ok(so_far + len)
  382 + }
  383 + },
  384 + reply(code, lines) then
  385 + smtp_reply(reply(code, lines))
  386 + }.
  387 +
  388 +define SendContentResult
  389 + sm_copy_Data_IO_List_to_Stream
  390 + (
  391 + List(Data_IO) io_list,
  392 + RWStream target,
  393 + Int so_far
  394 + )=
  395 + if io_list is
  396 + {
  397 + [] then copy_ok(so_far),
  398 + [ h . t ] then
  399 + if rewind(h)(unique) then
  400 + with result = sm_copy_Data_IO_to_Stream(h, target, 0),
  401 + if result is copy_ok(written) then
  402 + sm_copy_Data_IO_List_to_Stream(t, target, so_far + written)
  403 + else
  404 + result
  405 + else
  406 + copy_error(so_far)
  407 + }.
317 408
318 define Reply_Result 409 define Reply_Result
319 send_content 410 send_content
@@ -322,14 +413,23 @@ define Reply_Result @@ -322,14 +413,23 @@ define Reply_Result
322 List(Data_IO) mail_part 413 List(Data_IO) mail_part
323 ) = 414 ) =
324 //open the message file from the drive 415 //open the message file from the drive
325 - if copy_Data_IO_List_to_Stream(mail_part, weaken(conn)) is copy_ok(_) then  
326 - if reliable_write(tcp(conn), [crlf_dot_crlf]) is  
327 - {  
328 - failure then logError(send_mail_log, "send_content: error writing CRLF.CRLF"); error,  
329 - success(_) then receive_reply(conn)  
330 - }  
331 - else  
332 - logError(send_mail_log, "send_content: error sending data"); error. 416 + if sm_copy_Data_IO_List_to_Stream(mail_part, conn, 0) is
  417 + {
  418 + smtp_reply(reply) then reply
  419 + copy_error(written) then
  420 + logError(send_mail_log, "send_content: error sending data");
  421 + if written > 1024000 then
  422 + logInfo(send_mail_log, "send_content: may be due to over sized mail. Convert it to 452 error.");
  423 + reply(452, ["Error sending big email (more than 1 Mb)"])
  424 + else
  425 + error,
  426 + copy_ok(_) then
  427 + if reliable_write(tcp(conn), [crlf_dot_crlf]) is
  428 + {
  429 + failure then logError(send_mail_log, "send_content: error writing CRLF.CRLF"); error,
  430 + success(_) then receive_reply(conn)
  431 + }
  432 + }.
333 433
334 434
335 Almost the same for 'QUIT': 435 Almost the same for 'QUIT':
@@ -353,37 +453,38 @@ define SendMailResult @@ -353,37 +453,38 @@ define SendMailResult
353 Send_Mail_Session sm_session 453 Send_Mail_Session sm_session
354 ) = 454 ) =
355 with enhanced_status = sm_session.enhanced_status, 455 with enhanced_status = sm_session.enhanced_status,
356 - //send MAIL FROM  
357 - if send_mail_from(conn, param.sender.str) is 456 + //send MAIL FROM
  457 + if send_mail_from(conn, param.sender.str) is
  458 + {
  459 + error then error, // already logged
  460 + timeout then error,
  461 + reply(code, lines) then
  462 + if code = 250 then
  463 + //send RCPT TO
  464 + if send_recipient(conn,param.recipient.str) is
358 { 465 {
359 error then error, // already logged 466 error then error, // already logged
  467 + timeout then error,
360 reply(code, lines) then 468 reply(code, lines) then
361 if code = 250 then 469 if code = 250 then
362 - //send RCPT TO  
363 - if send_recipient(conn,param.recipient.str) is 470 + //send DATA
  471 + if send_data(conn) is
364 { 472 {
365 - error then error, // already logged 473 + error then error, // already logged
  474 + timeout then error,
366 reply(code, lines) then 475 reply(code, lines) then
367 - if code = 250 then  
368 - //send DATA  
369 - if send_data(conn) is 476 + if code = 354 then
  477 + if send_content(conn, param.mail_parts) is
370 { 478 {
371 - error then error, // already logged 479 + error then error, // already logged
  480 + timeout then error,
372 reply(code, lines) then 481 reply(code, lines) then
373 - if code = 354 then  
374 - if send_content(conn, param.mail_parts) is  
375 - {  
376 - error then error, // already logged  
377 - reply(code, lines) then  
378 - if code = 250 then  
379 - send_quit(conn);  
380 - logTrace(send_mail_log,send_mail_mask,"SENT Mail FROM "+param.sender.str+" TO "+param.recipient.str);  
381 - ok  
382 - else  
383 - reply_handling(code, lines, enhanced_status)  
384 - }  
385 - else  
386 - reply_handling(code, lines, enhanced_status) 482 + if code = 250 then
  483 + send_quit(conn);
  484 + logTrace(send_mail_log,send_mail_mask,"SENT Mail FROM "+param.sender.str+" TO "+param.recipient.str);
  485 + ok
  486 + else
  487 + reply_handling(code, lines, enhanced_status)
387 } 488 }
388 else 489 else
389 reply_handling(code, lines, enhanced_status) 490 reply_handling(code, lines, enhanced_status)
@@ -391,6 +492,9 @@ define SendMailResult @@ -391,6 +492,9 @@ define SendMailResult
391 else 492 else
392 reply_handling(code, lines, enhanced_status) 493 reply_handling(code, lines, enhanced_status)
393 } 494 }
  495 + else
  496 + reply_handling(code, lines, enhanced_status)
  497 + }
394 . 498 .
395 499
396 define List(String) 500 define List(String)
@@ -435,7 +539,8 @@ define SendMailResult @@ -435,7 +539,8 @@ define SendMailResult
435 success(_) then 539 success(_) then
436 if receive_reply(conn) is 540 if receive_reply(conn) is
437 { 541 {
438 - error then error // already logged 542 + error then error // already logged
  543 + timeout then error,
439 reply(code, lines) then 544 reply(code, lines) then
440 if code = 235 then 545 if code = 235 then
441 ok 546 ok
@@ -524,12 +629,14 @@ public define SendMailResult @@ -524,12 +629,14 @@ public define SendMailResult
524 if receive_reply(conn) is 629 if receive_reply(conn) is
525 { 630 {
526 error then error,// already logged 631 error then error,// already logged
  632 + timeout then error,
527 reply(code, lines) then 633 reply(code, lines) then
528 if code = 220 then 634 if code = 220 then
529 //send EHLO 635 //send EHLO
530 if send_ehlo(conn, param.host_name.str) is //"mail."+force_Type(get_main_domain(db), "mailfountain.net")) is 636 if send_ehlo(conn, param.host_name.str) is //"mail."+force_Type(get_main_domain(db), "mailfountain.net")) is
531 { 637 {
532 error then error, // already logged 638 error then error, // already logged
  639 + timeout then error,
533 reply(code, lines) then 640 reply(code, lines) then
534 if code = 250 then 641 if code = 250 then
535 // parse all available options here, and build a smtp context 642 // parse all available options here, and build a smtp context