1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * RFC1870 SMTP Service Extension for Message Size 22 * RFC2195 CRAM-MD5 authentication 23 * RFC2831 DIGEST-MD5 authentication 24 * RFC3207 SMTP over TLS 25 * RFC4422 Simple Authentication and Security Layer (SASL) 26 * RFC4616 PLAIN authentication 27 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism 28 * RFC4954 SMTP Authentication 29 * RFC5321 SMTP protocol 30 * RFC6749 OAuth 2.0 Authorization Framework 31 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt> 32 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> 33 * 34 ***************************************************************************/ 35 36 #include "curl_setup.h" 37 38 #ifndef CURL_DISABLE_SMTP 39 40 #ifdef HAVE_NETINET_IN_H 41 #include <netinet/in.h> 42 #endif 43 #ifdef HAVE_ARPA_INET_H 44 #include <arpa/inet.h> 45 #endif 46 #ifdef HAVE_UTSNAME_H 47 #include <sys/utsname.h> 48 #endif 49 #ifdef HAVE_NETDB_H 50 #include <netdb.h> 51 #endif 52 #ifdef __VMS 53 #include <in.h> 54 #include <inet.h> 55 #endif 56 57 #if (defined(NETWARE) && defined(__NOVELL_LIBC__)) 58 #undef in_addr_t 59 #define in_addr_t unsigned long 60 #endif 61 62 #include <curl/curl.h> 63 #include "urldata.h" 64 #include "sendf.h" 65 #include "hostip.h" 66 #include "progress.h" 67 #include "transfer.h" 68 #include "escape.h" 69 #include "http.h" /* for HTTP proxy tunnel stuff */ 70 #include "socks.h" 71 #include "smtp.h" 72 #include "strtoofft.h" 73 #include "strcase.h" 74 #include "vtls/vtls.h" 75 #include "connect.h" 76 #include "strerror.h" 77 #include "select.h" 78 #include "multiif.h" 79 #include "url.h" 80 #include "curl_gethostname.h" 81 #include "curl_sasl.h" 82 #include "warnless.h" 83 /* The last 3 #include files should be in this order */ 84 #include "curl_printf.h" 85 #include "curl_memory.h" 86 #include "memdebug.h" 87 88 /* Local API functions */ 89 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done); 90 static CURLcode smtp_do(struct connectdata *conn, bool *done); 91 static CURLcode smtp_done(struct connectdata *conn, CURLcode status, 92 bool premature); 93 static CURLcode smtp_connect(struct connectdata *conn, bool *done); 94 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead); 95 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done); 96 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, 97 int numsocks); 98 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done); 99 static CURLcode smtp_setup_connection(struct connectdata *conn); 100 static CURLcode smtp_parse_url_options(struct connectdata *conn); 101 static CURLcode smtp_parse_url_path(struct connectdata *conn); 102 static CURLcode smtp_parse_custom_request(struct connectdata *conn); 103 static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech, 104 const char *initresp); 105 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp); 106 static void smtp_get_message(char *buffer, char **outptr); 107 108 /* 109 * SMTP protocol handler. 110 */ 111 112 const struct Curl_handler Curl_handler_smtp = { 113 "SMTP", /* scheme */ 114 smtp_setup_connection, /* setup_connection */ 115 smtp_do, /* do_it */ 116 smtp_done, /* done */ 117 ZERO_NULL, /* do_more */ 118 smtp_connect, /* connect_it */ 119 smtp_multi_statemach, /* connecting */ 120 smtp_doing, /* doing */ 121 smtp_getsock, /* proto_getsock */ 122 smtp_getsock, /* doing_getsock */ 123 ZERO_NULL, /* domore_getsock */ 124 ZERO_NULL, /* perform_getsock */ 125 smtp_disconnect, /* disconnect */ 126 ZERO_NULL, /* readwrite */ 127 PORT_SMTP, /* defport */ 128 CURLPROTO_SMTP, /* protocol */ 129 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */ 130 }; 131 132 #ifdef USE_SSL 133 /* 134 * SMTPS protocol handler. 135 */ 136 137 const struct Curl_handler Curl_handler_smtps = { 138 "SMTPS", /* scheme */ 139 smtp_setup_connection, /* setup_connection */ 140 smtp_do, /* do_it */ 141 smtp_done, /* done */ 142 ZERO_NULL, /* do_more */ 143 smtp_connect, /* connect_it */ 144 smtp_multi_statemach, /* connecting */ 145 smtp_doing, /* doing */ 146 smtp_getsock, /* proto_getsock */ 147 smtp_getsock, /* doing_getsock */ 148 ZERO_NULL, /* domore_getsock */ 149 ZERO_NULL, /* perform_getsock */ 150 smtp_disconnect, /* disconnect */ 151 ZERO_NULL, /* readwrite */ 152 PORT_SMTPS, /* defport */ 153 CURLPROTO_SMTPS, /* protocol */ 154 PROTOPT_CLOSEACTION | PROTOPT_SSL 155 | PROTOPT_NOURLQUERY /* flags */ 156 }; 157 #endif 158 159 #ifndef CURL_DISABLE_HTTP 160 /* 161 * HTTP-proxyed SMTP protocol handler. 162 */ 163 164 static const struct Curl_handler Curl_handler_smtp_proxy = { 165 "SMTP", /* scheme */ 166 Curl_http_setup_conn, /* setup_connection */ 167 Curl_http, /* do_it */ 168 Curl_http_done, /* done */ 169 ZERO_NULL, /* do_more */ 170 ZERO_NULL, /* connect_it */ 171 ZERO_NULL, /* connecting */ 172 ZERO_NULL, /* doing */ 173 ZERO_NULL, /* proto_getsock */ 174 ZERO_NULL, /* doing_getsock */ 175 ZERO_NULL, /* domore_getsock */ 176 ZERO_NULL, /* perform_getsock */ 177 ZERO_NULL, /* disconnect */ 178 ZERO_NULL, /* readwrite */ 179 PORT_SMTP, /* defport */ 180 CURLPROTO_HTTP, /* protocol */ 181 PROTOPT_NONE /* flags */ 182 }; 183 184 #ifdef USE_SSL 185 /* 186 * HTTP-proxyed SMTPS protocol handler. 187 */ 188 189 static const struct Curl_handler Curl_handler_smtps_proxy = { 190 "SMTPS", /* scheme */ 191 Curl_http_setup_conn, /* setup_connection */ 192 Curl_http, /* do_it */ 193 Curl_http_done, /* done */ 194 ZERO_NULL, /* do_more */ 195 ZERO_NULL, /* connect_it */ 196 ZERO_NULL, /* connecting */ 197 ZERO_NULL, /* doing */ 198 ZERO_NULL, /* proto_getsock */ 199 ZERO_NULL, /* doing_getsock */ 200 ZERO_NULL, /* domore_getsock */ 201 ZERO_NULL, /* perform_getsock */ 202 ZERO_NULL, /* disconnect */ 203 ZERO_NULL, /* readwrite */ 204 PORT_SMTPS, /* defport */ 205 CURLPROTO_HTTP, /* protocol */ 206 PROTOPT_NONE /* flags */ 207 }; 208 #endif 209 #endif 210 211 /* SASL parameters for the smtp protocol */ 212 static const struct SASLproto saslsmtp = { 213 "smtp", /* The service name */ 214 334, /* Code received when continuation is expected */ 215 235, /* Code to receive upon authentication success */ 216 512 - 8, /* Maximum initial response length (no max) */ 217 smtp_perform_auth, /* Send authentication command */ 218 smtp_continue_auth, /* Send authentication continuation */ 219 smtp_get_message /* Get SASL response message */ 220 }; 221 222 #ifdef USE_SSL 223 static void smtp_to_smtps(struct connectdata *conn) 224 { 225 /* Change the connection handler */ 226 conn->handler = &Curl_handler_smtps; 227 228 /* Set the connection's upgraded to TLS flag */ 229 conn->tls_upgraded = TRUE; 230 } 231 #else 232 #define smtp_to_smtps(x) Curl_nop_stmt 233 #endif 234 235 /*********************************************************************** 236 * 237 * smtp_endofresp() 238 * 239 * Checks for an ending SMTP status code at the start of the given string, but 240 * also detects various capabilities from the EHLO response including the 241 * supported authentication mechanisms. 242 */ 243 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len, 244 int *resp) 245 { 246 struct smtp_conn *smtpc = &conn->proto.smtpc; 247 bool result = FALSE; 248 249 /* Nothing for us */ 250 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) 251 return FALSE; 252 253 /* Do we have a command response? This should be the response code followed 254 by a space and optionally some text as per RFC-5321 and as outlined in 255 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and 256 only send the response code instead as per Section 4.2. */ 257 if(line[3] == ' ' || len == 5) { 258 result = TRUE; 259 *resp = curlx_sltosi(strtol(line, NULL, 10)); 260 261 /* Make sure real server never sends internal value */ 262 if(*resp == 1) 263 *resp = 0; 264 } 265 /* Do we have a multiline (continuation) response? */ 266 else if(line[3] == '-' && 267 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { 268 result = TRUE; 269 *resp = 1; /* Internal response code */ 270 } 271 272 return result; 273 } 274 275 /*********************************************************************** 276 * 277 * smtp_get_message() 278 * 279 * Gets the authentication message from the response buffer. 280 */ 281 static void smtp_get_message(char *buffer, char **outptr) 282 { 283 size_t len = 0; 284 char *message = NULL; 285 286 /* Find the start of the message */ 287 for(message = buffer + 4; *message == ' ' || *message == '\t'; message++) 288 ; 289 290 /* Find the end of the message */ 291 for(len = strlen(message); len--;) 292 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && 293 message[len] != '\t') 294 break; 295 296 /* Terminate the message */ 297 if(++len) { 298 message[len] = '\0'; 299 } 300 301 *outptr = message; 302 } 303 304 /*********************************************************************** 305 * 306 * state() 307 * 308 * This is the ONLY way to change SMTP state! 309 */ 310 static void state(struct connectdata *conn, smtpstate newstate) 311 { 312 struct smtp_conn *smtpc = &conn->proto.smtpc; 313 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 314 /* for debug purposes */ 315 static const char * const names[] = { 316 "STOP", 317 "SERVERGREET", 318 "EHLO", 319 "HELO", 320 "STARTTLS", 321 "UPGRADETLS", 322 "AUTH", 323 "COMMAND", 324 "MAIL", 325 "RCPT", 326 "DATA", 327 "POSTDATA", 328 "QUIT", 329 /* LAST */ 330 }; 331 332 if(smtpc->state != newstate) 333 infof(conn->data, "SMTP %p state change from %s to %s\n", 334 (void *)smtpc, names[smtpc->state], names[newstate]); 335 #endif 336 337 smtpc->state = newstate; 338 } 339 340 /*********************************************************************** 341 * 342 * smtp_perform_ehlo() 343 * 344 * Sends the EHLO command to not only initialise communication with the ESMTP 345 * server but to also obtain a list of server side supported capabilities. 346 */ 347 static CURLcode smtp_perform_ehlo(struct connectdata *conn) 348 { 349 CURLcode result = CURLE_OK; 350 struct smtp_conn *smtpc = &conn->proto.smtpc; 351 352 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */ 353 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism 354 used for esmtp connections */ 355 smtpc->tls_supported = FALSE; /* Clear the TLS capability */ 356 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ 357 358 /* Send the EHLO command */ 359 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain); 360 361 if(!result) 362 state(conn, SMTP_EHLO); 363 364 return result; 365 } 366 367 /*********************************************************************** 368 * 369 * smtp_perform_helo() 370 * 371 * Sends the HELO command to initialise communication with the SMTP server. 372 */ 373 static CURLcode smtp_perform_helo(struct connectdata *conn) 374 { 375 CURLcode result = CURLE_OK; 376 struct smtp_conn *smtpc = &conn->proto.smtpc; 377 378 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used 379 in smtp connections */ 380 381 /* Send the HELO command */ 382 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain); 383 384 if(!result) 385 state(conn, SMTP_HELO); 386 387 return result; 388 } 389 390 /*********************************************************************** 391 * 392 * smtp_perform_starttls() 393 * 394 * Sends the STLS command to start the upgrade to TLS. 395 */ 396 static CURLcode smtp_perform_starttls(struct connectdata *conn) 397 { 398 CURLcode result = CURLE_OK; 399 400 /* Send the STARTTLS command */ 401 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS"); 402 403 if(!result) 404 state(conn, SMTP_STARTTLS); 405 406 return result; 407 } 408 409 /*********************************************************************** 410 * 411 * smtp_perform_upgrade_tls() 412 * 413 * Performs the upgrade to TLS. 414 */ 415 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn) 416 { 417 CURLcode result = CURLE_OK; 418 struct smtp_conn *smtpc = &conn->proto.smtpc; 419 420 /* Start the SSL connection */ 421 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); 422 423 if(!result) { 424 if(smtpc->state != SMTP_UPGRADETLS) 425 state(conn, SMTP_UPGRADETLS); 426 427 if(smtpc->ssldone) { 428 smtp_to_smtps(conn); 429 result = smtp_perform_ehlo(conn); 430 } 431 } 432 433 return result; 434 } 435 436 /*********************************************************************** 437 * 438 * smtp_perform_auth() 439 * 440 * Sends an AUTH command allowing the client to login with the given SASL 441 * authentication mechanism. 442 */ 443 static CURLcode smtp_perform_auth(struct connectdata *conn, 444 const char *mech, 445 const char *initresp) 446 { 447 CURLcode result = CURLE_OK; 448 struct smtp_conn *smtpc = &conn->proto.smtpc; 449 450 if(initresp) { /* AUTH <mech> ...<crlf> */ 451 /* Send the AUTH command with the initial response */ 452 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); 453 } 454 else { 455 /* Send the AUTH command */ 456 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); 457 } 458 459 return result; 460 } 461 462 /*********************************************************************** 463 * 464 * smtp_continue_auth() 465 * 466 * Sends SASL continuation data or cancellation. 467 */ 468 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp) 469 { 470 struct smtp_conn *smtpc = &conn->proto.smtpc; 471 472 return Curl_pp_sendf(&smtpc->pp, "%s", resp); 473 } 474 475 /*********************************************************************** 476 * 477 * smtp_perform_authentication() 478 * 479 * Initiates the authentication sequence, with the appropriate SASL 480 * authentication mechanism. 481 */ 482 static CURLcode smtp_perform_authentication(struct connectdata *conn) 483 { 484 CURLcode result = CURLE_OK; 485 struct smtp_conn *smtpc = &conn->proto.smtpc; 486 saslprogress progress; 487 488 /* Check we have enough data to authenticate with, and the 489 server supports authentiation, and end the connect phase if not */ 490 if(!smtpc->auth_supported || 491 !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) { 492 state(conn, SMTP_STOP); 493 return result; 494 } 495 496 /* Calculate the SASL login details */ 497 result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress); 498 499 if(!result) { 500 if(progress == SASL_INPROGRESS) 501 state(conn, SMTP_AUTH); 502 else { 503 /* Other mechanisms not supported */ 504 infof(conn->data, "No known authentication mechanisms supported!\n"); 505 result = CURLE_LOGIN_DENIED; 506 } 507 } 508 509 return result; 510 } 511 512 /*********************************************************************** 513 * 514 * smtp_perform_command() 515 * 516 * Sends a SMTP based command. 517 */ 518 static CURLcode smtp_perform_command(struct connectdata *conn) 519 { 520 CURLcode result = CURLE_OK; 521 struct Curl_easy *data = conn->data; 522 struct SMTP *smtp = data->req.protop; 523 524 /* Send the command */ 525 if(smtp->rcpt) 526 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s", 527 smtp->custom && smtp->custom[0] != '\0' ? 528 smtp->custom : "VRFY", 529 smtp->rcpt->data); 530 else 531 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", 532 smtp->custom && smtp->custom[0] != '\0' ? 533 smtp->custom : "HELP"); 534 535 if(!result) 536 state(conn, SMTP_COMMAND); 537 538 return result; 539 } 540 541 /*********************************************************************** 542 * 543 * smtp_perform_mail() 544 * 545 * Sends an MAIL command to initiate the upload of a message. 546 */ 547 static CURLcode smtp_perform_mail(struct connectdata *conn) 548 { 549 char *from = NULL; 550 char *auth = NULL; 551 char *size = NULL; 552 CURLcode result = CURLE_OK; 553 struct Curl_easy *data = conn->data; 554 555 /* Calculate the FROM parameter */ 556 if(!data->set.str[STRING_MAIL_FROM]) 557 /* Null reverse-path, RFC-5321, sect. 3.6.3 */ 558 from = strdup("<>"); 559 else if(data->set.str[STRING_MAIL_FROM][0] == '<') 560 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]); 561 else 562 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]); 563 564 if(!from) 565 return CURLE_OUT_OF_MEMORY; 566 567 /* Calculate the optional AUTH parameter */ 568 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) { 569 if(data->set.str[STRING_MAIL_AUTH][0] != '\0') 570 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]); 571 else 572 /* Empty AUTH, RFC-2554, sect. 5 */ 573 auth = strdup("<>"); 574 575 if(!auth) { 576 free(from); 577 578 return CURLE_OUT_OF_MEMORY; 579 } 580 } 581 582 /* Calculate the optional SIZE parameter */ 583 if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) { 584 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); 585 586 if(!size) { 587 free(from); 588 free(auth); 589 590 return CURLE_OUT_OF_MEMORY; 591 } 592 } 593 594 /* Send the MAIL command */ 595 if(!auth && !size) 596 result = Curl_pp_sendf(&conn->proto.smtpc.pp, 597 "MAIL FROM:%s", from); 598 else if(auth && !size) 599 result = Curl_pp_sendf(&conn->proto.smtpc.pp, 600 "MAIL FROM:%s AUTH=%s", from, auth); 601 else if(auth && size) 602 result = Curl_pp_sendf(&conn->proto.smtpc.pp, 603 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size); 604 else 605 result = Curl_pp_sendf(&conn->proto.smtpc.pp, 606 "MAIL FROM:%s SIZE=%s", from, size); 607 608 free(from); 609 free(auth); 610 free(size); 611 612 if(!result) 613 state(conn, SMTP_MAIL); 614 615 return result; 616 } 617 618 /*********************************************************************** 619 * 620 * smtp_perform_rcpt_to() 621 * 622 * Sends a RCPT TO command for a given recipient as part of the message upload 623 * process. 624 */ 625 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn) 626 { 627 CURLcode result = CURLE_OK; 628 struct Curl_easy *data = conn->data; 629 struct SMTP *smtp = data->req.protop; 630 631 /* Send the RCPT TO command */ 632 if(smtp->rcpt->data[0] == '<') 633 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s", 634 smtp->rcpt->data); 635 else 636 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", 637 smtp->rcpt->data); 638 if(!result) 639 state(conn, SMTP_RCPT); 640 641 return result; 642 } 643 644 /*********************************************************************** 645 * 646 * smtp_perform_quit() 647 * 648 * Performs the quit action prior to sclose() being called. 649 */ 650 static CURLcode smtp_perform_quit(struct connectdata *conn) 651 { 652 CURLcode result = CURLE_OK; 653 654 /* Send the QUIT command */ 655 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT"); 656 657 if(!result) 658 state(conn, SMTP_QUIT); 659 660 return result; 661 } 662 663 /* For the initial server greeting */ 664 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn, 665 int smtpcode, 666 smtpstate instate) 667 { 668 CURLcode result = CURLE_OK; 669 struct Curl_easy *data = conn->data; 670 671 (void)instate; /* no use for this yet */ 672 673 if(smtpcode/100 != 2) { 674 failf(data, "Got unexpected smtp-server response: %d", smtpcode); 675 result = CURLE_WEIRD_SERVER_REPLY; 676 } 677 else 678 result = smtp_perform_ehlo(conn); 679 680 return result; 681 } 682 683 /* For STARTTLS responses */ 684 static CURLcode smtp_state_starttls_resp(struct connectdata *conn, 685 int smtpcode, 686 smtpstate instate) 687 { 688 CURLcode result = CURLE_OK; 689 struct Curl_easy *data = conn->data; 690 691 (void)instate; /* no use for this yet */ 692 693 if(smtpcode != 220) { 694 if(data->set.use_ssl != CURLUSESSL_TRY) { 695 failf(data, "STARTTLS denied. %c", smtpcode); 696 result = CURLE_USE_SSL_FAILED; 697 } 698 else 699 result = smtp_perform_authentication(conn); 700 } 701 else 702 result = smtp_perform_upgrade_tls(conn); 703 704 return result; 705 } 706 707 /* For EHLO responses */ 708 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, 709 smtpstate instate) 710 { 711 CURLcode result = CURLE_OK; 712 struct Curl_easy *data = conn->data; 713 struct smtp_conn *smtpc = &conn->proto.smtpc; 714 const char *line = data->state.buffer; 715 size_t len = strlen(line); 716 size_t wordlen; 717 718 (void)instate; /* no use for this yet */ 719 720 if(smtpcode/100 != 2 && smtpcode != 1) { 721 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) 722 result = smtp_perform_helo(conn); 723 else { 724 failf(data, "Remote access denied: %d", smtpcode); 725 result = CURLE_REMOTE_ACCESS_DENIED; 726 } 727 } 728 else { 729 line += 4; 730 len -= 4; 731 732 /* Does the server support the STARTTLS capability? */ 733 if(len >= 8 && !memcmp(line, "STARTTLS", 8)) 734 smtpc->tls_supported = TRUE; 735 736 /* Does the server support the SIZE capability? */ 737 else if(len >= 4 && !memcmp(line, "SIZE", 4)) 738 smtpc->size_supported = TRUE; 739 740 /* Does the server support authentication? */ 741 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { 742 smtpc->auth_supported = TRUE; 743 744 /* Advance past the AUTH keyword */ 745 line += 5; 746 len -= 5; 747 748 /* Loop through the data line */ 749 for(;;) { 750 size_t llen; 751 unsigned int mechbit; 752 753 while(len && 754 (*line == ' ' || *line == '\t' || 755 *line == '\r' || *line == '\n')) { 756 757 line++; 758 len--; 759 } 760 761 if(!len) 762 break; 763 764 /* Extract the word */ 765 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && 766 line[wordlen] != '\t' && line[wordlen] != '\r' && 767 line[wordlen] != '\n';) 768 wordlen++; 769 770 /* Test the word for a matching authentication mechanism */ 771 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); 772 if(mechbit && llen == wordlen) 773 smtpc->sasl.authmechs |= mechbit; 774 775 line += wordlen; 776 len -= wordlen; 777 } 778 } 779 780 if(smtpcode != 1) { 781 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { 782 /* We don't have a SSL/TLS connection yet, but SSL is requested */ 783 if(smtpc->tls_supported) 784 /* Switch to TLS connection now */ 785 result = smtp_perform_starttls(conn); 786 else if(data->set.use_ssl == CURLUSESSL_TRY) 787 /* Fallback and carry on with authentication */ 788 result = smtp_perform_authentication(conn); 789 else { 790 failf(data, "STARTTLS not supported."); 791 result = CURLE_USE_SSL_FAILED; 792 } 793 } 794 else 795 result = smtp_perform_authentication(conn); 796 } 797 } 798 799 return result; 800 } 801 802 /* For HELO responses */ 803 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode, 804 smtpstate instate) 805 { 806 CURLcode result = CURLE_OK; 807 struct Curl_easy *data = conn->data; 808 809 (void)instate; /* no use for this yet */ 810 811 if(smtpcode/100 != 2) { 812 failf(data, "Remote access denied: %d", smtpcode); 813 result = CURLE_REMOTE_ACCESS_DENIED; 814 } 815 else 816 /* End of connect phase */ 817 state(conn, SMTP_STOP); 818 819 return result; 820 } 821 822 /* For SASL authentication responses */ 823 static CURLcode smtp_state_auth_resp(struct connectdata *conn, 824 int smtpcode, 825 smtpstate instate) 826 { 827 CURLcode result = CURLE_OK; 828 struct Curl_easy *data = conn->data; 829 struct smtp_conn *smtpc = &conn->proto.smtpc; 830 saslprogress progress; 831 832 (void)instate; /* no use for this yet */ 833 834 result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress); 835 if(!result) 836 switch(progress) { 837 case SASL_DONE: 838 state(conn, SMTP_STOP); /* Authenticated */ 839 break; 840 case SASL_IDLE: /* No mechanism left after cancellation */ 841 failf(data, "Authentication cancelled"); 842 result = CURLE_LOGIN_DENIED; 843 break; 844 default: 845 break; 846 } 847 848 return result; 849 } 850 851 /* For command responses */ 852 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode, 853 smtpstate instate) 854 { 855 CURLcode result = CURLE_OK; 856 struct Curl_easy *data = conn->data; 857 struct SMTP *smtp = data->req.protop; 858 char *line = data->state.buffer; 859 size_t len = strlen(line); 860 861 (void)instate; /* no use for this yet */ 862 863 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) || 864 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) { 865 failf(data, "Command failed: %d", smtpcode); 866 result = CURLE_RECV_ERROR; 867 } 868 else { 869 /* Temporarily add the LF character back and send as body to the client */ 870 if(!data->set.opt_no_body) { 871 line[len] = '\n'; 872 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1); 873 line[len] = '\0'; 874 } 875 876 if(smtpcode != 1) { 877 if(smtp->rcpt) { 878 smtp->rcpt = smtp->rcpt->next; 879 880 if(smtp->rcpt) { 881 /* Send the next command */ 882 result = smtp_perform_command(conn); 883 } 884 else 885 /* End of DO phase */ 886 state(conn, SMTP_STOP); 887 } 888 else 889 /* End of DO phase */ 890 state(conn, SMTP_STOP); 891 } 892 } 893 894 return result; 895 } 896 897 /* For MAIL responses */ 898 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode, 899 smtpstate instate) 900 { 901 CURLcode result = CURLE_OK; 902 struct Curl_easy *data = conn->data; 903 904 (void)instate; /* no use for this yet */ 905 906 if(smtpcode/100 != 2) { 907 failf(data, "MAIL failed: %d", smtpcode); 908 result = CURLE_SEND_ERROR; 909 } 910 else 911 /* Start the RCPT TO command */ 912 result = smtp_perform_rcpt_to(conn); 913 914 return result; 915 } 916 917 /* For RCPT responses */ 918 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, 919 smtpstate instate) 920 { 921 CURLcode result = CURLE_OK; 922 struct Curl_easy *data = conn->data; 923 struct SMTP *smtp = data->req.protop; 924 925 (void)instate; /* no use for this yet */ 926 927 if(smtpcode/100 != 2) { 928 failf(data, "RCPT failed: %d", smtpcode); 929 result = CURLE_SEND_ERROR; 930 } 931 else { 932 smtp->rcpt = smtp->rcpt->next; 933 934 if(smtp->rcpt) 935 /* Send the next RCPT TO command */ 936 result = smtp_perform_rcpt_to(conn); 937 else { 938 /* Send the DATA command */ 939 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA"); 940 941 if(!result) 942 state(conn, SMTP_DATA); 943 } 944 } 945 946 return result; 947 } 948 949 /* For DATA response */ 950 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode, 951 smtpstate instate) 952 { 953 CURLcode result = CURLE_OK; 954 struct Curl_easy *data = conn->data; 955 956 (void)instate; /* no use for this yet */ 957 958 if(smtpcode != 354) { 959 failf(data, "DATA failed: %d", smtpcode); 960 result = CURLE_SEND_ERROR; 961 } 962 else { 963 /* Set the progress upload size */ 964 Curl_pgrsSetUploadSize(data, data->state.infilesize); 965 966 /* SMTP upload */ 967 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); 968 969 /* End of DO phase */ 970 state(conn, SMTP_STOP); 971 } 972 973 return result; 974 } 975 976 /* For POSTDATA responses, which are received after the entire DATA 977 part has been sent to the server */ 978 static CURLcode smtp_state_postdata_resp(struct connectdata *conn, 979 int smtpcode, 980 smtpstate instate) 981 { 982 CURLcode result = CURLE_OK; 983 984 (void)instate; /* no use for this yet */ 985 986 if(smtpcode != 250) 987 result = CURLE_RECV_ERROR; 988 989 /* End of DONE phase */ 990 state(conn, SMTP_STOP); 991 992 return result; 993 } 994 995 static CURLcode smtp_statemach_act(struct connectdata *conn) 996 { 997 CURLcode result = CURLE_OK; 998 curl_socket_t sock = conn->sock[FIRSTSOCKET]; 999 struct Curl_easy *data = conn->data; 1000 int smtpcode; 1001 struct smtp_conn *smtpc = &conn->proto.smtpc; 1002 struct pingpong *pp = &smtpc->pp; 1003 size_t nread = 0; 1004 1005 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ 1006 if(smtpc->state == SMTP_UPGRADETLS) 1007 return smtp_perform_upgrade_tls(conn); 1008 1009 /* Flush any data that needs to be sent */ 1010 if(pp->sendleft) 1011 return Curl_pp_flushsend(pp); 1012 1013 do { 1014 /* Read the response from the server */ 1015 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread); 1016 if(result) 1017 return result; 1018 1019 /* Store the latest response for later retrieval if necessary */ 1020 if(smtpc->state != SMTP_QUIT && smtpcode != 1) 1021 data->info.httpcode = smtpcode; 1022 1023 if(!smtpcode) 1024 break; 1025 1026 /* We have now received a full SMTP server response */ 1027 switch(smtpc->state) { 1028 case SMTP_SERVERGREET: 1029 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state); 1030 break; 1031 1032 case SMTP_EHLO: 1033 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state); 1034 break; 1035 1036 case SMTP_HELO: 1037 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state); 1038 break; 1039 1040 case SMTP_STARTTLS: 1041 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state); 1042 break; 1043 1044 case SMTP_AUTH: 1045 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state); 1046 break; 1047 1048 case SMTP_COMMAND: 1049 result = smtp_state_command_resp(conn, smtpcode, smtpc->state); 1050 break; 1051 1052 case SMTP_MAIL: 1053 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state); 1054 break; 1055 1056 case SMTP_RCPT: 1057 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state); 1058 break; 1059 1060 case SMTP_DATA: 1061 result = smtp_state_data_resp(conn, smtpcode, smtpc->state); 1062 break; 1063 1064 case SMTP_POSTDATA: 1065 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state); 1066 break; 1067 1068 case SMTP_QUIT: 1069 /* fallthrough, just stop! */ 1070 default: 1071 /* internal error */ 1072 state(conn, SMTP_STOP); 1073 break; 1074 } 1075 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp)); 1076 1077 return result; 1078 } 1079 1080 /* Called repeatedly until done from multi.c */ 1081 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done) 1082 { 1083 CURLcode result = CURLE_OK; 1084 struct smtp_conn *smtpc = &conn->proto.smtpc; 1085 1086 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { 1087 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); 1088 if(result || !smtpc->ssldone) 1089 return result; 1090 } 1091 1092 result = Curl_pp_statemach(&smtpc->pp, FALSE); 1093 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE; 1094 1095 return result; 1096 } 1097 1098 static CURLcode smtp_block_statemach(struct connectdata *conn) 1099 { 1100 CURLcode result = CURLE_OK; 1101 struct smtp_conn *smtpc = &conn->proto.smtpc; 1102 1103 while(smtpc->state != SMTP_STOP && !result) 1104 result = Curl_pp_statemach(&smtpc->pp, TRUE); 1105 1106 return result; 1107 } 1108 1109 /* Allocate and initialize the SMTP struct for the current Curl_easy if 1110 required */ 1111 static CURLcode smtp_init(struct connectdata *conn) 1112 { 1113 CURLcode result = CURLE_OK; 1114 struct Curl_easy *data = conn->data; 1115 struct SMTP *smtp; 1116 1117 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1); 1118 if(!smtp) 1119 result = CURLE_OUT_OF_MEMORY; 1120 1121 return result; 1122 } 1123 1124 /* For the SMTP "protocol connect" and "doing" phases only */ 1125 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, 1126 int numsocks) 1127 { 1128 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks); 1129 } 1130 1131 /*********************************************************************** 1132 * 1133 * smtp_connect() 1134 * 1135 * This function should do everything that is to be considered a part of 1136 * the connection phase. 1137 * 1138 * The variable pointed to by 'done' will be TRUE if the protocol-layer 1139 * connect phase is done when this function returns, or FALSE if not. 1140 */ 1141 static CURLcode smtp_connect(struct connectdata *conn, bool *done) 1142 { 1143 CURLcode result = CURLE_OK; 1144 struct smtp_conn *smtpc = &conn->proto.smtpc; 1145 struct pingpong *pp = &smtpc->pp; 1146 1147 *done = FALSE; /* default to not done yet */ 1148 1149 /* We always support persistent connections in SMTP */ 1150 connkeep(conn, "SMTP default"); 1151 1152 /* Set the default response time-out */ 1153 pp->response_time = RESP_TIMEOUT; 1154 pp->statemach_act = smtp_statemach_act; 1155 pp->endofresp = smtp_endofresp; 1156 pp->conn = conn; 1157 1158 /* Initialize the SASL storage */ 1159 Curl_sasl_init(&smtpc->sasl, &saslsmtp); 1160 1161 /* Initialise the pingpong layer */ 1162 Curl_pp_init(pp); 1163 1164 /* Parse the URL options */ 1165 result = smtp_parse_url_options(conn); 1166 if(result) 1167 return result; 1168 1169 /* Parse the URL path */ 1170 result = smtp_parse_url_path(conn); 1171 if(result) 1172 return result; 1173 1174 /* Start off waiting for the server greeting response */ 1175 state(conn, SMTP_SERVERGREET); 1176 1177 result = smtp_multi_statemach(conn, done); 1178 1179 return result; 1180 } 1181 1182 /*********************************************************************** 1183 * 1184 * smtp_done() 1185 * 1186 * The DONE function. This does what needs to be done after a single DO has 1187 * performed. 1188 * 1189 * Input argument is already checked for validity. 1190 */ 1191 static CURLcode smtp_done(struct connectdata *conn, CURLcode status, 1192 bool premature) 1193 { 1194 CURLcode result = CURLE_OK; 1195 struct Curl_easy *data = conn->data; 1196 struct SMTP *smtp = data->req.protop; 1197 struct pingpong *pp = &conn->proto.smtpc.pp; 1198 char *eob; 1199 ssize_t len; 1200 ssize_t bytes_written; 1201 1202 (void)premature; 1203 1204 if(!smtp || !pp->conn) 1205 return CURLE_OK; 1206 1207 if(status) { 1208 connclose(conn, "SMTP done with bad status"); /* marked for closure */ 1209 result = status; /* use the already set error code */ 1210 } 1211 else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) { 1212 /* Calculate the EOB taking into account any terminating CRLF from the 1213 previous line of the email or the CRLF of the DATA command when there 1214 is "no mail data". RFC-5321, sect. 4.1.1.4. 1215 1216 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to 1217 fail when using a different pointer following a previous write, that 1218 returned CURLE_AGAIN, we duplicate the EOB now rather than when the 1219 bytes written doesn't equal len. */ 1220 if(smtp->trailing_crlf || !conn->data->state.infilesize) { 1221 eob = strdup(SMTP_EOB + 2); 1222 len = SMTP_EOB_LEN - 2; 1223 } 1224 else { 1225 eob = strdup(SMTP_EOB); 1226 len = SMTP_EOB_LEN; 1227 } 1228 1229 if(!eob) 1230 return CURLE_OUT_OF_MEMORY; 1231 1232 /* Send the end of block data */ 1233 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written); 1234 if(result) { 1235 free(eob); 1236 return result; 1237 } 1238 1239 if(bytes_written != len) { 1240 /* The whole chunk was not sent so keep it around and adjust the 1241 pingpong structure accordingly */ 1242 pp->sendthis = eob; 1243 pp->sendsize = len; 1244 pp->sendleft = len - bytes_written; 1245 } 1246 else { 1247 /* Successfully sent so adjust the response timeout relative to now */ 1248 pp->response = Curl_tvnow(); 1249 1250 free(eob); 1251 } 1252 1253 state(conn, SMTP_POSTDATA); 1254 1255 /* Run the state-machine 1256 1257 TODO: when the multi interface is used, this _really_ should be using 1258 the smtp_multi_statemach function but we have no general support for 1259 non-blocking DONE operations! 1260 */ 1261 result = smtp_block_statemach(conn); 1262 } 1263 1264 /* Cleanup our per-request based variables */ 1265 Curl_safefree(smtp->custom); 1266 1267 /* Clear the transfer mode for the next request */ 1268 smtp->transfer = FTPTRANSFER_BODY; 1269 1270 return result; 1271 } 1272 1273 /*********************************************************************** 1274 * 1275 * smtp_perform() 1276 * 1277 * This is the actual DO function for SMTP. Transfer a mail, send a command 1278 * or get some data according to the options previously setup. 1279 */ 1280 static CURLcode smtp_perform(struct connectdata *conn, bool *connected, 1281 bool *dophase_done) 1282 { 1283 /* This is SMTP and no proxy */ 1284 CURLcode result = CURLE_OK; 1285 struct Curl_easy *data = conn->data; 1286 struct SMTP *smtp = data->req.protop; 1287 1288 DEBUGF(infof(conn->data, "DO phase starts\n")); 1289 1290 if(data->set.opt_no_body) { 1291 /* Requested no body means no transfer */ 1292 smtp->transfer = FTPTRANSFER_INFO; 1293 } 1294 1295 *dophase_done = FALSE; /* not done yet */ 1296 1297 /* Store the first recipient (or NULL if not specified) */ 1298 smtp->rcpt = data->set.mail_rcpt; 1299 1300 /* Start the first command in the DO phase */ 1301 if(data->set.upload && data->set.mail_rcpt) 1302 /* MAIL transfer */ 1303 result = smtp_perform_mail(conn); 1304 else 1305 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */ 1306 result = smtp_perform_command(conn); 1307 1308 if(result) 1309 return result; 1310 1311 /* Run the state-machine */ 1312 result = smtp_multi_statemach(conn, dophase_done); 1313 1314 *connected = conn->bits.tcpconnect[FIRSTSOCKET]; 1315 1316 if(*dophase_done) 1317 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1318 1319 return result; 1320 } 1321 1322 /*********************************************************************** 1323 * 1324 * smtp_do() 1325 * 1326 * This function is registered as 'curl_do' function. It decodes the path 1327 * parts etc as a wrapper to the actual DO function (smtp_perform). 1328 * 1329 * The input argument is already checked for validity. 1330 */ 1331 static CURLcode smtp_do(struct connectdata *conn, bool *done) 1332 { 1333 CURLcode result = CURLE_OK; 1334 1335 *done = FALSE; /* default to false */ 1336 1337 /* Parse the custom request */ 1338 result = smtp_parse_custom_request(conn); 1339 if(result) 1340 return result; 1341 1342 result = smtp_regular_transfer(conn, done); 1343 1344 return result; 1345 } 1346 1347 /*********************************************************************** 1348 * 1349 * smtp_disconnect() 1350 * 1351 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection 1352 * resources. BLOCKING. 1353 */ 1354 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection) 1355 { 1356 struct smtp_conn *smtpc = &conn->proto.smtpc; 1357 1358 /* We cannot send quit unconditionally. If this connection is stale or 1359 bad in any way, sending quit and waiting around here will make the 1360 disconnect wait in vain and cause more problems than we need to. */ 1361 1362 /* The SMTP session may or may not have been allocated/setup at this 1363 point! */ 1364 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart) 1365 if(!smtp_perform_quit(conn)) 1366 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */ 1367 1368 /* Disconnect from the server */ 1369 Curl_pp_disconnect(&smtpc->pp); 1370 1371 /* Cleanup the SASL module */ 1372 Curl_sasl_cleanup(conn, smtpc->sasl.authused); 1373 1374 /* Cleanup our connection based variables */ 1375 Curl_safefree(smtpc->domain); 1376 1377 return CURLE_OK; 1378 } 1379 1380 /* Call this when the DO phase has completed */ 1381 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected) 1382 { 1383 struct SMTP *smtp = conn->data->req.protop; 1384 1385 (void)connected; 1386 1387 if(smtp->transfer != FTPTRANSFER_BODY) 1388 /* no data to transfer */ 1389 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); 1390 1391 return CURLE_OK; 1392 } 1393 1394 /* Called from multi.c while DOing */ 1395 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done) 1396 { 1397 CURLcode result = smtp_multi_statemach(conn, dophase_done); 1398 1399 if(result) 1400 DEBUGF(infof(conn->data, "DO phase failed\n")); 1401 else if(*dophase_done) { 1402 result = smtp_dophase_done(conn, FALSE /* not connected */); 1403 1404 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1405 } 1406 1407 return result; 1408 } 1409 1410 /*********************************************************************** 1411 * 1412 * smtp_regular_transfer() 1413 * 1414 * The input argument is already checked for validity. 1415 * 1416 * Performs all commands done before a regular transfer between a local and a 1417 * remote host. 1418 */ 1419 static CURLcode smtp_regular_transfer(struct connectdata *conn, 1420 bool *dophase_done) 1421 { 1422 CURLcode result = CURLE_OK; 1423 bool connected = FALSE; 1424 struct Curl_easy *data = conn->data; 1425 1426 /* Make sure size is unknown at this point */ 1427 data->req.size = -1; 1428 1429 /* Set the progress data */ 1430 Curl_pgrsSetUploadCounter(data, 0); 1431 Curl_pgrsSetDownloadCounter(data, 0); 1432 Curl_pgrsSetUploadSize(data, -1); 1433 Curl_pgrsSetDownloadSize(data, -1); 1434 1435 /* Carry out the perform */ 1436 result = smtp_perform(conn, &connected, dophase_done); 1437 1438 /* Perform post DO phase operations if necessary */ 1439 if(!result && *dophase_done) 1440 result = smtp_dophase_done(conn, connected); 1441 1442 return result; 1443 } 1444 1445 static CURLcode smtp_setup_connection(struct connectdata *conn) 1446 { 1447 struct Curl_easy *data = conn->data; 1448 CURLcode result; 1449 1450 /* Clear the TLS upgraded flag */ 1451 conn->tls_upgraded = FALSE; 1452 1453 /* Set up the proxy if necessary */ 1454 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { 1455 /* Unless we have asked to tunnel SMTP operations through the proxy, we 1456 switch and use HTTP operations only */ 1457 #ifndef CURL_DISABLE_HTTP 1458 if(conn->handler == &Curl_handler_smtp) 1459 conn->handler = &Curl_handler_smtp_proxy; 1460 else { 1461 #ifdef USE_SSL 1462 conn->handler = &Curl_handler_smtps_proxy; 1463 #else 1464 failf(data, "SMTPS not supported!"); 1465 return CURLE_UNSUPPORTED_PROTOCOL; 1466 #endif 1467 } 1468 /* set it up as a HTTP connection instead */ 1469 return conn->handler->setup_connection(conn); 1470 1471 #else 1472 failf(data, "SMTP over http proxy requires HTTP support built-in!"); 1473 return CURLE_UNSUPPORTED_PROTOCOL; 1474 #endif 1475 } 1476 1477 /* Initialise the SMTP layer */ 1478 result = smtp_init(conn); 1479 if(result) 1480 return result; 1481 1482 data->state.path++; /* don't include the initial slash */ 1483 1484 return CURLE_OK; 1485 } 1486 1487 /*********************************************************************** 1488 * 1489 * smtp_parse_url_options() 1490 * 1491 * Parse the URL login options. 1492 */ 1493 static CURLcode smtp_parse_url_options(struct connectdata *conn) 1494 { 1495 CURLcode result = CURLE_OK; 1496 struct smtp_conn *smtpc = &conn->proto.smtpc; 1497 const char *ptr = conn->options; 1498 1499 smtpc->sasl.resetprefs = TRUE; 1500 1501 while(!result && ptr && *ptr) { 1502 const char *key = ptr; 1503 const char *value; 1504 1505 while(*ptr && *ptr != '=') 1506 ptr++; 1507 1508 value = ptr + 1; 1509 1510 while(*ptr && *ptr != ';') 1511 ptr++; 1512 1513 if(strncasecompare(key, "AUTH=", 5)) 1514 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, 1515 value, ptr - value); 1516 else 1517 result = CURLE_URL_MALFORMAT; 1518 1519 if(*ptr == ';') 1520 ptr++; 1521 } 1522 1523 return result; 1524 } 1525 1526 /*********************************************************************** 1527 * 1528 * smtp_parse_url_path() 1529 * 1530 * Parse the URL path into separate path components. 1531 */ 1532 static CURLcode smtp_parse_url_path(struct connectdata *conn) 1533 { 1534 /* The SMTP struct is already initialised in smtp_connect() */ 1535 struct Curl_easy *data = conn->data; 1536 struct smtp_conn *smtpc = &conn->proto.smtpc; 1537 const char *path = data->state.path; 1538 char localhost[HOSTNAME_MAX + 1]; 1539 1540 /* Calculate the path if necessary */ 1541 if(!*path) { 1542 if(!Curl_gethostname(localhost, sizeof(localhost))) 1543 path = localhost; 1544 else 1545 path = "localhost"; 1546 } 1547 1548 /* URL decode the path and use it as the domain in our EHLO */ 1549 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE); 1550 } 1551 1552 /*********************************************************************** 1553 * 1554 * smtp_parse_custom_request() 1555 * 1556 * Parse the custom request. 1557 */ 1558 static CURLcode smtp_parse_custom_request(struct connectdata *conn) 1559 { 1560 CURLcode result = CURLE_OK; 1561 struct Curl_easy *data = conn->data; 1562 struct SMTP *smtp = data->req.protop; 1563 const char *custom = data->set.str[STRING_CUSTOMREQUEST]; 1564 1565 /* URL decode the custom request */ 1566 if(custom) 1567 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE); 1568 1569 return result; 1570 } 1571 1572 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread) 1573 { 1574 /* When sending a SMTP payload we must detect CRLF. sequences making sure 1575 they are sent as CRLF.. instead, as a . on the beginning of a line will 1576 be deleted by the server when not part of an EOB terminator and a 1577 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of 1578 data by the server 1579 */ 1580 ssize_t i; 1581 ssize_t si; 1582 struct Curl_easy *data = conn->data; 1583 struct SMTP *smtp = data->req.protop; 1584 char *scratch = data->state.scratch; 1585 char *newscratch = NULL; 1586 char *oldscratch = NULL; 1587 size_t eob_sent; 1588 1589 /* Do we need to allocate a scratch buffer? */ 1590 if(!scratch || data->set.crlf) { 1591 oldscratch = scratch; 1592 1593 scratch = newscratch = malloc(2 * BUFSIZE); 1594 if(!newscratch) { 1595 failf(data, "Failed to alloc scratch buffer!"); 1596 1597 return CURLE_OUT_OF_MEMORY; 1598 } 1599 } 1600 1601 /* Have we already sent part of the EOB? */ 1602 eob_sent = smtp->eob; 1603 1604 /* This loop can be improved by some kind of Boyer-Moore style of 1605 approach but that is saved for later... */ 1606 for(i = 0, si = 0; i < nread; i++) { 1607 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) { 1608 smtp->eob++; 1609 1610 /* Is the EOB potentially the terminating CRLF? */ 1611 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob) 1612 smtp->trailing_crlf = TRUE; 1613 else 1614 smtp->trailing_crlf = FALSE; 1615 } 1616 else if(smtp->eob) { 1617 /* A previous substring matched so output that first */ 1618 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); 1619 si += smtp->eob - eob_sent; 1620 1621 /* Then compare the first byte */ 1622 if(SMTP_EOB[0] == data->req.upload_fromhere[i]) 1623 smtp->eob = 1; 1624 else 1625 smtp->eob = 0; 1626 1627 eob_sent = 0; 1628 1629 /* Reset the trailing CRLF flag as there was more data */ 1630 smtp->trailing_crlf = FALSE; 1631 } 1632 1633 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */ 1634 if(SMTP_EOB_FIND_LEN == smtp->eob) { 1635 /* Copy the replacement data to the target buffer */ 1636 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent], 1637 SMTP_EOB_REPL_LEN - eob_sent); 1638 si += SMTP_EOB_REPL_LEN - eob_sent; 1639 smtp->eob = 0; 1640 eob_sent = 0; 1641 } 1642 else if(!smtp->eob) 1643 scratch[si++] = data->req.upload_fromhere[i]; 1644 } 1645 1646 if(smtp->eob - eob_sent) { 1647 /* A substring matched before processing ended so output that now */ 1648 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); 1649 si += smtp->eob - eob_sent; 1650 } 1651 1652 /* Only use the new buffer if we replaced something */ 1653 if(si != nread) { 1654 /* Upload from the new (replaced) buffer instead */ 1655 data->req.upload_fromhere = scratch; 1656 1657 /* Save the buffer so it can be freed later */ 1658 data->state.scratch = scratch; 1659 1660 /* Free the old scratch buffer */ 1661 free(oldscratch); 1662 1663 /* Set the new amount too */ 1664 data->req.upload_present = si; 1665 } 1666 else 1667 free(newscratch); 1668 1669 return CURLE_OK; 1670 } 1671 1672 #endif /* CURL_DISABLE_SMTP */ 1673