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 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 86 receive_command
82 87 (
83   - RWStream conn,
  88 + RStream conn,
84 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 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 97 success(c) then
92 98 if c = 10 & previous = 13 then // <LF>
93 99 with result = implode(reverse(so_far)),
... ... @@ -95,27 +101,29 @@ define Maybe(String)
95 101 success(result)
96 102 else
97 103 if previous = 13 then
98   - receive_command(conn, so_far, c)
  104 + receive_command(conn, so_far, c, time_out)
99 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 110 receive_command
105 111 (
106   - RWStream conn,
  112 + RStream conn,
107 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 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 124 type Reply_Result:
118 125 error,
  126 + timeout,
119 127 reply(Int code, List(String) lines).
120 128  
121 129 define SendMailResult
... ... @@ -140,12 +148,14 @@ define SendMailResult
140 148 define Reply_Result
141 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 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 159 success(line) then
150 160 //check if we must read another line by presence of hyphen after the reply code
151 161 //220-bla bla bla
... ... @@ -161,7 +171,7 @@ define Reply_Result
161 171 failure then logError(send_mail_log, "receive_reply: error getting 4th character from '" + line +"'"); error,
162 172 success(char) then
163 173 if char = '-' then
164   - receive_reply(conn, [line . so_far])
  174 + receive_reply(conn, [line . so_far], time_out)
165 175 else
166 176 //decode the code
167 177 if sub_string(line, 0, 3) is
... ... @@ -182,7 +192,7 @@ define Reply_Result
182 192 (
183 193 RWStream conn,
184 194 ) =
185   - receive_reply(conn, []).
  195 + receive_reply(weaken(conn), [], smtp_time_out).
186 196  
187 197  
188 198 Sending a piece of text (String) from the begining.
... ... @@ -229,6 +239,7 @@ define Reply_Result
229 239 with rep = if receive_reply(conn) is
230 240 {
231 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 243 reply(code, lines) then
233 244 //we manage the 500 error, that mean the remote server is not ESMTP
234 245 //hence we try we with HELO, the old manner RFC 821
... ... @@ -314,6 +325,86 @@ define Reply_Result
314 325 The same for the content of the message. We need base64 encoding.
315 326  
316 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 409 define Reply_Result
319 410 send_content
... ... @@ -322,14 +413,23 @@ define Reply_Result
322 413 List(Data_IO) mail_part
323 414 ) =
324 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 435 Almost the same for 'QUIT':
... ... @@ -353,37 +453,38 @@ define SendMailResult
353 453 Send_Mail_Session sm_session
354 454 ) =
355 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 466 error then error, // already logged
  467 + timeout then error,
360 468 reply(code, lines) then
361 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 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 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 489 else
389 490 reply_handling(code, lines, enhanced_status)
... ... @@ -391,6 +492,9 @@ define SendMailResult
391 492 else
392 493 reply_handling(code, lines, enhanced_status)
393 494 }
  495 + else
  496 + reply_handling(code, lines, enhanced_status)
  497 + }
394 498 .
395 499  
396 500 define List(String)
... ... @@ -435,7 +539,8 @@ define SendMailResult
435 539 success(_) then
436 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 544 reply(code, lines) then
440 545 if code = 235 then
441 546 ok
... ... @@ -524,12 +629,14 @@ public define SendMailResult
524 629 if receive_reply(conn) is
525 630 {
526 631 error then error,// already logged
  632 + timeout then error,
527 633 reply(code, lines) then
528 634 if code = 220 then
529 635 //send EHLO
530 636 if send_ehlo(conn, param.host_name.str) is //"mail."+force_Type(get_main_domain(db), "mailfountain.net")) is
531 637 {
532 638 error then error, // already logged
  639 + timeout then error,
533 640 reply(code, lines) then
534 641 if code = 250 then
535 642 // parse all available options here, and build a smtp context
... ...