Fossil SCM
Automatic retry on an SMTP relay failure.
Commit
2b96941c4c924e56246d673a30d5ef5146824dfd7361af503c55bbf66fc4eac8
Parent
e6c27d3dabf0c36…
2 files changed
-1
+36
-33
-1
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -655,11 +655,10 @@ | ||
| 655 | 655 | emailerError(p, "Could not start SMTP session: %s", |
| 656 | 656 | p->pSmtp ? p->pSmtp->zErr : "reason unknown"); |
| 657 | 657 | }else if( p->zDest[0]=='d' ){ |
| 658 | 658 | smtp_session_config(p->pSmtp, SMTP_TRACE_BLOB, &p->out); |
| 659 | 659 | } |
| 660 | - smtp_client_startup(p->pSmtp); | |
| 661 | 660 | } |
| 662 | 661 | } |
| 663 | 662 | return p; |
| 664 | 663 | } |
| 665 | 664 | |
| 666 | 665 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -655,11 +655,10 @@ | |
| 655 | emailerError(p, "Could not start SMTP session: %s", |
| 656 | p->pSmtp ? p->pSmtp->zErr : "reason unknown"); |
| 657 | }else if( p->zDest[0]=='d' ){ |
| 658 | smtp_session_config(p->pSmtp, SMTP_TRACE_BLOB, &p->out); |
| 659 | } |
| 660 | smtp_client_startup(p->pSmtp); |
| 661 | } |
| 662 | } |
| 663 | return p; |
| 664 | } |
| 665 | |
| 666 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -655,11 +655,10 @@ | |
| 655 | emailerError(p, "Could not start SMTP session: %s", |
| 656 | p->pSmtp ? p->pSmtp->zErr : "reason unknown"); |
| 657 | }else if( p->zDest[0]=='d' ){ |
| 658 | smtp_session_config(p->pSmtp, SMTP_TRACE_BLOB, &p->out); |
| 659 | } |
| 660 | } |
| 661 | } |
| 662 | return p; |
| 663 | } |
| 664 | |
| 665 |
+36
-33
| --- src/smtp.c | ||
| +++ src/smtp.c | ||
| @@ -156,14 +156,15 @@ | ||
| 156 | 156 | const char *zDest; /* Domain that will receive the email */ |
| 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 | - int atEof; /* True after connection closes */ | |
| 161 | + int bOpen; /* True if connection is Open */ | |
| 162 | 162 | int bFatal; /* Error is fatal. Do not retry */ |
| 163 | 163 | char *zErr; /* Error message */ |
| 164 | 164 | Blob inbuf; /* Input buffer */ |
| 165 | + UrlData url; /* Address of the server */ | |
| 165 | 166 | }; |
| 166 | 167 | |
| 167 | 168 | /* Allowed values for SmtpSession.smtpFlags */ |
| 168 | 169 | #define SMTP_TRACE_STDOUT 0x00001 /* Debugging info to console */ |
| 169 | 170 | #define SMTP_TRACE_FILE 0x00002 /* Debugging info to logFile */ |
| @@ -198,12 +199,14 @@ | ||
| 198 | 199 | va_list ap; |
| 199 | 200 | va_start(ap, zFormat); |
| 200 | 201 | p->zErr = vmprintf(zFormat, ap); |
| 201 | 202 | va_end(ap); |
| 202 | 203 | } |
| 203 | - socket_close(); | |
| 204 | - p->atEof = 1; | |
| 204 | + if( p->bOpen ){ | |
| 205 | + socket_close(); | |
| 206 | + p->bOpen = 0; | |
| 207 | + } | |
| 205 | 208 | } |
| 206 | 209 | |
| 207 | 210 | /* |
| 208 | 211 | ** Allocate a new SmtpSession object. |
| 209 | 212 | ** |
| @@ -218,43 +221,39 @@ | ||
| 218 | 221 | const char *zDest, /* Domain of the server */ |
| 219 | 222 | u32 smtpFlags, /* Flags */ |
| 220 | 223 | int iPort /* TCP port if the SMTP_PORT flags is present */ |
| 221 | 224 | ){ |
| 222 | 225 | SmtpSession *p; |
| 223 | - UrlData url; | |
| 224 | 226 | |
| 225 | 227 | p = fossil_malloc( sizeof(*p) ); |
| 226 | 228 | memset(p, 0, sizeof(*p)); |
| 227 | 229 | p->zFrom = zFrom; |
| 228 | 230 | p->zDest = zDest; |
| 229 | 231 | p->smtpFlags = smtpFlags; |
| 230 | - memset(&url, 0, sizeof(url)); | |
| 231 | - url.port = 25; | |
| 232 | + p->url.port = 25; | |
| 232 | 233 | blob_init(&p->inbuf, 0, 0); |
| 233 | 234 | if( smtpFlags & SMTP_PORT ){ |
| 234 | - url.port = iPort; | |
| 235 | + p->url.port = iPort; | |
| 235 | 236 | } |
| 236 | 237 | if( (smtpFlags & SMTP_DIRECT)!=0 ){ |
| 237 | 238 | int i; |
| 238 | 239 | p->zHostname = fossil_strdup(zDest); |
| 239 | 240 | for(i=0; p->zHostname[i] && p->zHostname[i]!=':'; i++){} |
| 240 | 241 | if( p->zHostname[i]==':' ){ |
| 241 | 242 | p->zHostname[i] = 0; |
| 242 | - url.port = atoi(&p->zHostname[i+1]); | |
| 243 | + p->url.port = atoi(&p->zHostname[i+1]); | |
| 243 | 244 | } |
| 244 | 245 | }else{ |
| 245 | 246 | p->zHostname = smtp_mx_host(zDest); |
| 246 | 247 | } |
| 247 | 248 | if( p->zHostname==0 ){ |
| 248 | 249 | smtp_set_error(p, 1, "cannot locate SMTP server for \"%s\"", zDest); |
| 249 | 250 | return p; |
| 250 | 251 | } |
| 251 | - url.name = p->zHostname; | |
| 252 | + p->url.name = p->zHostname; | |
| 252 | 253 | socket_global_init(); |
| 253 | - if( socket_open(&url) ){ | |
| 254 | - smtp_set_error(p, 1, "can't open socket: %z", socket_errmsg()); | |
| 255 | - } | |
| 254 | + p->bOpen = 0; | |
| 256 | 255 | return p; |
| 257 | 256 | } |
| 258 | 257 | |
| 259 | 258 | /* |
| 260 | 259 | ** Configure debugging options on SmtpSession. Add all bits in |
| @@ -279,11 +278,11 @@ | ||
| 279 | 278 | static void smtp_send_line(SmtpSession *p, const char *zFormat, ...){ |
| 280 | 279 | Blob b = empty_blob; |
| 281 | 280 | va_list ap; |
| 282 | 281 | char *z; |
| 283 | 282 | int n; |
| 284 | - if( p->atEof ) return; | |
| 283 | + if( !p->bOpen ) return; | |
| 285 | 284 | va_start(ap, zFormat); |
| 286 | 285 | blob_vappendf(&b, zFormat, ap); |
| 287 | 286 | va_end(ap); |
| 288 | 287 | z = blob_buffer(&b); |
| 289 | 288 | n = blob_size(&b); |
| @@ -315,11 +314,11 @@ | ||
| 315 | 314 | char *z = blob_buffer(&p->inbuf); |
| 316 | 315 | int i = blob_tell(&p->inbuf); |
| 317 | 316 | int nDelay = 0; |
| 318 | 317 | if( i<n && z[n-1]=='\n' ){ |
| 319 | 318 | blob_line(&p->inbuf, in); |
| 320 | - }else if( p->atEof ){ | |
| 319 | + }else if( !p->bOpen ){ | |
| 321 | 320 | blob_init(in, 0, 0); |
| 322 | 321 | }else{ |
| 323 | 322 | if( n>0 && i>=n ){ |
| 324 | 323 | blob_truncate(&p->inbuf, 0); |
| 325 | 324 | blob_rewind(&p->inbuf); |
| @@ -376,10 +375,11 @@ | ||
| 376 | 375 | ){ |
| 377 | 376 | int n; |
| 378 | 377 | char *z; |
| 379 | 378 | blob_truncate(in, 0); |
| 380 | 379 | smtp_recv_line(p, in); |
| 380 | + blob_trim(in); | |
| 381 | 381 | z = blob_str(in); |
| 382 | 382 | n = blob_size(in); |
| 383 | 383 | if( z[0]=='#' ){ |
| 384 | 384 | *piCode = 0; |
| 385 | 385 | *pbMore = 1; |
| @@ -397,52 +397,57 @@ | ||
| 397 | 397 | int smtp_client_quit(SmtpSession *p){ |
| 398 | 398 | Blob in = BLOB_INITIALIZER; |
| 399 | 399 | int iCode = 0; |
| 400 | 400 | int bMore = 0; |
| 401 | 401 | char *zArg = 0; |
| 402 | - if( !p->atEof ){ | |
| 402 | + if( p->bOpen ){ | |
| 403 | 403 | smtp_send_line(p, "QUIT\r\n"); |
| 404 | 404 | do{ |
| 405 | 405 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 406 | 406 | }while( bMore ); |
| 407 | - p->atEof = 1; | |
| 407 | + p->bOpen = 0; | |
| 408 | + socket_close(); | |
| 408 | 409 | } |
| 409 | - socket_close(); | |
| 410 | 410 | return 0; |
| 411 | 411 | } |
| 412 | 412 | |
| 413 | 413 | /* |
| 414 | 414 | ** Begin a client SMTP session. Wait for the initial 220 then send |
| 415 | 415 | ** the EHLO and wait for a 250. |
| 416 | 416 | ** |
| 417 | 417 | ** Return 0 on success and non-zero for a failure. |
| 418 | 418 | */ |
| 419 | -int smtp_client_startup(SmtpSession *p){ | |
| 419 | +static int smtp_client_startup(SmtpSession *p){ | |
| 420 | 420 | Blob in = BLOB_INITIALIZER; |
| 421 | 421 | int iCode = 0; |
| 422 | 422 | int bMore = 0; |
| 423 | 423 | char *zArg = 0; |
| 424 | 424 | if( p==0 || p->bFatal ) return 1; |
| 425 | - fossil_free(p->zErr); | |
| 426 | - p->zErr = 0; | |
| 425 | + if( socket_open(&p->url) ){ | |
| 426 | + smtp_set_error(p, 1, "can't open socket: %z", socket_errmsg()); | |
| 427 | + return 1; | |
| 428 | + } | |
| 429 | + p->bOpen = 1; | |
| 427 | 430 | do{ |
| 428 | 431 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 429 | 432 | }while( bMore ); |
| 430 | 433 | if( iCode!=220 ){ |
| 431 | - smtp_set_error(p, 1, "server opens conversation with: %b", &in); | |
| 434 | + smtp_set_error(p, 1, "conversation begins with: \"%d %s\"",iCode,zArg); | |
| 432 | 435 | smtp_client_quit(p); |
| 433 | 436 | return 1; |
| 434 | 437 | } |
| 435 | 438 | smtp_send_line(p, "EHLO %s\r\n", p->zFrom); |
| 436 | 439 | do{ |
| 437 | 440 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 438 | 441 | }while( bMore ); |
| 439 | 442 | if( iCode!=250 ){ |
| 440 | - smtp_set_error(p, 1, "server responds to EHLO with: %b", &in); | |
| 443 | + smtp_set_error(p, 1, "reply to EHLO with: \"%d %s\"",iCode, zArg); | |
| 441 | 444 | smtp_client_quit(p); |
| 442 | 445 | return 1; |
| 443 | 446 | } |
| 447 | + fossil_free(p->zErr); | |
| 448 | + p->zErr = 0; | |
| 444 | 449 | return 0; |
| 445 | 450 | } |
| 446 | 451 | |
| 447 | 452 | /* |
| 448 | 453 | ** COMMAND: test-smtp-probe |
| @@ -570,38 +575,38 @@ | ||
| 570 | 575 | int iCode = 0; |
| 571 | 576 | int bMore = 0; |
| 572 | 577 | char *zArg = 0; |
| 573 | 578 | Blob in; |
| 574 | 579 | blob_init(&in, 0, 0); |
| 575 | - if( p->atEof && !p->bFatal ){ | |
| 576 | - smtp_client_startup(p); | |
| 577 | - if( p->atEof ) return 1; | |
| 580 | + if( !p->bOpen ){ | |
| 581 | + if( !p->bFatal ) smtp_client_startup(p); | |
| 582 | + if( !p->bOpen ) return 1; | |
| 578 | 583 | } |
| 579 | 584 | smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom); |
| 580 | 585 | do{ |
| 581 | 586 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 582 | 587 | }while( bMore ); |
| 583 | 588 | if( iCode!=250 ){ |
| 584 | - smtp_set_error(p, 0, "server replies to MAIL FROM with: %b", &in); | |
| 589 | + smtp_set_error(p, 0,"reply to MAIL FROM: \"%d %s\"",iCode,zArg); | |
| 585 | 590 | return 1; |
| 586 | 591 | } |
| 587 | 592 | for(i=0; i<nTo; i++){ |
| 588 | 593 | smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]); |
| 589 | 594 | do{ |
| 590 | 595 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 591 | 596 | }while( bMore ); |
| 592 | 597 | if( iCode!=250 ){ |
| 593 | - smtp_set_error(p, 0, "server replies to RCPT TO with: %b", &in); | |
| 598 | + smtp_set_error(p, 0,"reply to RCPT TO: \"%d %s\"",iCode,zArg); | |
| 594 | 599 | return 1; |
| 595 | 600 | } |
| 596 | 601 | } |
| 597 | 602 | smtp_send_line(p, "DATA\r\n"); |
| 598 | 603 | do{ |
| 599 | 604 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 600 | 605 | }while( bMore ); |
| 601 | 606 | if( iCode!=354 ){ |
| 602 | - smtp_set_error(p, 0, "server replies to DATA with: %b", &in); | |
| 607 | + smtp_set_error(p, 0, "reply to DATA with: \"%d %s\"",iCode,zArg); | |
| 603 | 608 | return 1; |
| 604 | 609 | } |
| 605 | 610 | smtp_send_email_body(zMsg, socket_send, 0); |
| 606 | 611 | if( p->smtpFlags & SMTP_TRACE_STDOUT ){ |
| 607 | 612 | fossil_print("C: # message content\nC: .\n"); |
| @@ -614,11 +619,12 @@ | ||
| 614 | 619 | } |
| 615 | 620 | do{ |
| 616 | 621 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 617 | 622 | }while( bMore ); |
| 618 | 623 | if( iCode!=250 ){ |
| 619 | - smtp_set_error(p, 0, "server replies to end-of-DATA with: %b", &in); | |
| 624 | + smtp_set_error(p, 0, "reply to end-of-DATA with: \"%d %s\"", | |
| 625 | + iCode, zArg); | |
| 620 | 626 | return 1; |
| 621 | 627 | } |
| 622 | 628 | return 0; |
| 623 | 629 | } |
| 624 | 630 | |
| @@ -681,16 +687,13 @@ | ||
| 681 | 687 | p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort); |
| 682 | 688 | if( p->zErr ){ |
| 683 | 689 | fossil_fatal("%s", p->zErr); |
| 684 | 690 | } |
| 685 | 691 | fossil_print("Connection to \"%s\"\n", p->zHostname); |
| 686 | - smtp_client_startup(p); | |
| 687 | - if( !p->atEof ){ | |
| 688 | - smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); | |
| 689 | - } | |
| 692 | + smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); | |
| 690 | 693 | smtp_client_quit(p); |
| 691 | 694 | if( p->zErr ){ |
| 692 | 695 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 693 | 696 | } |
| 694 | 697 | smtp_session_free(p); |
| 695 | 698 | blob_reset(&body); |
| 696 | 699 | } |
| 697 | 700 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -156,14 +156,15 @@ | |
| 156 | const char *zDest; /* Domain that will receive the email */ |
| 157 | char *zHostname; /* Hostname of SMTP server for zDest */ |
| 158 | u32 smtpFlags; /* Flags changing the operation */ |
| 159 | FILE *logFile; /* Write session transcript to this log file */ |
| 160 | Blob *pTranscript; /* Record session transcript here */ |
| 161 | int atEof; /* True after connection closes */ |
| 162 | int bFatal; /* Error is fatal. Do not retry */ |
| 163 | char *zErr; /* Error message */ |
| 164 | Blob inbuf; /* Input buffer */ |
| 165 | }; |
| 166 | |
| 167 | /* Allowed values for SmtpSession.smtpFlags */ |
| 168 | #define SMTP_TRACE_STDOUT 0x00001 /* Debugging info to console */ |
| 169 | #define SMTP_TRACE_FILE 0x00002 /* Debugging info to logFile */ |
| @@ -198,12 +199,14 @@ | |
| 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 | } |
| 206 | |
| 207 | /* |
| 208 | ** Allocate a new SmtpSession object. |
| 209 | ** |
| @@ -218,43 +221,39 @@ | |
| 218 | const char *zDest, /* Domain of the server */ |
| 219 | u32 smtpFlags, /* Flags */ |
| 220 | int iPort /* TCP port if the SMTP_PORT flags is present */ |
| 221 | ){ |
| 222 | SmtpSession *p; |
| 223 | UrlData url; |
| 224 | |
| 225 | p = fossil_malloc( sizeof(*p) ); |
| 226 | memset(p, 0, sizeof(*p)); |
| 227 | p->zFrom = zFrom; |
| 228 | p->zDest = zDest; |
| 229 | p->smtpFlags = smtpFlags; |
| 230 | memset(&url, 0, sizeof(url)); |
| 231 | url.port = 25; |
| 232 | blob_init(&p->inbuf, 0, 0); |
| 233 | if( smtpFlags & SMTP_PORT ){ |
| 234 | url.port = iPort; |
| 235 | } |
| 236 | if( (smtpFlags & SMTP_DIRECT)!=0 ){ |
| 237 | int i; |
| 238 | p->zHostname = fossil_strdup(zDest); |
| 239 | for(i=0; p->zHostname[i] && p->zHostname[i]!=':'; i++){} |
| 240 | if( p->zHostname[i]==':' ){ |
| 241 | p->zHostname[i] = 0; |
| 242 | url.port = atoi(&p->zHostname[i+1]); |
| 243 | } |
| 244 | }else{ |
| 245 | p->zHostname = smtp_mx_host(zDest); |
| 246 | } |
| 247 | if( p->zHostname==0 ){ |
| 248 | smtp_set_error(p, 1, "cannot locate SMTP server for \"%s\"", zDest); |
| 249 | return p; |
| 250 | } |
| 251 | url.name = p->zHostname; |
| 252 | socket_global_init(); |
| 253 | if( socket_open(&url) ){ |
| 254 | smtp_set_error(p, 1, "can't open socket: %z", socket_errmsg()); |
| 255 | } |
| 256 | return p; |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** Configure debugging options on SmtpSession. Add all bits in |
| @@ -279,11 +278,11 @@ | |
| 279 | static void smtp_send_line(SmtpSession *p, const char *zFormat, ...){ |
| 280 | Blob b = empty_blob; |
| 281 | va_list ap; |
| 282 | char *z; |
| 283 | int n; |
| 284 | if( p->atEof ) return; |
| 285 | va_start(ap, zFormat); |
| 286 | blob_vappendf(&b, zFormat, ap); |
| 287 | va_end(ap); |
| 288 | z = blob_buffer(&b); |
| 289 | n = blob_size(&b); |
| @@ -315,11 +314,11 @@ | |
| 315 | char *z = blob_buffer(&p->inbuf); |
| 316 | int i = blob_tell(&p->inbuf); |
| 317 | int nDelay = 0; |
| 318 | if( i<n && z[n-1]=='\n' ){ |
| 319 | blob_line(&p->inbuf, in); |
| 320 | }else if( p->atEof ){ |
| 321 | blob_init(in, 0, 0); |
| 322 | }else{ |
| 323 | if( n>0 && i>=n ){ |
| 324 | blob_truncate(&p->inbuf, 0); |
| 325 | blob_rewind(&p->inbuf); |
| @@ -376,10 +375,11 @@ | |
| 376 | ){ |
| 377 | int n; |
| 378 | char *z; |
| 379 | blob_truncate(in, 0); |
| 380 | smtp_recv_line(p, in); |
| 381 | z = blob_str(in); |
| 382 | n = blob_size(in); |
| 383 | if( z[0]=='#' ){ |
| 384 | *piCode = 0; |
| 385 | *pbMore = 1; |
| @@ -397,52 +397,57 @@ | |
| 397 | int smtp_client_quit(SmtpSession *p){ |
| 398 | Blob in = BLOB_INITIALIZER; |
| 399 | int iCode = 0; |
| 400 | int bMore = 0; |
| 401 | char *zArg = 0; |
| 402 | if( !p->atEof ){ |
| 403 | smtp_send_line(p, "QUIT\r\n"); |
| 404 | do{ |
| 405 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 406 | }while( bMore ); |
| 407 | p->atEof = 1; |
| 408 | } |
| 409 | socket_close(); |
| 410 | return 0; |
| 411 | } |
| 412 | |
| 413 | /* |
| 414 | ** Begin a client SMTP session. Wait for the initial 220 then send |
| 415 | ** the EHLO and wait for a 250. |
| 416 | ** |
| 417 | ** Return 0 on success and non-zero for a failure. |
| 418 | */ |
| 419 | int smtp_client_startup(SmtpSession *p){ |
| 420 | Blob in = BLOB_INITIALIZER; |
| 421 | int iCode = 0; |
| 422 | int bMore = 0; |
| 423 | char *zArg = 0; |
| 424 | if( p==0 || p->bFatal ) return 1; |
| 425 | fossil_free(p->zErr); |
| 426 | p->zErr = 0; |
| 427 | do{ |
| 428 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 429 | }while( bMore ); |
| 430 | if( iCode!=220 ){ |
| 431 | smtp_set_error(p, 1, "server opens conversation with: %b", &in); |
| 432 | smtp_client_quit(p); |
| 433 | return 1; |
| 434 | } |
| 435 | smtp_send_line(p, "EHLO %s\r\n", p->zFrom); |
| 436 | do{ |
| 437 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 438 | }while( bMore ); |
| 439 | if( iCode!=250 ){ |
| 440 | smtp_set_error(p, 1, "server responds to EHLO with: %b", &in); |
| 441 | smtp_client_quit(p); |
| 442 | return 1; |
| 443 | } |
| 444 | return 0; |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | ** COMMAND: test-smtp-probe |
| @@ -570,38 +575,38 @@ | |
| 570 | int iCode = 0; |
| 571 | int bMore = 0; |
| 572 | char *zArg = 0; |
| 573 | Blob in; |
| 574 | blob_init(&in, 0, 0); |
| 575 | if( p->atEof && !p->bFatal ){ |
| 576 | smtp_client_startup(p); |
| 577 | if( p->atEof ) return 1; |
| 578 | } |
| 579 | smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom); |
| 580 | do{ |
| 581 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 582 | }while( bMore ); |
| 583 | if( iCode!=250 ){ |
| 584 | smtp_set_error(p, 0, "server replies to MAIL FROM with: %b", &in); |
| 585 | return 1; |
| 586 | } |
| 587 | for(i=0; i<nTo; i++){ |
| 588 | smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]); |
| 589 | do{ |
| 590 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 591 | }while( bMore ); |
| 592 | if( iCode!=250 ){ |
| 593 | smtp_set_error(p, 0, "server replies to RCPT TO with: %b", &in); |
| 594 | return 1; |
| 595 | } |
| 596 | } |
| 597 | smtp_send_line(p, "DATA\r\n"); |
| 598 | do{ |
| 599 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 600 | }while( bMore ); |
| 601 | if( iCode!=354 ){ |
| 602 | smtp_set_error(p, 0, "server replies to DATA with: %b", &in); |
| 603 | return 1; |
| 604 | } |
| 605 | smtp_send_email_body(zMsg, socket_send, 0); |
| 606 | if( p->smtpFlags & SMTP_TRACE_STDOUT ){ |
| 607 | fossil_print("C: # message content\nC: .\n"); |
| @@ -614,11 +619,12 @@ | |
| 614 | } |
| 615 | do{ |
| 616 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 617 | }while( bMore ); |
| 618 | if( iCode!=250 ){ |
| 619 | smtp_set_error(p, 0, "server replies to end-of-DATA with: %b", &in); |
| 620 | return 1; |
| 621 | } |
| 622 | return 0; |
| 623 | } |
| 624 | |
| @@ -681,16 +687,13 @@ | |
| 681 | p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort); |
| 682 | if( p->zErr ){ |
| 683 | fossil_fatal("%s", p->zErr); |
| 684 | } |
| 685 | fossil_print("Connection to \"%s\"\n", p->zHostname); |
| 686 | smtp_client_startup(p); |
| 687 | if( !p->atEof ){ |
| 688 | smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); |
| 689 | } |
| 690 | smtp_client_quit(p); |
| 691 | if( p->zErr ){ |
| 692 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 693 | } |
| 694 | smtp_session_free(p); |
| 695 | blob_reset(&body); |
| 696 | } |
| 697 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -156,14 +156,15 @@ | |
| 156 | const char *zDest; /* Domain that will receive the email */ |
| 157 | char *zHostname; /* Hostname of SMTP server for zDest */ |
| 158 | u32 smtpFlags; /* Flags changing the operation */ |
| 159 | FILE *logFile; /* Write session transcript to this log file */ |
| 160 | Blob *pTranscript; /* Record session transcript here */ |
| 161 | int bOpen; /* True if connection is Open */ |
| 162 | int bFatal; /* Error is fatal. Do not retry */ |
| 163 | char *zErr; /* Error message */ |
| 164 | Blob inbuf; /* Input buffer */ |
| 165 | UrlData url; /* Address of the server */ |
| 166 | }; |
| 167 | |
| 168 | /* Allowed values for SmtpSession.smtpFlags */ |
| 169 | #define SMTP_TRACE_STDOUT 0x00001 /* Debugging info to console */ |
| 170 | #define SMTP_TRACE_FILE 0x00002 /* Debugging info to logFile */ |
| @@ -198,12 +199,14 @@ | |
| 199 | va_list ap; |
| 200 | va_start(ap, zFormat); |
| 201 | p->zErr = vmprintf(zFormat, ap); |
| 202 | va_end(ap); |
| 203 | } |
| 204 | if( p->bOpen ){ |
| 205 | socket_close(); |
| 206 | p->bOpen = 0; |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | /* |
| 211 | ** Allocate a new SmtpSession object. |
| 212 | ** |
| @@ -218,43 +221,39 @@ | |
| 221 | const char *zDest, /* Domain of the server */ |
| 222 | u32 smtpFlags, /* Flags */ |
| 223 | int iPort /* TCP port if the SMTP_PORT flags is present */ |
| 224 | ){ |
| 225 | SmtpSession *p; |
| 226 | |
| 227 | p = fossil_malloc( sizeof(*p) ); |
| 228 | memset(p, 0, sizeof(*p)); |
| 229 | p->zFrom = zFrom; |
| 230 | p->zDest = zDest; |
| 231 | p->smtpFlags = smtpFlags; |
| 232 | p->url.port = 25; |
| 233 | blob_init(&p->inbuf, 0, 0); |
| 234 | if( smtpFlags & SMTP_PORT ){ |
| 235 | p->url.port = iPort; |
| 236 | } |
| 237 | if( (smtpFlags & SMTP_DIRECT)!=0 ){ |
| 238 | int i; |
| 239 | p->zHostname = fossil_strdup(zDest); |
| 240 | for(i=0; p->zHostname[i] && p->zHostname[i]!=':'; i++){} |
| 241 | if( p->zHostname[i]==':' ){ |
| 242 | p->zHostname[i] = 0; |
| 243 | p->url.port = atoi(&p->zHostname[i+1]); |
| 244 | } |
| 245 | }else{ |
| 246 | p->zHostname = smtp_mx_host(zDest); |
| 247 | } |
| 248 | if( p->zHostname==0 ){ |
| 249 | smtp_set_error(p, 1, "cannot locate SMTP server for \"%s\"", zDest); |
| 250 | return p; |
| 251 | } |
| 252 | p->url.name = p->zHostname; |
| 253 | socket_global_init(); |
| 254 | p->bOpen = 0; |
| 255 | return p; |
| 256 | } |
| 257 | |
| 258 | /* |
| 259 | ** Configure debugging options on SmtpSession. Add all bits in |
| @@ -279,11 +278,11 @@ | |
| 278 | static void smtp_send_line(SmtpSession *p, const char *zFormat, ...){ |
| 279 | Blob b = empty_blob; |
| 280 | va_list ap; |
| 281 | char *z; |
| 282 | int n; |
| 283 | if( !p->bOpen ) return; |
| 284 | va_start(ap, zFormat); |
| 285 | blob_vappendf(&b, zFormat, ap); |
| 286 | va_end(ap); |
| 287 | z = blob_buffer(&b); |
| 288 | n = blob_size(&b); |
| @@ -315,11 +314,11 @@ | |
| 314 | char *z = blob_buffer(&p->inbuf); |
| 315 | int i = blob_tell(&p->inbuf); |
| 316 | int nDelay = 0; |
| 317 | if( i<n && z[n-1]=='\n' ){ |
| 318 | blob_line(&p->inbuf, in); |
| 319 | }else if( !p->bOpen ){ |
| 320 | blob_init(in, 0, 0); |
| 321 | }else{ |
| 322 | if( n>0 && i>=n ){ |
| 323 | blob_truncate(&p->inbuf, 0); |
| 324 | blob_rewind(&p->inbuf); |
| @@ -376,10 +375,11 @@ | |
| 375 | ){ |
| 376 | int n; |
| 377 | char *z; |
| 378 | blob_truncate(in, 0); |
| 379 | smtp_recv_line(p, in); |
| 380 | blob_trim(in); |
| 381 | z = blob_str(in); |
| 382 | n = blob_size(in); |
| 383 | if( z[0]=='#' ){ |
| 384 | *piCode = 0; |
| 385 | *pbMore = 1; |
| @@ -397,52 +397,57 @@ | |
| 397 | int smtp_client_quit(SmtpSession *p){ |
| 398 | Blob in = BLOB_INITIALIZER; |
| 399 | int iCode = 0; |
| 400 | int bMore = 0; |
| 401 | char *zArg = 0; |
| 402 | if( p->bOpen ){ |
| 403 | smtp_send_line(p, "QUIT\r\n"); |
| 404 | do{ |
| 405 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 406 | }while( bMore ); |
| 407 | p->bOpen = 0; |
| 408 | socket_close(); |
| 409 | } |
| 410 | return 0; |
| 411 | } |
| 412 | |
| 413 | /* |
| 414 | ** Begin a client SMTP session. Wait for the initial 220 then send |
| 415 | ** the EHLO and wait for a 250. |
| 416 | ** |
| 417 | ** Return 0 on success and non-zero for a failure. |
| 418 | */ |
| 419 | static int smtp_client_startup(SmtpSession *p){ |
| 420 | Blob in = BLOB_INITIALIZER; |
| 421 | int iCode = 0; |
| 422 | int bMore = 0; |
| 423 | char *zArg = 0; |
| 424 | if( p==0 || p->bFatal ) return 1; |
| 425 | if( socket_open(&p->url) ){ |
| 426 | smtp_set_error(p, 1, "can't open socket: %z", socket_errmsg()); |
| 427 | return 1; |
| 428 | } |
| 429 | p->bOpen = 1; |
| 430 | do{ |
| 431 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 432 | }while( bMore ); |
| 433 | if( iCode!=220 ){ |
| 434 | smtp_set_error(p, 1, "conversation begins with: \"%d %s\"",iCode,zArg); |
| 435 | smtp_client_quit(p); |
| 436 | return 1; |
| 437 | } |
| 438 | smtp_send_line(p, "EHLO %s\r\n", p->zFrom); |
| 439 | do{ |
| 440 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 441 | }while( bMore ); |
| 442 | if( iCode!=250 ){ |
| 443 | smtp_set_error(p, 1, "reply to EHLO with: \"%d %s\"",iCode, zArg); |
| 444 | smtp_client_quit(p); |
| 445 | return 1; |
| 446 | } |
| 447 | fossil_free(p->zErr); |
| 448 | p->zErr = 0; |
| 449 | return 0; |
| 450 | } |
| 451 | |
| 452 | /* |
| 453 | ** COMMAND: test-smtp-probe |
| @@ -570,38 +575,38 @@ | |
| 575 | int iCode = 0; |
| 576 | int bMore = 0; |
| 577 | char *zArg = 0; |
| 578 | Blob in; |
| 579 | blob_init(&in, 0, 0); |
| 580 | if( !p->bOpen ){ |
| 581 | if( !p->bFatal ) smtp_client_startup(p); |
| 582 | if( !p->bOpen ) return 1; |
| 583 | } |
| 584 | smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom); |
| 585 | do{ |
| 586 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 587 | }while( bMore ); |
| 588 | if( iCode!=250 ){ |
| 589 | smtp_set_error(p, 0,"reply to MAIL FROM: \"%d %s\"",iCode,zArg); |
| 590 | return 1; |
| 591 | } |
| 592 | for(i=0; i<nTo; i++){ |
| 593 | smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]); |
| 594 | do{ |
| 595 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 596 | }while( bMore ); |
| 597 | if( iCode!=250 ){ |
| 598 | smtp_set_error(p, 0,"reply to RCPT TO: \"%d %s\"",iCode,zArg); |
| 599 | return 1; |
| 600 | } |
| 601 | } |
| 602 | smtp_send_line(p, "DATA\r\n"); |
| 603 | do{ |
| 604 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 605 | }while( bMore ); |
| 606 | if( iCode!=354 ){ |
| 607 | smtp_set_error(p, 0, "reply to DATA with: \"%d %s\"",iCode,zArg); |
| 608 | return 1; |
| 609 | } |
| 610 | smtp_send_email_body(zMsg, socket_send, 0); |
| 611 | if( p->smtpFlags & SMTP_TRACE_STDOUT ){ |
| 612 | fossil_print("C: # message content\nC: .\n"); |
| @@ -614,11 +619,12 @@ | |
| 619 | } |
| 620 | do{ |
| 621 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 622 | }while( bMore ); |
| 623 | if( iCode!=250 ){ |
| 624 | smtp_set_error(p, 0, "reply to end-of-DATA with: \"%d %s\"", |
| 625 | iCode, zArg); |
| 626 | return 1; |
| 627 | } |
| 628 | return 0; |
| 629 | } |
| 630 | |
| @@ -681,16 +687,13 @@ | |
| 687 | p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort); |
| 688 | if( p->zErr ){ |
| 689 | fossil_fatal("%s", p->zErr); |
| 690 | } |
| 691 | fossil_print("Connection to \"%s\"\n", p->zHostname); |
| 692 | smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); |
| 693 | smtp_client_quit(p); |
| 694 | if( p->zErr ){ |
| 695 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 696 | } |
| 697 | smtp_session_free(p); |
| 698 | blob_reset(&body); |
| 699 | } |
| 700 |