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