Fossil SCM
Add the smtp_send_msg() function capable of encoding and sending a complete email message.
Commit
03888604076d42a27ea216886d0bf97f356c2ef7474bfca44043b1a1c91299cc
Parent
9281d52a91d8d2f…
2 files changed
+1
-1
+179
-4
+1
-1
| --- src/http_socket.c | ||
| +++ src/http_socket.c | ||
| @@ -192,11 +192,11 @@ | ||
| 192 | 192 | } |
| 193 | 193 | |
| 194 | 194 | /* |
| 195 | 195 | ** Send content out over the open socket connection. |
| 196 | 196 | */ |
| 197 | -size_t socket_send(void *NotUsed, void *pContent, size_t N){ | |
| 197 | +size_t socket_send(void *NotUsed, const void *pContent, size_t N){ | |
| 198 | 198 | size_t sent; |
| 199 | 199 | size_t total = 0; |
| 200 | 200 | while( N>0 ){ |
| 201 | 201 | sent = send(iSocket, pContent, N, 0); |
| 202 | 202 | if( sent<=0 ) break; |
| 203 | 203 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -192,11 +192,11 @@ | |
| 192 | } |
| 193 | |
| 194 | /* |
| 195 | ** Send content out over the open socket connection. |
| 196 | */ |
| 197 | size_t socket_send(void *NotUsed, void *pContent, size_t N){ |
| 198 | size_t sent; |
| 199 | size_t total = 0; |
| 200 | while( N>0 ){ |
| 201 | sent = send(iSocket, pContent, N, 0); |
| 202 | if( sent<=0 ) break; |
| 203 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -192,11 +192,11 @@ | |
| 192 | } |
| 193 | |
| 194 | /* |
| 195 | ** Send content out over the open socket connection. |
| 196 | */ |
| 197 | size_t socket_send(void *NotUsed, const void *pContent, size_t N){ |
| 198 | size_t sent; |
| 199 | size_t total = 0; |
| 200 | while( N>0 ){ |
| 201 | sent = send(iSocket, pContent, N, 0); |
| 202 | if( sent<=0 ) break; |
| 203 |
+179
-4
| --- src/smtp.c | ||
| +++ src/smtp.c | ||
| @@ -260,11 +260,11 @@ | ||
| 260 | 260 | p->zErr = mprintf("timeout"); |
| 261 | 261 | socket_close(); |
| 262 | 262 | p->atEof = 1; |
| 263 | 263 | return; |
| 264 | 264 | }else{ |
| 265 | - sqlite3_sleep(25); | |
| 265 | + sqlite3_sleep(100); | |
| 266 | 266 | } |
| 267 | 267 | }while( n<1 || z[n-1]!='\n' ); |
| 268 | 268 | blob_truncate(&p->inbuf, n); |
| 269 | 269 | blob_line(&p->inbuf, in); |
| 270 | 270 | } |
| @@ -320,10 +320,11 @@ | ||
| 320 | 320 | char *zArg = 0; |
| 321 | 321 | smtp_send_line(p, "QUIT\r\n"); |
| 322 | 322 | do{ |
| 323 | 323 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 324 | 324 | }while( bMore ); |
| 325 | + p->atEof = 1; | |
| 325 | 326 | socket_close(); |
| 326 | 327 | return 0; |
| 327 | 328 | } |
| 328 | 329 | |
| 329 | 330 | /* |
| @@ -364,11 +365,10 @@ | ||
| 364 | 365 | ** and then immediately shutting it back down. Log all interaction |
| 365 | 366 | ** on the console. Use ME as the domain name of the sender. |
| 366 | 367 | */ |
| 367 | 368 | void test_smtp_probe(void){ |
| 368 | 369 | SmtpSession *p; |
| 369 | - int rc; | |
| 370 | 370 | const char *zDomain; |
| 371 | 371 | const char *zSelf; |
| 372 | 372 | if( g.argc!=3 && g.argc!=4 ) usage("DOMAIN [ME]"); |
| 373 | 373 | zDomain = g.argv[2]; |
| 374 | 374 | zSelf = g.argc==4 ? g.argv[3] : "fossil-scm.org"; |
| @@ -375,12 +375,187 @@ | ||
| 375 | 375 | p = smtp_session_new(zSelf, zDomain, SMTP_TRACE_STDOUT); |
| 376 | 376 | if( p->zErr ){ |
| 377 | 377 | fossil_fatal("%s", p->zErr); |
| 378 | 378 | } |
| 379 | 379 | fossil_print("Connection to \"%s\"\n", p->zHostname); |
| 380 | - rc = smtp_client_startup(p); | |
| 381 | - if( !rc ) smtp_client_quit(p); | |
| 380 | + smtp_client_startup(p); | |
| 381 | + smtp_client_quit(p); | |
| 382 | + if( p->zErr ){ | |
| 383 | + fossil_fatal("ERROR: %s\n", p->zErr); | |
| 384 | + } | |
| 385 | + smtp_session_free(p); | |
| 386 | +} | |
| 387 | + | |
| 388 | +/* | |
| 389 | +** Send the content of an email message followed by a single | |
| 390 | +** "." line. All lines must be \r\n terminated. Any isolated | |
| 391 | +** \n line terminators in the input must be converted. Also, | |
| 392 | +** an line contain using "." should be converted to "..". | |
| 393 | +*/ | |
| 394 | +static void smtp_send_email_body( | |
| 395 | + const char *zMsg, /* Message to send */ | |
| 396 | + size_t (*xSend)(void*,const void*,size_t), /* Sender callback function */ | |
| 397 | + void *pArg /* First arg to sender */ | |
| 398 | +){ | |
| 399 | + Blob in; | |
| 400 | + Blob out = BLOB_INITIALIZER; | |
| 401 | + Blob line; | |
| 402 | + blob_init(&in, zMsg, -1); | |
| 403 | + while( blob_line(&in, &line) ){ | |
| 404 | + char *z = blob_buffer(&line); | |
| 405 | + int n = blob_size(&line); | |
| 406 | + if( n==0 ) break; | |
| 407 | + n--; | |
| 408 | + if( n && z[n-1]=='\r' ) n--; | |
| 409 | + if( n==1 && z[0]=='.' ){ | |
| 410 | + blob_append(&out, "..\r\n", 4); | |
| 411 | + }else{ | |
| 412 | + blob_append(&out, z, n); | |
| 413 | + blob_append(&out, "\r\n", 2); | |
| 414 | + } | |
| 415 | + } | |
| 416 | + blob_append(&out, ".\r\n", 3); | |
| 417 | + xSend(pArg, blob_buffer(&out), blob_size(&out)); | |
| 418 | + blob_zero(&out); | |
| 419 | + blob_zero(&line); | |
| 420 | +} | |
| 421 | + | |
| 422 | +/* A sender function appropriate for use by smtp_send_email_body() to | |
| 423 | +** send all content to the console, for testing. | |
| 424 | +*/ | |
| 425 | +static size_t smtp_test_sender(void *NotUsed, const void *pContent, size_t N){ | |
| 426 | + return fwrite(pContent, 1, N, stdout); | |
| 427 | +} | |
| 428 | + | |
| 429 | +/* | |
| 430 | +** COMMAND: test-smtp-senddata | |
| 431 | +** | |
| 432 | +** Usage: %fossil test-smtp-senddata FILE | |
| 433 | +** | |
| 434 | +** Read content from FILE, then send it to stdout encoded as if sent | |
| 435 | +** to the DATA portion of an SMTP session. This command is used to | |
| 436 | +** test the encoding logic. | |
| 437 | +*/ | |
| 438 | +void test_smtp_senddata(void){ | |
| 439 | + Blob f; | |
| 440 | + if( g.argc!=3 ) usage("FILE"); | |
| 441 | + blob_read_from_file(&f, g.argv[2], ExtFILE); | |
| 442 | + smtp_send_email_body(blob_str(&f), smtp_test_sender, 0); | |
| 443 | + blob_zero(&f); | |
| 444 | +} | |
| 445 | + | |
| 446 | +/* | |
| 447 | +** Send a single email message to the SMTP server. | |
| 448 | +** | |
| 449 | +** All email addresses (zFrom and azTo) must be plain "local@domain" | |
| 450 | +** format with the surrounding "<..>". This routine will add the | |
| 451 | +** necessary "<..>". | |
| 452 | +** | |
| 453 | +** The body of the email should be well-structured. This routine will | |
| 454 | +** convert any \n line endings into \r\n and will escape lines containing | |
| 455 | +** just ".", but will not make any other alterations or corrections to | |
| 456 | +** the message content. | |
| 457 | +** | |
| 458 | +** Return 0 on success. Otherwise an error code. | |
| 459 | +*/ | |
| 460 | +int smtp_send_msg( | |
| 461 | + SmtpSession *p, /* The SMTP server to which the message is sent */ | |
| 462 | + const char *zFrom, /* Who the message is from */ | |
| 463 | + int nTo, /* Number of receipients */ | |
| 464 | + const char **azTo, /* Email address of each recipient */ | |
| 465 | + const char *zMsg /* Body of the message */ | |
| 466 | +){ | |
| 467 | + int i; | |
| 468 | + int iCode = 0; | |
| 469 | + int bMore = 0; | |
| 470 | + char *zArg = 0; | |
| 471 | + Blob in; | |
| 472 | + blob_init(&in, 0, 0); | |
| 473 | + smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom); | |
| 474 | + do{ | |
| 475 | + smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); | |
| 476 | + }while( bMore ); | |
| 477 | + if( iCode!=250 ) return 1; | |
| 478 | + for(i=0; i<nTo; i++){ | |
| 479 | + smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]); | |
| 480 | + do{ | |
| 481 | + smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); | |
| 482 | + }while( bMore ); | |
| 483 | + if( iCode!=250 ) return 1; | |
| 484 | + } | |
| 485 | + smtp_send_line(p, "DATA\r\n"); | |
| 486 | + do{ | |
| 487 | + smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); | |
| 488 | + }while( bMore ); | |
| 489 | + if( iCode!=354 ) return 1; | |
| 490 | + smtp_send_email_body(zMsg, socket_send, 0); | |
| 491 | + if( p->smtpFlags & SMTP_TRACE_STDOUT ){ | |
| 492 | + fossil_print("C: # message content\nC: .\n"); | |
| 493 | + } | |
| 494 | + if( p->smtpFlags & SMTP_TRACE_FILE ){ | |
| 495 | + fprintf(p->logFile, "C: # message content\nC: .\n"); | |
| 496 | + } | |
| 497 | + if( p->smtpFlags & SMTP_TRACE_BLOB ){ | |
| 498 | + blob_appendf(p->pTranscript, "C: # message content\nC: .\n"); | |
| 499 | + } | |
| 500 | + do{ | |
| 501 | + smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); | |
| 502 | + }while( bMore ); | |
| 503 | + if( iCode!=250 ) return 1; | |
| 504 | + return 0; | |
| 505 | +} | |
| 506 | + | |
| 507 | +/* | |
| 508 | +** The input is a base email address of the form "local@domain". | |
| 509 | +** Return a pointer to just the "domain" part. | |
| 510 | +*/ | |
| 511 | +static const char *domainOfAddr(const char *z){ | |
| 512 | + while( z[0] && z[0]!='@' ) z++; | |
| 513 | + if( z[0]==0 ) return 0; | |
| 514 | + return z+1; | |
| 515 | +} | |
| 516 | + | |
| 517 | + | |
| 518 | +/* | |
| 519 | +** COMMAND: test-smtp-send | |
| 520 | +** | |
| 521 | +** Usage: %fossil test-smtp-send EMAIL FROM TO ... | |
| 522 | +** | |
| 523 | +** Use SMTP to send the email message contained in the file named EMAIL | |
| 524 | +** to the list of users TO. FROM is the sender of the email. | |
| 525 | +** | |
| 526 | +** Options: | |
| 527 | +** | |
| 528 | +** --trace Show the SMTP conversation on the console | |
| 529 | +*/ | |
| 530 | +void test_smtp_send(void){ | |
| 531 | + SmtpSession *p; | |
| 532 | + const char *zFrom; | |
| 533 | + int nTo; | |
| 534 | + const char *zToDomain; | |
| 535 | + const char *zFromDomain; | |
| 536 | + const char **azTo; | |
| 537 | + Blob body; | |
| 538 | + u32 smtpFlags = 0; | |
| 539 | + if( find_option("trace",0,0)!=0 ) smtpFlags |= SMTP_TRACE_STDOUT; | |
| 540 | + verify_all_options(); | |
| 541 | + if( g.argc<5 ) usage("EMAIL FROM TO ..."); | |
| 542 | + blob_read_from_file(&body, g.argv[2], ExtFILE); | |
| 543 | + zFrom = g.argv[3]; | |
| 544 | + nTo = g.argc-4; | |
| 545 | + azTo = (const char**)g.argv+4; | |
| 546 | + zFromDomain = domainOfAddr(zFrom); | |
| 547 | + zToDomain = domainOfAddr(azTo[0]); | |
| 548 | + p = smtp_session_new(zFromDomain, zToDomain, smtpFlags); | |
| 549 | + if( p->zErr ){ | |
| 550 | + fossil_fatal("%s", p->zErr); | |
| 551 | + } | |
| 552 | + fossil_print("Connection to \"%s\"\n", p->zHostname); | |
| 553 | + smtp_client_startup(p); | |
| 554 | + smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); | |
| 555 | + smtp_client_quit(p); | |
| 382 | 556 | if( p->zErr ){ |
| 383 | 557 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 384 | 558 | } |
| 385 | 559 | smtp_session_free(p); |
| 560 | + blob_zero(&body); | |
| 386 | 561 | } |
| 387 | 562 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -260,11 +260,11 @@ | |
| 260 | p->zErr = mprintf("timeout"); |
| 261 | socket_close(); |
| 262 | p->atEof = 1; |
| 263 | return; |
| 264 | }else{ |
| 265 | sqlite3_sleep(25); |
| 266 | } |
| 267 | }while( n<1 || z[n-1]!='\n' ); |
| 268 | blob_truncate(&p->inbuf, n); |
| 269 | blob_line(&p->inbuf, in); |
| 270 | } |
| @@ -320,10 +320,11 @@ | |
| 320 | char *zArg = 0; |
| 321 | smtp_send_line(p, "QUIT\r\n"); |
| 322 | do{ |
| 323 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 324 | }while( bMore ); |
| 325 | socket_close(); |
| 326 | return 0; |
| 327 | } |
| 328 | |
| 329 | /* |
| @@ -364,11 +365,10 @@ | |
| 364 | ** and then immediately shutting it back down. Log all interaction |
| 365 | ** on the console. Use ME as the domain name of the sender. |
| 366 | */ |
| 367 | void test_smtp_probe(void){ |
| 368 | SmtpSession *p; |
| 369 | int rc; |
| 370 | const char *zDomain; |
| 371 | const char *zSelf; |
| 372 | if( g.argc!=3 && g.argc!=4 ) usage("DOMAIN [ME]"); |
| 373 | zDomain = g.argv[2]; |
| 374 | zSelf = g.argc==4 ? g.argv[3] : "fossil-scm.org"; |
| @@ -375,12 +375,187 @@ | |
| 375 | p = smtp_session_new(zSelf, zDomain, SMTP_TRACE_STDOUT); |
| 376 | if( p->zErr ){ |
| 377 | fossil_fatal("%s", p->zErr); |
| 378 | } |
| 379 | fossil_print("Connection to \"%s\"\n", p->zHostname); |
| 380 | rc = smtp_client_startup(p); |
| 381 | if( !rc ) smtp_client_quit(p); |
| 382 | if( p->zErr ){ |
| 383 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 384 | } |
| 385 | smtp_session_free(p); |
| 386 | } |
| 387 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -260,11 +260,11 @@ | |
| 260 | p->zErr = mprintf("timeout"); |
| 261 | socket_close(); |
| 262 | p->atEof = 1; |
| 263 | return; |
| 264 | }else{ |
| 265 | sqlite3_sleep(100); |
| 266 | } |
| 267 | }while( n<1 || z[n-1]!='\n' ); |
| 268 | blob_truncate(&p->inbuf, n); |
| 269 | blob_line(&p->inbuf, in); |
| 270 | } |
| @@ -320,10 +320,11 @@ | |
| 320 | char *zArg = 0; |
| 321 | smtp_send_line(p, "QUIT\r\n"); |
| 322 | do{ |
| 323 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 324 | }while( bMore ); |
| 325 | p->atEof = 1; |
| 326 | socket_close(); |
| 327 | return 0; |
| 328 | } |
| 329 | |
| 330 | /* |
| @@ -364,11 +365,10 @@ | |
| 365 | ** and then immediately shutting it back down. Log all interaction |
| 366 | ** on the console. Use ME as the domain name of the sender. |
| 367 | */ |
| 368 | void test_smtp_probe(void){ |
| 369 | SmtpSession *p; |
| 370 | const char *zDomain; |
| 371 | const char *zSelf; |
| 372 | if( g.argc!=3 && g.argc!=4 ) usage("DOMAIN [ME]"); |
| 373 | zDomain = g.argv[2]; |
| 374 | zSelf = g.argc==4 ? g.argv[3] : "fossil-scm.org"; |
| @@ -375,12 +375,187 @@ | |
| 375 | p = smtp_session_new(zSelf, zDomain, SMTP_TRACE_STDOUT); |
| 376 | if( p->zErr ){ |
| 377 | fossil_fatal("%s", p->zErr); |
| 378 | } |
| 379 | fossil_print("Connection to \"%s\"\n", p->zHostname); |
| 380 | smtp_client_startup(p); |
| 381 | smtp_client_quit(p); |
| 382 | if( p->zErr ){ |
| 383 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 384 | } |
| 385 | smtp_session_free(p); |
| 386 | } |
| 387 | |
| 388 | /* |
| 389 | ** Send the content of an email message followed by a single |
| 390 | ** "." line. All lines must be \r\n terminated. Any isolated |
| 391 | ** \n line terminators in the input must be converted. Also, |
| 392 | ** an line contain using "." should be converted to "..". |
| 393 | */ |
| 394 | static void smtp_send_email_body( |
| 395 | const char *zMsg, /* Message to send */ |
| 396 | size_t (*xSend)(void*,const void*,size_t), /* Sender callback function */ |
| 397 | void *pArg /* First arg to sender */ |
| 398 | ){ |
| 399 | Blob in; |
| 400 | Blob out = BLOB_INITIALIZER; |
| 401 | Blob line; |
| 402 | blob_init(&in, zMsg, -1); |
| 403 | while( blob_line(&in, &line) ){ |
| 404 | char *z = blob_buffer(&line); |
| 405 | int n = blob_size(&line); |
| 406 | if( n==0 ) break; |
| 407 | n--; |
| 408 | if( n && z[n-1]=='\r' ) n--; |
| 409 | if( n==1 && z[0]=='.' ){ |
| 410 | blob_append(&out, "..\r\n", 4); |
| 411 | }else{ |
| 412 | blob_append(&out, z, n); |
| 413 | blob_append(&out, "\r\n", 2); |
| 414 | } |
| 415 | } |
| 416 | blob_append(&out, ".\r\n", 3); |
| 417 | xSend(pArg, blob_buffer(&out), blob_size(&out)); |
| 418 | blob_zero(&out); |
| 419 | blob_zero(&line); |
| 420 | } |
| 421 | |
| 422 | /* A sender function appropriate for use by smtp_send_email_body() to |
| 423 | ** send all content to the console, for testing. |
| 424 | */ |
| 425 | static size_t smtp_test_sender(void *NotUsed, const void *pContent, size_t N){ |
| 426 | return fwrite(pContent, 1, N, stdout); |
| 427 | } |
| 428 | |
| 429 | /* |
| 430 | ** COMMAND: test-smtp-senddata |
| 431 | ** |
| 432 | ** Usage: %fossil test-smtp-senddata FILE |
| 433 | ** |
| 434 | ** Read content from FILE, then send it to stdout encoded as if sent |
| 435 | ** to the DATA portion of an SMTP session. This command is used to |
| 436 | ** test the encoding logic. |
| 437 | */ |
| 438 | void test_smtp_senddata(void){ |
| 439 | Blob f; |
| 440 | if( g.argc!=3 ) usage("FILE"); |
| 441 | blob_read_from_file(&f, g.argv[2], ExtFILE); |
| 442 | smtp_send_email_body(blob_str(&f), smtp_test_sender, 0); |
| 443 | blob_zero(&f); |
| 444 | } |
| 445 | |
| 446 | /* |
| 447 | ** Send a single email message to the SMTP server. |
| 448 | ** |
| 449 | ** All email addresses (zFrom and azTo) must be plain "local@domain" |
| 450 | ** format with the surrounding "<..>". This routine will add the |
| 451 | ** necessary "<..>". |
| 452 | ** |
| 453 | ** The body of the email should be well-structured. This routine will |
| 454 | ** convert any \n line endings into \r\n and will escape lines containing |
| 455 | ** just ".", but will not make any other alterations or corrections to |
| 456 | ** the message content. |
| 457 | ** |
| 458 | ** Return 0 on success. Otherwise an error code. |
| 459 | */ |
| 460 | int smtp_send_msg( |
| 461 | SmtpSession *p, /* The SMTP server to which the message is sent */ |
| 462 | const char *zFrom, /* Who the message is from */ |
| 463 | int nTo, /* Number of receipients */ |
| 464 | const char **azTo, /* Email address of each recipient */ |
| 465 | const char *zMsg /* Body of the message */ |
| 466 | ){ |
| 467 | int i; |
| 468 | int iCode = 0; |
| 469 | int bMore = 0; |
| 470 | char *zArg = 0; |
| 471 | Blob in; |
| 472 | blob_init(&in, 0, 0); |
| 473 | smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom); |
| 474 | do{ |
| 475 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 476 | }while( bMore ); |
| 477 | if( iCode!=250 ) return 1; |
| 478 | for(i=0; i<nTo; i++){ |
| 479 | smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]); |
| 480 | do{ |
| 481 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 482 | }while( bMore ); |
| 483 | if( iCode!=250 ) return 1; |
| 484 | } |
| 485 | smtp_send_line(p, "DATA\r\n"); |
| 486 | do{ |
| 487 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 488 | }while( bMore ); |
| 489 | if( iCode!=354 ) return 1; |
| 490 | smtp_send_email_body(zMsg, socket_send, 0); |
| 491 | if( p->smtpFlags & SMTP_TRACE_STDOUT ){ |
| 492 | fossil_print("C: # message content\nC: .\n"); |
| 493 | } |
| 494 | if( p->smtpFlags & SMTP_TRACE_FILE ){ |
| 495 | fprintf(p->logFile, "C: # message content\nC: .\n"); |
| 496 | } |
| 497 | if( p->smtpFlags & SMTP_TRACE_BLOB ){ |
| 498 | blob_appendf(p->pTranscript, "C: # message content\nC: .\n"); |
| 499 | } |
| 500 | do{ |
| 501 | smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg); |
| 502 | }while( bMore ); |
| 503 | if( iCode!=250 ) return 1; |
| 504 | return 0; |
| 505 | } |
| 506 | |
| 507 | /* |
| 508 | ** The input is a base email address of the form "local@domain". |
| 509 | ** Return a pointer to just the "domain" part. |
| 510 | */ |
| 511 | static const char *domainOfAddr(const char *z){ |
| 512 | while( z[0] && z[0]!='@' ) z++; |
| 513 | if( z[0]==0 ) return 0; |
| 514 | return z+1; |
| 515 | } |
| 516 | |
| 517 | |
| 518 | /* |
| 519 | ** COMMAND: test-smtp-send |
| 520 | ** |
| 521 | ** Usage: %fossil test-smtp-send EMAIL FROM TO ... |
| 522 | ** |
| 523 | ** Use SMTP to send the email message contained in the file named EMAIL |
| 524 | ** to the list of users TO. FROM is the sender of the email. |
| 525 | ** |
| 526 | ** Options: |
| 527 | ** |
| 528 | ** --trace Show the SMTP conversation on the console |
| 529 | */ |
| 530 | void test_smtp_send(void){ |
| 531 | SmtpSession *p; |
| 532 | const char *zFrom; |
| 533 | int nTo; |
| 534 | const char *zToDomain; |
| 535 | const char *zFromDomain; |
| 536 | const char **azTo; |
| 537 | Blob body; |
| 538 | u32 smtpFlags = 0; |
| 539 | if( find_option("trace",0,0)!=0 ) smtpFlags |= SMTP_TRACE_STDOUT; |
| 540 | verify_all_options(); |
| 541 | if( g.argc<5 ) usage("EMAIL FROM TO ..."); |
| 542 | blob_read_from_file(&body, g.argv[2], ExtFILE); |
| 543 | zFrom = g.argv[3]; |
| 544 | nTo = g.argc-4; |
| 545 | azTo = (const char**)g.argv+4; |
| 546 | zFromDomain = domainOfAddr(zFrom); |
| 547 | zToDomain = domainOfAddr(azTo[0]); |
| 548 | p = smtp_session_new(zFromDomain, zToDomain, smtpFlags); |
| 549 | if( p->zErr ){ |
| 550 | fossil_fatal("%s", p->zErr); |
| 551 | } |
| 552 | fossil_print("Connection to \"%s\"\n", p->zHostname); |
| 553 | smtp_client_startup(p); |
| 554 | smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body)); |
| 555 | smtp_client_quit(p); |
| 556 | if( p->zErr ){ |
| 557 | fossil_fatal("ERROR: %s\n", p->zErr); |
| 558 | } |
| 559 | smtp_session_free(p); |
| 560 | blob_zero(&body); |
| 561 | } |
| 562 |