| | @@ -157,10 +157,11 @@ |
| 157 | 157 | char *zHostname; /* Hostname of SMTP server for zDest */ |
| 158 | 158 | u32 smtpFlags; /* Flags changing the operation */ |
| 159 | 159 | FILE *logFile; /* Write session transcript to this log file */ |
| 160 | 160 | Blob *pTranscript; /* Record session transcript here */ |
| 161 | 161 | int atEof; /* True after connection closes */ |
| 162 | + int bFatal; /* Error is fatal. Do not retry */ |
| 162 | 163 | char *zErr; /* Error message */ |
| 163 | 164 | Blob inbuf; /* Input buffer */ |
| 164 | 165 | }; |
| 165 | 166 | |
| 166 | 167 | /* Allowed values for SmtpSession.smtpFlags */ |
| | @@ -180,10 +181,30 @@ |
| 180 | 181 | blob_reset(&pSession->inbuf); |
| 181 | 182 | fossil_free(pSession->zHostname); |
| 182 | 183 | fossil_free(pSession->zErr); |
| 183 | 184 | fossil_free(pSession); |
| 184 | 185 | } |
| 186 | + |
| 187 | +/* |
| 188 | +** Set an error message on the SmtpSession |
| 189 | +*/ |
| 190 | +static void smtp_set_error( |
| 191 | + SmtpSession *p, /* The SMTP context */ |
| 192 | + int bFatal, /* Fatal error. Reset and retry is pointless */ |
| 193 | + const char *zFormat, /* Error message. */ |
| 194 | + ... |
| 195 | +){ |
| 196 | + if( bFatal ) p->bFatal = 1; |
| 197 | + if( p->zErr==0 ){ |
| 198 | + va_list ap; |
| 199 | + va_start(ap, zFormat); |
| 200 | + p->zErr = vmprintf(zFormat, ap); |
| 201 | + va_end(ap); |
| 202 | + } |
| 203 | + socket_close(); |
| 204 | + p->atEof = 1; |
| 205 | +} |
| 185 | 206 | |
| 186 | 207 | /* |
| 187 | 208 | ** Allocate a new SmtpSession object. |
| 188 | 209 | ** |
| 189 | 210 | ** Both zFrom and zDest must be specified. smtpFlags may not contain |
| | @@ -222,20 +243,17 @@ |
| 222 | 243 | } |
| 223 | 244 | }else{ |
| 224 | 245 | p->zHostname = smtp_mx_host(zDest); |
| 225 | 246 | } |
| 226 | 247 | if( p->zHostname==0 ){ |
| 227 | | - p->atEof = 1; |
| 228 | | - p->zErr = mprintf("cannot locate SMTP server for \"%s\"", zDest); |
| 248 | + smtp_set_error(p, 1, "cannot locate SMTP server for \"%s\"", zDest); |
| 229 | 249 | return p; |
| 230 | 250 | } |
| 231 | 251 | url.name = p->zHostname; |
| 232 | 252 | socket_global_init(); |
| 233 | 253 | if( socket_open(&url) ){ |
| 234 | | - p->atEof = 1; |
| 235 | | - p->zErr = socket_errmsg(); |
| 236 | | - socket_close(); |
| 254 | + smtp_set_error(p, 1, "can't open socket: %z", socket_errmsg()); |
| 237 | 255 | } |
| 238 | 256 | return p; |
| 239 | 257 | } |
| 240 | 258 | |
| 241 | 259 | /* |
| | @@ -320,13 +338,11 @@ |
| 320 | 338 | if( got==1000 ) continue; |
| 321 | 339 | } |
| 322 | 340 | nDelay++; |
| 323 | 341 | if( nDelay>100 ){ |
| 324 | 342 | blob_init(in, 0, 0); |
| 325 | | - p->zErr = mprintf("timeout"); |
| 326 | | - socket_close(); |
| 327 | | - p->atEof = 1; |
| 343 | + smtp_set_error(p, 1, "client times out waiting on server response"); |
| 328 | 344 | return; |
| 329 | 345 | }else{ |
| 330 | 346 | sqlite3_sleep(100); |
| 331 | 347 | } |
| 332 | 348 | }while( n<1 || z[n-1]!='\n' ); |
| | @@ -403,23 +419,27 @@ |
| 403 | 419 | int smtp_client_startup(SmtpSession *p){ |
| 404 | 420 | Blob in = BLOB_INITIALIZER; |
| 405 | 421 | int iCode = 0; |
| 406 | 422 | int bMore = 0; |
| 407 | 423 | char *zArg = 0; |
| 408 | | - if( p==0 || p->atEof ) return 1; |
| 424 | + if( p==0 || p->bFatal ) return 1; |
| 425 | + fossil_free(p->zErr); |
| 426 | + p->zErr = 0; |
| 409 | 427 | do{ |
| 410 | 428 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 411 | 429 | }while( bMore ); |
| 412 | 430 | if( iCode!=220 ){ |
| 431 | + smtp_set_error(p, 1, "server opens conversation with: %b", &in); |
| 413 | 432 | smtp_client_quit(p); |
| 414 | 433 | return 1; |
| 415 | 434 | } |
| 416 | 435 | smtp_send_line(p, "EHLO %s\r\n", p->zFrom); |
| 417 | 436 | do{ |
| 418 | 437 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 419 | 438 | }while( bMore ); |
| 420 | 439 | if( iCode!=250 ){ |
| 440 | + smtp_set_error(p, 1, "server responds to EHLO with: %b", &in); |
| 421 | 441 | smtp_client_quit(p); |
| 422 | 442 | return 1; |
| 423 | 443 | } |
| 424 | 444 | return 0; |
| 425 | 445 | } |
| | @@ -550,27 +570,40 @@ |
| 550 | 570 | int iCode = 0; |
| 551 | 571 | int bMore = 0; |
| 552 | 572 | char *zArg = 0; |
| 553 | 573 | Blob in; |
| 554 | 574 | blob_init(&in, 0, 0); |
| 575 | + if( p->atEof && !p->bFatal ){ |
| 576 | + smtp_client_startup(p); |
| 577 | + if( p->atEof ) return 1; |
| 578 | + } |
| 555 | 579 | smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom); |
| 556 | 580 | do{ |
| 557 | 581 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 558 | 582 | }while( bMore ); |
| 559 | | - if( iCode!=250 ) return 1; |
| 583 | + if( iCode!=250 ){ |
| 584 | + smtp_set_error(p, 0, "server replies to MAIL FROM with: %b", &in); |
| 585 | + return 1; |
| 586 | + } |
| 560 | 587 | for(i=0; i<nTo; i++){ |
| 561 | 588 | smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]); |
| 562 | 589 | do{ |
| 563 | 590 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 564 | 591 | }while( bMore ); |
| 565 | | - if( iCode!=250 ) return 1; |
| 592 | + if( iCode!=250 ){ |
| 593 | + smtp_set_error(p, 0, "server replies to RCPT TO with: %b", &in); |
| 594 | + return 1; |
| 595 | + } |
| 566 | 596 | } |
| 567 | 597 | smtp_send_line(p, "DATA\r\n"); |
| 568 | 598 | do{ |
| 569 | 599 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 570 | 600 | }while( bMore ); |
| 571 | | - if( iCode!=354 ) return 1; |
| 601 | + if( iCode!=354 ){ |
| 602 | + smtp_set_error(p, 0, "server replies to DATA with: %b", &in); |
| 603 | + return 1; |
| 604 | + } |
| 572 | 605 | smtp_send_email_body(zMsg, socket_send, 0); |
| 573 | 606 | if( p->smtpFlags & SMTP_TRACE_STDOUT ){ |
| 574 | 607 | fossil_print("C: # message content\nC: .\n"); |
| 575 | 608 | } |
| 576 | 609 | if( p->smtpFlags & SMTP_TRACE_FILE ){ |
| | @@ -580,11 +613,14 @@ |
| 580 | 613 | blob_appendf(p->pTranscript, "C: # message content\nC: .\n"); |
| 581 | 614 | } |
| 582 | 615 | do{ |
| 583 | 616 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 584 | 617 | }while( bMore ); |
| 585 | | - if( iCode!=250 ) return 1; |
| 618 | + if( iCode!=250 ){ |
| 619 | + smtp_set_error(p, 0, "server replies to end-of-DATA with: %b", &in); |
| 620 | + return 1; |
| 621 | + } |
| 586 | 622 | return 0; |
| 587 | 623 | } |
| 588 | 624 | |
| 589 | 625 | /* |
| 590 | 626 | ** The input is a base email address of the form "local@domain". |
| | @@ -646,13 +682,15 @@ |
| 646 | 682 | if( p->zErr ){ |
| 647 | 683 | fossil_fatal("%s", p->zErr); |
| 648 | 684 | } |
| 649 | 685 | fossil_print("Connection to \"%s\"\n", p->zHostname); |
| 650 | 686 | smtp_client_startup(p); |
| 651 | | - smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); |
| 687 | + if( !p->atEof ){ |
| 688 | + smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); |
| 689 | + } |
| 652 | 690 | smtp_client_quit(p); |
| 653 | 691 | if( p->zErr ){ |
| 654 | 692 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 655 | 693 | } |
| 656 | 694 | smtp_session_free(p); |
| 657 | 695 | blob_reset(&body); |
| 658 | 696 | } |
| 659 | 697 | |