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 * RFC6749 OAuth 2.0 Authorization Framework 30 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt> 31 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> 32 * 33 ***************************************************************************/ 34 35#include "curl_setup.h" 36 37#ifndef CURL_DISABLE_SMTP 38 39#ifdef HAVE_NETINET_IN_H 40#include <netinet/in.h> 41#endif 42#ifdef HAVE_ARPA_INET_H 43#include <arpa/inet.h> 44#endif 45#ifdef HAVE_UTSNAME_H 46#include <sys/utsname.h> 47#endif 48#ifdef HAVE_NETDB_H 49#include <netdb.h> 50#endif 51#ifdef __VMS 52#include <in.h> 53#include <inet.h> 54#endif 55 56#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) 57#undef in_addr_t 58#define in_addr_t unsigned long 59#endif 60 61#include <curl/curl.h> 62#include "urldata.h" 63#include "sendf.h" 64#include "if2ip.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 73#include "strtoofft.h" 74#include "strequal.h" 75#include "vtls/vtls.h" 76#include "connect.h" 77#include "strerror.h" 78#include "select.h" 79#include "multiif.h" 80#include "url.h" 81#include "rawstr.h" 82#include "curl_gethostname.h" 83#include "curl_sasl.h" 84#include "warnless.h" 85 86#define _MPRINTF_REPLACE /* use our functions only */ 87#include <curl/mprintf.h> 88 89#include "curl_memory.h" 90/* The last #include file should be: */ 91#include "memdebug.h" 92 93/* Local API functions */ 94static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done); 95static CURLcode smtp_do(struct connectdata *conn, bool *done); 96static CURLcode smtp_done(struct connectdata *conn, CURLcode status, 97 bool premature); 98static CURLcode smtp_connect(struct connectdata *conn, bool *done); 99static CURLcode smtp_disconnect(struct connectdata *conn, bool dead); 100static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done); 101static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, 102 int numsocks); 103static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done); 104static CURLcode smtp_setup_connection(struct connectdata *conn); 105static CURLcode smtp_parse_url_options(struct connectdata *conn); 106static CURLcode smtp_parse_url_path(struct connectdata *conn); 107static CURLcode smtp_parse_custom_request(struct connectdata *conn); 108static CURLcode smtp_calc_sasl_details(struct connectdata *conn, 109 const char **mech, 110 char **initresp, size_t *len, 111 smtpstate *state1, smtpstate *state2); 112 113/* 114 * SMTP protocol handler. 115 */ 116 117const struct Curl_handler Curl_handler_smtp = { 118 "SMTP", /* scheme */ 119 smtp_setup_connection, /* setup_connection */ 120 smtp_do, /* do_it */ 121 smtp_done, /* done */ 122 ZERO_NULL, /* do_more */ 123 smtp_connect, /* connect_it */ 124 smtp_multi_statemach, /* connecting */ 125 smtp_doing, /* doing */ 126 smtp_getsock, /* proto_getsock */ 127 smtp_getsock, /* doing_getsock */ 128 ZERO_NULL, /* domore_getsock */ 129 ZERO_NULL, /* perform_getsock */ 130 smtp_disconnect, /* disconnect */ 131 ZERO_NULL, /* readwrite */ 132 PORT_SMTP, /* defport */ 133 CURLPROTO_SMTP, /* protocol */ 134 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */ 135}; 136 137#ifdef USE_SSL 138/* 139 * SMTPS protocol handler. 140 */ 141 142const struct Curl_handler Curl_handler_smtps = { 143 "SMTPS", /* scheme */ 144 smtp_setup_connection, /* setup_connection */ 145 smtp_do, /* do_it */ 146 smtp_done, /* done */ 147 ZERO_NULL, /* do_more */ 148 smtp_connect, /* connect_it */ 149 smtp_multi_statemach, /* connecting */ 150 smtp_doing, /* doing */ 151 smtp_getsock, /* proto_getsock */ 152 smtp_getsock, /* doing_getsock */ 153 ZERO_NULL, /* domore_getsock */ 154 ZERO_NULL, /* perform_getsock */ 155 smtp_disconnect, /* disconnect */ 156 ZERO_NULL, /* readwrite */ 157 PORT_SMTPS, /* defport */ 158 CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */ 159 PROTOPT_CLOSEACTION | PROTOPT_SSL 160 | PROTOPT_NOURLQUERY /* flags */ 161}; 162#endif 163 164#ifndef CURL_DISABLE_HTTP 165/* 166 * HTTP-proxyed SMTP protocol handler. 167 */ 168 169static const struct Curl_handler Curl_handler_smtp_proxy = { 170 "SMTP", /* scheme */ 171 Curl_http_setup_conn, /* setup_connection */ 172 Curl_http, /* do_it */ 173 Curl_http_done, /* done */ 174 ZERO_NULL, /* do_more */ 175 ZERO_NULL, /* connect_it */ 176 ZERO_NULL, /* connecting */ 177 ZERO_NULL, /* doing */ 178 ZERO_NULL, /* proto_getsock */ 179 ZERO_NULL, /* doing_getsock */ 180 ZERO_NULL, /* domore_getsock */ 181 ZERO_NULL, /* perform_getsock */ 182 ZERO_NULL, /* disconnect */ 183 ZERO_NULL, /* readwrite */ 184 PORT_SMTP, /* defport */ 185 CURLPROTO_HTTP, /* protocol */ 186 PROTOPT_NONE /* flags */ 187}; 188 189#ifdef USE_SSL 190/* 191 * HTTP-proxyed SMTPS protocol handler. 192 */ 193 194static const struct Curl_handler Curl_handler_smtps_proxy = { 195 "SMTPS", /* scheme */ 196 Curl_http_setup_conn, /* setup_connection */ 197 Curl_http, /* do_it */ 198 Curl_http_done, /* done */ 199 ZERO_NULL, /* do_more */ 200 ZERO_NULL, /* connect_it */ 201 ZERO_NULL, /* connecting */ 202 ZERO_NULL, /* doing */ 203 ZERO_NULL, /* proto_getsock */ 204 ZERO_NULL, /* doing_getsock */ 205 ZERO_NULL, /* domore_getsock */ 206 ZERO_NULL, /* perform_getsock */ 207 ZERO_NULL, /* disconnect */ 208 ZERO_NULL, /* readwrite */ 209 PORT_SMTPS, /* defport */ 210 CURLPROTO_HTTP, /* protocol */ 211 PROTOPT_NONE /* flags */ 212}; 213#endif 214#endif 215 216#ifdef USE_SSL 217static void smtp_to_smtps(struct connectdata *conn) 218{ 219 conn->handler = &Curl_handler_smtps; 220} 221#else 222#define smtp_to_smtps(x) Curl_nop_stmt 223#endif 224 225/*********************************************************************** 226 * 227 * smtp_endofresp() 228 * 229 * Checks for an ending SMTP status code at the start of the given string, but 230 * also detects various capabilities from the EHLO response including the 231 * supported authentication mechanisms. 232 */ 233static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len, 234 int *resp) 235{ 236 struct smtp_conn *smtpc = &conn->proto.smtpc; 237 bool result = FALSE; 238 239 /* Nothing for us */ 240 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) 241 return FALSE; 242 243 /* Do we have a command response? This should be the response code followed 244 by a space and optionally some text as per RFC-5321 and as outlined in 245 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and 246 only send the response code instead as per Section 4.2. */ 247 if(line[3] == ' ' || len == 5) { 248 result = TRUE; 249 *resp = curlx_sltosi(strtol(line, NULL, 10)); 250 251 /* Make sure real server never sends internal value */ 252 if(*resp == 1) 253 *resp = 0; 254 } 255 /* Do we have a multiline (continuation) response? */ 256 else if(line[3] == '-' && 257 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { 258 result = TRUE; 259 *resp = 1; /* Internal response code */ 260 } 261 262 return result; 263} 264 265/*********************************************************************** 266 * 267 * smtp_get_message() 268 * 269 * Gets the authentication message from the response buffer. 270 */ 271static void smtp_get_message(char *buffer, char** outptr) 272{ 273 size_t len = 0; 274 char* message = NULL; 275 276 /* Find the start of the message */ 277 for(message = buffer + 4; *message == ' ' || *message == '\t'; message++) 278 ; 279 280 /* Find the end of the message */ 281 for(len = strlen(message); len--;) 282 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && 283 message[len] != '\t') 284 break; 285 286 /* Terminate the message */ 287 if(++len) { 288 message[len] = '\0'; 289 } 290 291 *outptr = message; 292} 293 294/*********************************************************************** 295 * 296 * state() 297 * 298 * This is the ONLY way to change SMTP state! 299 */ 300static void state(struct connectdata *conn, smtpstate newstate) 301{ 302 struct smtp_conn *smtpc = &conn->proto.smtpc; 303#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 304 /* for debug purposes */ 305 static const char * const names[] = { 306 "STOP", 307 "SERVERGREET", 308 "EHLO", 309 "HELO", 310 "STARTTLS", 311 "UPGRADETLS", 312 "AUTH_PLAIN", 313 "AUTH_LOGIN", 314 "AUTH_LOGIN_PASSWD", 315 "AUTH_CRAMMD5", 316 "AUTH_DIGESTMD5", 317 "AUTH_DIGESTMD5_RESP", 318 "AUTH_NTLM", 319 "AUTH_NTLM_TYPE2MSG", 320 "AUTH_XOAUTH2", 321 "AUTH_CANCEL", 322 "AUTH_FINAL", 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 */ 347static CURLcode smtp_perform_ehlo(struct connectdata *conn) 348{ 349 CURLcode result = CURLE_OK; 350 struct smtp_conn *smtpc = &conn->proto.smtpc; 351 352 smtpc->authmechs = 0; /* No known authentication mechanisms yet */ 353 smtpc->authused = 0; /* Clear the authentication mechanism used 354 for esmtp connections */ 355 smtpc->tls_supported = FALSE; /* Clear the TLS capability */ 356 357 /* Send the EHLO command */ 358 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain); 359 360 if(!result) 361 state(conn, SMTP_EHLO); 362 363 return result; 364} 365 366/*********************************************************************** 367 * 368 * smtp_perform_helo() 369 * 370 * Sends the HELO command to initialise communication with the SMTP server. 371 */ 372static CURLcode smtp_perform_helo(struct connectdata *conn) 373{ 374 CURLcode result = CURLE_OK; 375 struct smtp_conn *smtpc = &conn->proto.smtpc; 376 377 smtpc->authused = 0; /* No authentication mechanism used in smtp 378 connections */ 379 380 /* Send the HELO command */ 381 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain); 382 383 if(!result) 384 state(conn, SMTP_HELO); 385 386 return result; 387} 388 389/*********************************************************************** 390 * 391 * smtp_perform_starttls() 392 * 393 * Sends the STLS command to start the upgrade to TLS. 394 */ 395static CURLcode smtp_perform_starttls(struct connectdata *conn) 396{ 397 CURLcode result = CURLE_OK; 398 399 /* Send the STARTTLS command */ 400 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS"); 401 402 if(!result) 403 state(conn, SMTP_STARTTLS); 404 405 return result; 406} 407 408/*********************************************************************** 409 * 410 * smtp_perform_upgrade_tls() 411 * 412 * Performs the upgrade to TLS. 413 */ 414static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn) 415{ 416 CURLcode result = CURLE_OK; 417 struct smtp_conn *smtpc = &conn->proto.smtpc; 418 419 /* Start the SSL connection */ 420 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); 421 422 if(!result) { 423 if(smtpc->state != SMTP_UPGRADETLS) 424 state(conn, SMTP_UPGRADETLS); 425 426 if(smtpc->ssldone) { 427 smtp_to_smtps(conn); 428 result = smtp_perform_ehlo(conn); 429 } 430 } 431 432 return result; 433} 434 435/*********************************************************************** 436 * 437 * smtp_perform_auth() 438 * 439 * Sends an AUTH command allowing the client to login with the given SASL 440 * authentication mechanism. 441 */ 442static CURLcode smtp_perform_auth(struct connectdata *conn, 443 const char *mech, 444 const char *initresp, size_t len, 445 smtpstate state1, smtpstate state2) 446{ 447 CURLcode result = CURLE_OK; 448 struct smtp_conn *smtpc = &conn->proto.smtpc; 449 450 if(initresp && 8 + strlen(mech) + len <= 512) { /* 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 if(!result) 455 state(conn, state2); 456 } 457 else { 458 /* Send the AUTH command */ 459 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); 460 461 if(!result) 462 state(conn, state1); 463 } 464 465 return result; 466} 467 468/*********************************************************************** 469 * 470 * smtp_perform_authentication() 471 * 472 * Initiates the authentication sequence, with the appropriate SASL 473 * authentication mechanism. 474 */ 475static CURLcode smtp_perform_authentication(struct connectdata *conn) 476{ 477 CURLcode result = CURLE_OK; 478 const char *mech = NULL; 479 char *initresp = NULL; 480 size_t len = 0; 481 smtpstate state1 = SMTP_STOP; 482 smtpstate state2 = SMTP_STOP; 483 484 /* Check we have a username and password to authenticate with and end the 485 connect phase if we don't */ 486 if(!conn->bits.user_passwd) { 487 state(conn, SMTP_STOP); 488 489 return result; 490 } 491 492 /* Calculate the SASL login details */ 493 result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1, 494 &state2); 495 496 if(!result) { 497 if(mech) { 498 /* Perform SASL based authentication */ 499 result = smtp_perform_auth(conn, mech, initresp, len, state1, state2); 500 501 Curl_safefree(initresp); 502 } 503 else { 504 /* Other mechanisms not supported */ 505 infof(conn->data, "No known authentication mechanisms supported!\n"); 506 result = CURLE_LOGIN_DENIED; 507 } 508 } 509 510 return result; 511} 512 513/*********************************************************************** 514 * 515 * smtp_perform_command() 516 * 517 * Sends a SMTP based command. 518 */ 519static CURLcode smtp_perform_command(struct connectdata *conn) 520{ 521 CURLcode result = CURLE_OK; 522 struct SessionHandle *data = conn->data; 523 struct SMTP *smtp = data->req.protop; 524 525 /* Send the command */ 526 if(smtp->rcpt) 527 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s", 528 smtp->custom && smtp->custom[0] != '\0' ? 529 smtp->custom : "VRFY", 530 smtp->rcpt->data); 531 else 532 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", 533 smtp->custom && smtp->custom[0] != '\0' ? 534 smtp->custom : "HELP"); 535 536 if(!result) 537 state(conn, SMTP_COMMAND); 538 539 return result; 540} 541 542/*********************************************************************** 543 * 544 * smtp_perform_mail() 545 * 546 * Sends an MAIL command to initiate the upload of a message. 547 */ 548static CURLcode smtp_perform_mail(struct connectdata *conn) 549{ 550 char *from = NULL; 551 char *auth = NULL; 552 char *size = NULL; 553 CURLcode result = CURLE_OK; 554 struct SessionHandle *data = conn->data; 555 556 /* Calculate the FROM parameter */ 557 if(!data->set.str[STRING_MAIL_FROM]) 558 /* Null reverse-path, RFC-5321, sect. 3.6.3 */ 559 from = strdup("<>"); 560 else if(data->set.str[STRING_MAIL_FROM][0] == '<') 561 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]); 562 else 563 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]); 564 565 if(!from) 566 return CURLE_OUT_OF_MEMORY; 567 568 /* Calculate the optional AUTH parameter */ 569 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) { 570 if(data->set.str[STRING_MAIL_AUTH][0] != '\0') 571 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]); 572 else 573 /* Empty AUTH, RFC-2554, sect. 5 */ 574 auth = strdup("<>"); 575 576 if(!auth) { 577 Curl_safefree(from); 578 579 return CURLE_OUT_OF_MEMORY; 580 } 581 } 582 583 /* Calculate the optional SIZE parameter */ 584 if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) { 585 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->set.infilesize); 586 587 if(!size) { 588 Curl_safefree(from); 589 Curl_safefree(auth); 590 591 return CURLE_OUT_OF_MEMORY; 592 } 593 } 594 595 /* Send the MAIL command */ 596 if(!auth && !size) 597 result = Curl_pp_sendf(&conn->proto.smtpc.pp, 598 "MAIL FROM:%s", from); 599 else if(auth && !size) 600 result = Curl_pp_sendf(&conn->proto.smtpc.pp, 601 "MAIL FROM:%s AUTH=%s", from, auth); 602 else if(auth && size) 603 result = Curl_pp_sendf(&conn->proto.smtpc.pp, 604 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size); 605 else 606 result = Curl_pp_sendf(&conn->proto.smtpc.pp, 607 "MAIL FROM:%s SIZE=%s", from, size); 608 609 Curl_safefree(from); 610 Curl_safefree(auth); 611 Curl_safefree(size); 612 613 if(!result) 614 state(conn, SMTP_MAIL); 615 616 return result; 617} 618 619/*********************************************************************** 620 * 621 * smtp_perform_rcpt_to() 622 * 623 * Sends a RCPT TO command for a given recipient as part of the message upload 624 * process. 625 */ 626static CURLcode smtp_perform_rcpt_to(struct connectdata *conn) 627{ 628 CURLcode result = CURLE_OK; 629 struct SessionHandle *data = conn->data; 630 struct SMTP *smtp = data->req.protop; 631 632 /* Send the RCPT TO command */ 633 if(smtp->rcpt->data[0] == '<') 634 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s", 635 smtp->rcpt->data); 636 else 637 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", 638 smtp->rcpt->data); 639 if(!result) 640 state(conn, SMTP_RCPT); 641 642 return result; 643} 644 645/*********************************************************************** 646 * 647 * smtp_perform_quit() 648 * 649 * Performs the quit action prior to sclose() being called. 650 */ 651static CURLcode smtp_perform_quit(struct connectdata *conn) 652{ 653 CURLcode result = CURLE_OK; 654 655 /* Send the QUIT command */ 656 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT"); 657 658 if(!result) 659 state(conn, SMTP_QUIT); 660 661 return result; 662} 663 664/* For the initial server greeting */ 665static CURLcode smtp_state_servergreet_resp(struct connectdata *conn, 666 int smtpcode, 667 smtpstate instate) 668{ 669 CURLcode result = CURLE_OK; 670 struct SessionHandle *data = conn->data; 671 672 (void)instate; /* no use for this yet */ 673 674 if(smtpcode/100 != 2) { 675 failf(data, "Got unexpected smtp-server response: %d", smtpcode); 676 result = CURLE_FTP_WEIRD_SERVER_REPLY; 677 } 678 else 679 result = smtp_perform_ehlo(conn); 680 681 return result; 682} 683 684/* For STARTTLS responses */ 685static CURLcode smtp_state_starttls_resp(struct connectdata *conn, 686 int smtpcode, 687 smtpstate instate) 688{ 689 CURLcode result = CURLE_OK; 690 struct SessionHandle *data = conn->data; 691 692 (void)instate; /* no use for this yet */ 693 694 if(smtpcode != 220) { 695 if(data->set.use_ssl != CURLUSESSL_TRY) { 696 failf(data, "STARTTLS denied. %c", smtpcode); 697 result = CURLE_USE_SSL_FAILED; 698 } 699 else 700 result = smtp_perform_authentication(conn); 701 } 702 else 703 result = smtp_perform_upgrade_tls(conn); 704 705 return result; 706} 707 708/* For EHLO responses */ 709static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, 710 smtpstate instate) 711{ 712 CURLcode result = CURLE_OK; 713 struct SessionHandle *data = conn->data; 714 struct smtp_conn *smtpc = &conn->proto.smtpc; 715 const char *line = data->state.buffer; 716 size_t len = strlen(line); 717 size_t wordlen; 718 719 (void)instate; /* no use for this yet */ 720 721 if(smtpcode/100 != 2 && smtpcode != 1) { 722 if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) && 723 !conn->bits.user_passwd) 724 result = smtp_perform_helo(conn); 725 else { 726 failf(data, "Remote access denied: %d", smtpcode); 727 result = CURLE_REMOTE_ACCESS_DENIED; 728 } 729 } 730 else { 731 line += 4; 732 len -= 4; 733 734 /* Does the server support the STARTTLS capability? */ 735 if(len >= 8 && !memcmp(line, "STARTTLS", 8)) 736 smtpc->tls_supported = TRUE; 737 738 /* Does the server support the SIZE capability? */ 739 else if(len >= 4 && !memcmp(line, "SIZE", 4)) 740 smtpc->size_supported = TRUE; 741 742 /* Do we have the authentication mechanism list? */ 743 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { 744 line += 5; 745 len -= 5; 746 747 /* Loop through the data line */ 748 for(;;) { 749 while(len && 750 (*line == ' ' || *line == '\t' || 751 *line == '\r' || *line == '\n')) { 752 753 line++; 754 len--; 755 } 756 757 if(!len) 758 break; 759 760 /* Extract the word */ 761 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && 762 line[wordlen] != '\t' && line[wordlen] != '\r' && 763 line[wordlen] != '\n';) 764 wordlen++; 765 766 /* Test the word for a matching authentication mechanism */ 767 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) 768 smtpc->authmechs |= SASL_MECH_LOGIN; 769 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) 770 smtpc->authmechs |= SASL_MECH_PLAIN; 771 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) 772 smtpc->authmechs |= SASL_MECH_CRAM_MD5; 773 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) 774 smtpc->authmechs |= SASL_MECH_DIGEST_MD5; 775 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) 776 smtpc->authmechs |= SASL_MECH_GSSAPI; 777 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) 778 smtpc->authmechs |= SASL_MECH_EXTERNAL; 779 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) 780 smtpc->authmechs |= SASL_MECH_NTLM; 781 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) 782 smtpc->authmechs |= SASL_MECH_XOAUTH2; 783 784 line += wordlen; 785 len -= wordlen; 786 } 787 } 788 789 if(smtpcode != 1) { 790 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { 791 /* We don't have a SSL/TLS connection yet, but SSL is requested */ 792 if(smtpc->tls_supported) 793 /* Switch to TLS connection now */ 794 result = smtp_perform_starttls(conn); 795 else if(data->set.use_ssl == CURLUSESSL_TRY) 796 /* Fallback and carry on with authentication */ 797 result = smtp_perform_authentication(conn); 798 else { 799 failf(data, "STARTTLS not supported."); 800 result = CURLE_USE_SSL_FAILED; 801 } 802 } 803 else 804 result = smtp_perform_authentication(conn); 805 } 806 } 807 808 return result; 809} 810 811/* For HELO responses */ 812static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode, 813 smtpstate instate) 814{ 815 CURLcode result = CURLE_OK; 816 struct SessionHandle *data = conn->data; 817 818 (void)instate; /* no use for this yet */ 819 820 if(smtpcode/100 != 2) { 821 failf(data, "Remote access denied: %d", smtpcode); 822 result = CURLE_REMOTE_ACCESS_DENIED; 823 } 824 else 825 /* End of connect phase */ 826 state(conn, SMTP_STOP); 827 828 return result; 829} 830 831/* For AUTH PLAIN (without initial response) responses */ 832static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn, 833 int smtpcode, 834 smtpstate instate) 835{ 836 CURLcode result = CURLE_OK; 837 struct SessionHandle *data = conn->data; 838 size_t len = 0; 839 char *plainauth = NULL; 840 841 (void)instate; /* no use for this yet */ 842 843 if(smtpcode != 334) { 844 failf(data, "Access denied: %d", smtpcode); 845 result = CURLE_LOGIN_DENIED; 846 } 847 else { 848 /* Create the authorisation message */ 849 result = Curl_sasl_create_plain_message(conn->data, conn->user, 850 conn->passwd, &plainauth, &len); 851 if(!result && plainauth) { 852 /* Send the message */ 853 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth); 854 855 if(!result) 856 state(conn, SMTP_AUTH_FINAL); 857 } 858 } 859 860 Curl_safefree(plainauth); 861 862 return result; 863} 864 865/* For AUTH LOGIN (without initial response) responses */ 866static CURLcode smtp_state_auth_login_resp(struct connectdata *conn, 867 int smtpcode, 868 smtpstate instate) 869{ 870 CURLcode result = CURLE_OK; 871 struct SessionHandle *data = conn->data; 872 size_t len = 0; 873 char *authuser = NULL; 874 875 (void)instate; /* no use for this yet */ 876 877 if(smtpcode != 334) { 878 failf(data, "Access denied: %d", smtpcode); 879 result = CURLE_LOGIN_DENIED; 880 } 881 else { 882 /* Create the user message */ 883 result = Curl_sasl_create_login_message(conn->data, conn->user, 884 &authuser, &len); 885 if(!result && authuser) { 886 /* Send the user */ 887 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser); 888 889 if(!result) 890 state(conn, SMTP_AUTH_LOGIN_PASSWD); 891 } 892 } 893 894 Curl_safefree(authuser); 895 896 return result; 897} 898 899/* For AUTH LOGIN user entry responses */ 900static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn, 901 int smtpcode, 902 smtpstate instate) 903{ 904 CURLcode result = CURLE_OK; 905 struct SessionHandle *data = conn->data; 906 size_t len = 0; 907 char *authpasswd = NULL; 908 909 (void)instate; /* no use for this yet */ 910 911 if(smtpcode != 334) { 912 failf(data, "Access denied: %d", smtpcode); 913 result = CURLE_LOGIN_DENIED; 914 } 915 else { 916 /* Create the password message */ 917 result = Curl_sasl_create_login_message(conn->data, conn->passwd, 918 &authpasswd, &len); 919 if(!result && authpasswd) { 920 /* Send the password */ 921 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd); 922 923 if(!result) 924 state(conn, SMTP_AUTH_FINAL); 925 } 926 } 927 928 Curl_safefree(authpasswd); 929 930 return result; 931} 932 933#ifndef CURL_DISABLE_CRYPTO_AUTH 934/* For AUTH CRAM-MD5 responses */ 935static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn, 936 int smtpcode, 937 smtpstate instate) 938{ 939 CURLcode result = CURLE_OK; 940 struct SessionHandle *data = conn->data; 941 char *chlg = NULL; 942 char *chlg64 = NULL; 943 char *rplyb64 = NULL; 944 size_t len = 0; 945 946 (void)instate; /* no use for this yet */ 947 948 if(smtpcode != 334) { 949 failf(data, "Access denied: %d", smtpcode); 950 return CURLE_LOGIN_DENIED; 951 } 952 953 /* Get the challenge message */ 954 smtp_get_message(data->state.buffer, &chlg64); 955 956 /* Decode the challenge message */ 957 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len); 958 if(result) { 959 /* Send the cancellation */ 960 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*"); 961 962 if(!result) 963 state(conn, SMTP_AUTH_CANCEL); 964 } 965 else { 966 /* Create the response message */ 967 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user, 968 conn->passwd, &rplyb64, &len); 969 if(!result && rplyb64) { 970 /* Send the response */ 971 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); 972 973 if(!result) 974 state(conn, SMTP_AUTH_FINAL); 975 } 976 } 977 978 Curl_safefree(chlg); 979 Curl_safefree(rplyb64); 980 981 return result; 982} 983 984/* For AUTH DIGEST-MD5 challenge responses */ 985static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn, 986 int smtpcode, 987 smtpstate instate) 988{ 989 CURLcode result = CURLE_OK; 990 struct SessionHandle *data = conn->data; 991 char *chlg64 = NULL; 992 char *rplyb64 = NULL; 993 size_t len = 0; 994 995 char nonce[64]; 996 char realm[128]; 997 char algorithm[64]; 998 999 (void)instate; /* no use for this yet */ 1000 1001 if(smtpcode != 334) { 1002 failf(data, "Access denied: %d", smtpcode); 1003 return CURLE_LOGIN_DENIED; 1004 } 1005 1006 /* Get the challenge message */ 1007 smtp_get_message(data->state.buffer, &chlg64); 1008 1009 /* Decode the challange message */ 1010 result = Curl_sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce), 1011 realm, sizeof(realm), 1012 algorithm, sizeof(algorithm)); 1013 if(result || strcmp(algorithm, "md5-sess") != 0) { 1014 /* Send the cancellation */ 1015 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*"); 1016 1017 if(!result) 1018 state(conn, SMTP_AUTH_CANCEL); 1019 } 1020 else { 1021 /* Create the response message */ 1022 result = Curl_sasl_create_digest_md5_message(data, nonce, realm, 1023 conn->user, conn->passwd, 1024 "smtp", &rplyb64, &len); 1025 if(!result && rplyb64) { 1026 /* Send the response */ 1027 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64); 1028 1029 if(!result) 1030 state(conn, SMTP_AUTH_DIGESTMD5_RESP); 1031 } 1032 } 1033 1034 Curl_safefree(rplyb64); 1035 1036 return result; 1037} 1038 1039/* For AUTH DIGEST-MD5 challenge-response responses */ 1040static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn, 1041 int smtpcode, 1042 smtpstate instate) 1043{ 1044 CURLcode result = CURLE_OK; 1045 struct SessionHandle *data = conn->data; 1046 1047 (void)instate; /* no use for this yet */ 1048 1049 if(smtpcode != 334) { 1050 failf(data, "Authentication failed: %d", smtpcode); 1051 result = CURLE_LOGIN_DENIED; 1052 } 1053 else { 1054 /* Send an empty response */ 1055 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", ""); 1056 1057 if(!result) 1058 state(conn, SMTP_AUTH_FINAL); 1059 } 1060 1061 return result; 1062} 1063 1064#endif 1065 1066#ifdef USE_NTLM 1067/* For AUTH NTLM (without initial response) responses */ 1068static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn, 1069 int smtpcode, 1070 smtpstate instate) 1071{ 1072 CURLcode result = CURLE_OK; 1073 struct SessionHandle *data = conn->data; 1074 char *type1msg = NULL; 1075 size_t len = 0; 1076 1077 (void)instate; /* no use for this yet */ 1078 1079 if(smtpcode != 334) { 1080 failf(data, "Access denied: %d", smtpcode); 1081 result = CURLE_LOGIN_DENIED; 1082 } 1083 else { 1084 /* Create the type-1 message */ 1085 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, 1086 &conn->ntlm, 1087 &type1msg, &len); 1088 if(!result && type1msg) { 1089 /* Send the message */ 1090 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg); 1091 1092 if(!result) 1093 state(conn, SMTP_AUTH_NTLM_TYPE2MSG); 1094 } 1095 } 1096 1097 Curl_safefree(type1msg); 1098 1099 return result; 1100} 1101 1102/* For NTLM type-2 responses (sent in reponse to our type-1 message) */ 1103static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn, 1104 int smtpcode, 1105 smtpstate instate) 1106{ 1107 CURLcode result = CURLE_OK; 1108 struct SessionHandle *data = conn->data; 1109 char *type2msg = NULL; 1110 char *type3msg = NULL; 1111 size_t len = 0; 1112 1113 (void)instate; /* no use for this yet */ 1114 1115 if(smtpcode != 334) { 1116 failf(data, "Access denied: %d", smtpcode); 1117 result = CURLE_LOGIN_DENIED; 1118 } 1119 else { 1120 /* Get the type-2 message */ 1121 smtp_get_message(data->state.buffer, &type2msg); 1122 1123 /* Decode the type-2 message */ 1124 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm); 1125 if(result) { 1126 /* Send the cancellation */ 1127 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*"); 1128 1129 if(!result) 1130 state(conn, SMTP_AUTH_CANCEL); 1131 } 1132 else { 1133 /* Create the type-3 message */ 1134 result = Curl_sasl_create_ntlm_type3_message(data, conn->user, 1135 conn->passwd, &conn->ntlm, 1136 &type3msg, &len); 1137 if(!result && type3msg) { 1138 /* Send the message */ 1139 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg); 1140 1141 if(!result) 1142 state(conn, SMTP_AUTH_FINAL); 1143 } 1144 } 1145 } 1146 1147 Curl_safefree(type3msg); 1148 1149 return result; 1150} 1151#endif 1152 1153/* For AUTH XOAUTH2 (without initial response) responses */ 1154static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn, 1155 int smtpcode, smtpstate instate) 1156{ 1157 CURLcode result = CURLE_OK; 1158 struct SessionHandle *data = conn->data; 1159 size_t len = 0; 1160 char *xoauth = NULL; 1161 1162 (void)instate; /* no use for this yet */ 1163 1164 if(smtpcode != 334) { 1165 failf(data, "Access denied: %d", smtpcode); 1166 result = CURLE_LOGIN_DENIED; 1167 } 1168 else { 1169 /* Create the authorisation message */ 1170 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, 1171 conn->xoauth2_bearer, 1172 &xoauth, &len); 1173 if(!result && xoauth) { 1174 /* Send the message */ 1175 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth); 1176 1177 if(!result) 1178 state(conn, SMTP_AUTH_FINAL); 1179 } 1180 } 1181 1182 Curl_safefree(xoauth); 1183 1184 return result; 1185} 1186 1187/* For AUTH cancellation responses */ 1188static CURLcode smtp_state_auth_cancel_resp(struct connectdata *conn, 1189 int smtpcode, 1190 smtpstate instate) 1191{ 1192 CURLcode result = CURLE_OK; 1193 struct SessionHandle *data = conn->data; 1194 struct smtp_conn *smtpc = &conn->proto.smtpc; 1195 const char *mech = NULL; 1196 char *initresp = NULL; 1197 size_t len = 0; 1198 smtpstate state1 = SMTP_STOP; 1199 smtpstate state2 = SMTP_STOP; 1200 1201 (void)smtpcode; 1202 (void)instate; /* no use for this yet */ 1203 1204 /* Remove the offending mechanism from the supported list */ 1205 smtpc->authmechs ^= smtpc->authused; 1206 1207 /* Calculate alternative SASL login details */ 1208 result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1, 1209 &state2); 1210 1211 if(!result) { 1212 /* Do we have any mechanisms left? */ 1213 if(mech) { 1214 /* Retry SASL based authentication */ 1215 result = smtp_perform_auth(conn, mech, initresp, len, state1, state2); 1216 1217 Curl_safefree(initresp); 1218 } 1219 else { 1220 failf(data, "Authentication cancelled"); 1221 1222 result = CURLE_LOGIN_DENIED; 1223 } 1224 } 1225 1226 return result; 1227} 1228 1229/* For final responses in the AUTH sequence */ 1230static CURLcode smtp_state_auth_final_resp(struct connectdata *conn, 1231 int smtpcode, 1232 smtpstate instate) 1233{ 1234 CURLcode result = CURLE_OK; 1235 struct SessionHandle *data = conn->data; 1236 1237 (void)instate; /* no use for this yet */ 1238 1239 if(smtpcode != 235) { 1240 failf(data, "Authentication failed: %d", smtpcode); 1241 result = CURLE_LOGIN_DENIED; 1242 } 1243 else 1244 /* End of connect phase */ 1245 state(conn, SMTP_STOP); 1246 1247 return result; 1248} 1249 1250/* For command responses */ 1251static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode, 1252 smtpstate instate) 1253{ 1254 CURLcode result = CURLE_OK; 1255 struct SessionHandle *data = conn->data; 1256 struct SMTP *smtp = data->req.protop; 1257 char *line = data->state.buffer; 1258 size_t len = strlen(line); 1259 1260 (void)instate; /* no use for this yet */ 1261 1262 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) || 1263 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) { 1264 failf(data, "Command failed: %d", smtpcode); 1265 result = CURLE_RECV_ERROR; 1266 } 1267 else { 1268 /* Temporarily add the LF character back and send as body to the client */ 1269 if(!data->set.opt_no_body) { 1270 line[len] = '\n'; 1271 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1); 1272 line[len] = '\0'; 1273 } 1274 1275 if(smtpcode != 1) { 1276 if(smtp->rcpt) { 1277 smtp->rcpt = smtp->rcpt->next; 1278 1279 if(smtp->rcpt) { 1280 /* Send the next command */ 1281 result = smtp_perform_command(conn); 1282 } 1283 else 1284 /* End of DO phase */ 1285 state(conn, SMTP_STOP); 1286 } 1287 else 1288 /* End of DO phase */ 1289 state(conn, SMTP_STOP); 1290 } 1291 } 1292 1293 return result; 1294} 1295 1296/* For MAIL responses */ 1297static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode, 1298 smtpstate instate) 1299{ 1300 CURLcode result = CURLE_OK; 1301 struct SessionHandle *data = conn->data; 1302 1303 (void)instate; /* no use for this yet */ 1304 1305 if(smtpcode/100 != 2) { 1306 failf(data, "MAIL failed: %d", smtpcode); 1307 result = CURLE_SEND_ERROR; 1308 } 1309 else 1310 /* Start the RCPT TO command */ 1311 result = smtp_perform_rcpt_to(conn); 1312 1313 return result; 1314} 1315 1316/* For RCPT responses */ 1317static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, 1318 smtpstate instate) 1319{ 1320 CURLcode result = CURLE_OK; 1321 struct SessionHandle *data = conn->data; 1322 struct SMTP *smtp = data->req.protop; 1323 1324 (void)instate; /* no use for this yet */ 1325 1326 if(smtpcode/100 != 2) { 1327 failf(data, "RCPT failed: %d", smtpcode); 1328 result = CURLE_SEND_ERROR; 1329 } 1330 else { 1331 smtp->rcpt = smtp->rcpt->next; 1332 1333 if(smtp->rcpt) 1334 /* Send the next RCPT TO command */ 1335 result = smtp_perform_rcpt_to(conn); 1336 else { 1337 /* Send the DATA command */ 1338 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA"); 1339 1340 if(!result) 1341 state(conn, SMTP_DATA); 1342 } 1343 } 1344 1345 return result; 1346} 1347 1348/* For DATA response */ 1349static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode, 1350 smtpstate instate) 1351{ 1352 CURLcode result = CURLE_OK; 1353 struct SessionHandle *data = conn->data; 1354 1355 (void)instate; /* no use for this yet */ 1356 1357 if(smtpcode != 354) { 1358 failf(data, "DATA failed: %d", smtpcode); 1359 result = CURLE_SEND_ERROR; 1360 } 1361 else { 1362 /* Set the progress upload size */ 1363 Curl_pgrsSetUploadSize(data, data->set.infilesize); 1364 1365 /* SMTP upload */ 1366 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); 1367 1368 /* End of DO phase */ 1369 state(conn, SMTP_STOP); 1370 } 1371 1372 return result; 1373} 1374 1375/* For POSTDATA responses, which are received after the entire DATA 1376 part has been sent to the server */ 1377static CURLcode smtp_state_postdata_resp(struct connectdata *conn, 1378 int smtpcode, 1379 smtpstate instate) 1380{ 1381 CURLcode result = CURLE_OK; 1382 1383 (void)instate; /* no use for this yet */ 1384 1385 if(smtpcode != 250) 1386 result = CURLE_RECV_ERROR; 1387 1388 /* End of DONE phase */ 1389 state(conn, SMTP_STOP); 1390 1391 return result; 1392} 1393 1394static CURLcode smtp_statemach_act(struct connectdata *conn) 1395{ 1396 CURLcode result = CURLE_OK; 1397 curl_socket_t sock = conn->sock[FIRSTSOCKET]; 1398 struct SessionHandle *data = conn->data; 1399 int smtpcode; 1400 struct smtp_conn *smtpc = &conn->proto.smtpc; 1401 struct pingpong *pp = &smtpc->pp; 1402 size_t nread = 0; 1403 1404 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ 1405 if(smtpc->state == SMTP_UPGRADETLS) 1406 return smtp_perform_upgrade_tls(conn); 1407 1408 /* Flush any data that needs to be sent */ 1409 if(pp->sendleft) 1410 return Curl_pp_flushsend(pp); 1411 1412 do { 1413 /* Read the response from the server */ 1414 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread); 1415 if(result) 1416 return result; 1417 1418 /* Store the latest response for later retrieval if necessary */ 1419 if(smtpc->state != SMTP_QUIT && smtpcode != 1) 1420 data->info.httpcode = smtpcode; 1421 1422 if(!smtpcode) 1423 break; 1424 1425 /* We have now received a full SMTP server response */ 1426 switch(smtpc->state) { 1427 case SMTP_SERVERGREET: 1428 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state); 1429 break; 1430 1431 case SMTP_EHLO: 1432 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state); 1433 break; 1434 1435 case SMTP_HELO: 1436 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state); 1437 break; 1438 1439 case SMTP_STARTTLS: 1440 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state); 1441 break; 1442 1443 case SMTP_AUTH_PLAIN: 1444 result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state); 1445 break; 1446 1447 case SMTP_AUTH_LOGIN: 1448 result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state); 1449 break; 1450 1451 case SMTP_AUTH_LOGIN_PASSWD: 1452 result = smtp_state_auth_login_password_resp(conn, smtpcode, 1453 smtpc->state); 1454 break; 1455 1456#ifndef CURL_DISABLE_CRYPTO_AUTH 1457 case SMTP_AUTH_CRAMMD5: 1458 result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state); 1459 break; 1460 1461 case SMTP_AUTH_DIGESTMD5: 1462 result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state); 1463 break; 1464 1465 case SMTP_AUTH_DIGESTMD5_RESP: 1466 result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state); 1467 break; 1468#endif 1469 1470#ifdef USE_NTLM 1471 case SMTP_AUTH_NTLM: 1472 result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state); 1473 break; 1474 1475 case SMTP_AUTH_NTLM_TYPE2MSG: 1476 result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode, 1477 smtpc->state); 1478 break; 1479#endif 1480 1481 case SMTP_AUTH_XOAUTH2: 1482 result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state); 1483 break; 1484 1485 case SMTP_AUTH_CANCEL: 1486 result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state); 1487 break; 1488 1489 case SMTP_AUTH_FINAL: 1490 result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state); 1491 break; 1492 1493 case SMTP_COMMAND: 1494 result = smtp_state_command_resp(conn, smtpcode, smtpc->state); 1495 break; 1496 1497 case SMTP_MAIL: 1498 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state); 1499 break; 1500 1501 case SMTP_RCPT: 1502 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state); 1503 break; 1504 1505 case SMTP_DATA: 1506 result = smtp_state_data_resp(conn, smtpcode, smtpc->state); 1507 break; 1508 1509 case SMTP_POSTDATA: 1510 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state); 1511 break; 1512 1513 case SMTP_QUIT: 1514 /* fallthrough, just stop! */ 1515 default: 1516 /* internal error */ 1517 state(conn, SMTP_STOP); 1518 break; 1519 } 1520 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp)); 1521 1522 return result; 1523} 1524 1525/* Called repeatedly until done from multi.c */ 1526static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done) 1527{ 1528 CURLcode result = CURLE_OK; 1529 struct smtp_conn *smtpc = &conn->proto.smtpc; 1530 1531 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { 1532 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); 1533 if(result || !smtpc->ssldone) 1534 return result; 1535 } 1536 1537 result = Curl_pp_statemach(&smtpc->pp, FALSE); 1538 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE; 1539 1540 return result; 1541} 1542 1543static CURLcode smtp_block_statemach(struct connectdata *conn) 1544{ 1545 CURLcode result = CURLE_OK; 1546 struct smtp_conn *smtpc = &conn->proto.smtpc; 1547 1548 while(smtpc->state != SMTP_STOP && !result) 1549 result = Curl_pp_statemach(&smtpc->pp, TRUE); 1550 1551 return result; 1552} 1553 1554/* Allocate and initialize the SMTP struct for the current SessionHandle if 1555 required */ 1556static CURLcode smtp_init(struct connectdata *conn) 1557{ 1558 CURLcode result = CURLE_OK; 1559 struct SessionHandle *data = conn->data; 1560 struct SMTP *smtp; 1561 1562 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1); 1563 if(!smtp) 1564 result = CURLE_OUT_OF_MEMORY; 1565 1566 return result; 1567} 1568 1569/* For the SMTP "protocol connect" and "doing" phases only */ 1570static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, 1571 int numsocks) 1572{ 1573 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks); 1574} 1575 1576/*********************************************************************** 1577 * 1578 * smtp_connect() 1579 * 1580 * This function should do everything that is to be considered a part of 1581 * the connection phase. 1582 * 1583 * The variable pointed to by 'done' will be TRUE if the protocol-layer 1584 * connect phase is done when this function returns, or FALSE if not. 1585 */ 1586static CURLcode smtp_connect(struct connectdata *conn, bool *done) 1587{ 1588 CURLcode result = CURLE_OK; 1589 struct smtp_conn *smtpc = &conn->proto.smtpc; 1590 struct pingpong *pp = &smtpc->pp; 1591 1592 *done = FALSE; /* default to not done yet */ 1593 1594 /* We always support persistent connections in SMTP */ 1595 conn->bits.close = FALSE; 1596 1597 /* Set the default response time-out */ 1598 pp->response_time = RESP_TIMEOUT; 1599 pp->statemach_act = smtp_statemach_act; 1600 pp->endofresp = smtp_endofresp; 1601 pp->conn = conn; 1602 1603 /* Set the default preferred authentication mechanism */ 1604 smtpc->prefmech = SASL_AUTH_ANY; 1605 1606 /* Initialise the pingpong layer */ 1607 Curl_pp_init(pp); 1608 1609 /* Parse the URL options */ 1610 result = smtp_parse_url_options(conn); 1611 if(result) 1612 return result; 1613 1614 /* Parse the URL path */ 1615 result = smtp_parse_url_path(conn); 1616 if(result) 1617 return result; 1618 1619 /* Start off waiting for the server greeting response */ 1620 state(conn, SMTP_SERVERGREET); 1621 1622 result = smtp_multi_statemach(conn, done); 1623 1624 return result; 1625} 1626 1627/*********************************************************************** 1628 * 1629 * smtp_done() 1630 * 1631 * The DONE function. This does what needs to be done after a single DO has 1632 * performed. 1633 * 1634 * Input argument is already checked for validity. 1635 */ 1636static CURLcode smtp_done(struct connectdata *conn, CURLcode status, 1637 bool premature) 1638{ 1639 CURLcode result = CURLE_OK; 1640 struct SessionHandle *data = conn->data; 1641 struct SMTP *smtp = data->req.protop; 1642 struct pingpong *pp = &conn->proto.smtpc.pp; 1643 const char *eob; 1644 ssize_t len; 1645 ssize_t bytes_written; 1646 1647 (void)premature; 1648 1649 if(!smtp) 1650 /* When the easy handle is removed from the multi interface while libcurl 1651 is still trying to resolve the host name, the SMTP struct is not yet 1652 initialized. However, the removal action calls Curl_done() which in 1653 turn calls this function, so we simply return success. */ 1654 return CURLE_OK; 1655 1656 if(status) { 1657 conn->bits.close = TRUE; /* marked for closure */ 1658 result = status; /* use the already set error code */ 1659 } 1660 else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) { 1661 /* Calculate the EOB taking into account any terminating CRLF from the 1662 previous line of the email or the CRLF of the DATA command when there 1663 is "no mail data". RFC-5321, sect. 4.1.1.4. */ 1664 eob = SMTP_EOB; 1665 len = SMTP_EOB_LEN; 1666 if(smtp->trailing_crlf || !conn->data->set.infilesize) { 1667 eob += 2; 1668 len -= 2; 1669 } 1670 1671 /* Send the end of block data */ 1672 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written); 1673 if(result) 1674 return result; 1675 1676 if(bytes_written != len) { 1677 /* The whole chunk was not sent so keep it around and adjust the 1678 pingpong structure accordingly */ 1679 pp->sendthis = strdup(eob); 1680 pp->sendsize = len; 1681 pp->sendleft = len - bytes_written; 1682 } 1683 else 1684 /* Successfully sent so adjust the response timeout relative to now */ 1685 pp->response = Curl_tvnow(); 1686 1687 state(conn, SMTP_POSTDATA); 1688 1689 /* Run the state-machine 1690 1691 TODO: when the multi interface is used, this _really_ should be using 1692 the smtp_multi_statemach function but we have no general support for 1693 non-blocking DONE operations, not in the multi state machine and with 1694 Curl_done() invokes on several places in the code! 1695 */ 1696 result = smtp_block_statemach(conn); 1697 } 1698 1699 /* Cleanup our per-request based variables */ 1700 Curl_safefree(smtp->custom); 1701 1702 /* Clear the transfer mode for the next request */ 1703 smtp->transfer = FTPTRANSFER_BODY; 1704 1705 return result; 1706} 1707 1708/*********************************************************************** 1709 * 1710 * smtp_perform() 1711 * 1712 * This is the actual DO function for SMTP. Transfer a mail, send a command 1713 * or get some data according to the options previously setup. 1714 */ 1715static CURLcode smtp_perform(struct connectdata *conn, bool *connected, 1716 bool *dophase_done) 1717{ 1718 /* This is SMTP and no proxy */ 1719 CURLcode result = CURLE_OK; 1720 struct SessionHandle *data = conn->data; 1721 struct SMTP *smtp = data->req.protop; 1722 1723 DEBUGF(infof(conn->data, "DO phase starts\n")); 1724 1725 if(data->set.opt_no_body) { 1726 /* Requested no body means no transfer */ 1727 smtp->transfer = FTPTRANSFER_INFO; 1728 } 1729 1730 *dophase_done = FALSE; /* not done yet */ 1731 1732 /* Store the first recipient (or NULL if not specified) */ 1733 smtp->rcpt = data->set.mail_rcpt; 1734 1735 /* Start the first command in the DO phase */ 1736 if(data->set.upload && data->set.mail_rcpt) 1737 /* MAIL transfer */ 1738 result = smtp_perform_mail(conn); 1739 else 1740 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */ 1741 result = smtp_perform_command(conn); 1742 1743 if(result) 1744 return result; 1745 1746 /* Run the state-machine */ 1747 result = smtp_multi_statemach(conn, dophase_done); 1748 1749 *connected = conn->bits.tcpconnect[FIRSTSOCKET]; 1750 1751 if(*dophase_done) 1752 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1753 1754 return result; 1755} 1756 1757/*********************************************************************** 1758 * 1759 * smtp_do() 1760 * 1761 * This function is registered as 'curl_do' function. It decodes the path 1762 * parts etc as a wrapper to the actual DO function (smtp_perform). 1763 * 1764 * The input argument is already checked for validity. 1765 */ 1766static CURLcode smtp_do(struct connectdata *conn, bool *done) 1767{ 1768 CURLcode result = CURLE_OK; 1769 1770 *done = FALSE; /* default to false */ 1771 1772 /* Parse the custom request */ 1773 result = smtp_parse_custom_request(conn); 1774 if(result) 1775 return result; 1776 1777 result = smtp_regular_transfer(conn, done); 1778 1779 return result; 1780} 1781 1782/*********************************************************************** 1783 * 1784 * smtp_disconnect() 1785 * 1786 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection 1787 * resources. BLOCKING. 1788 */ 1789static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection) 1790{ 1791 struct smtp_conn *smtpc = &conn->proto.smtpc; 1792 1793 /* We cannot send quit unconditionally. If this connection is stale or 1794 bad in any way, sending quit and waiting around here will make the 1795 disconnect wait in vain and cause more problems than we need to. */ 1796 1797 /* The SMTP session may or may not have been allocated/setup at this 1798 point! */ 1799 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart) 1800 if(!smtp_perform_quit(conn)) 1801 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */ 1802 1803 /* Disconnect from the server */ 1804 Curl_pp_disconnect(&smtpc->pp); 1805 1806 /* Cleanup the SASL module */ 1807 Curl_sasl_cleanup(conn, smtpc->authused); 1808 1809 /* Cleanup our connection based variables */ 1810 Curl_safefree(smtpc->domain); 1811 1812 return CURLE_OK; 1813} 1814 1815/* Call this when the DO phase has completed */ 1816static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected) 1817{ 1818 struct SMTP *smtp = conn->data->req.protop; 1819 1820 (void)connected; 1821 1822 if(smtp->transfer != FTPTRANSFER_BODY) 1823 /* no data to transfer */ 1824 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); 1825 1826 return CURLE_OK; 1827} 1828 1829/* Called from multi.c while DOing */ 1830static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done) 1831{ 1832 CURLcode result = smtp_multi_statemach(conn, dophase_done); 1833 1834 if(result) 1835 DEBUGF(infof(conn->data, "DO phase failed\n")); 1836 else if(*dophase_done) { 1837 result = smtp_dophase_done(conn, FALSE /* not connected */); 1838 1839 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1840 } 1841 1842 return result; 1843} 1844 1845/*********************************************************************** 1846 * 1847 * smtp_regular_transfer() 1848 * 1849 * The input argument is already checked for validity. 1850 * 1851 * Performs all commands done before a regular transfer between a local and a 1852 * remote host. 1853 */ 1854static CURLcode smtp_regular_transfer(struct connectdata *conn, 1855 bool *dophase_done) 1856{ 1857 CURLcode result = CURLE_OK; 1858 bool connected = FALSE; 1859 struct SessionHandle *data = conn->data; 1860 1861 /* Make sure size is unknown at this point */ 1862 data->req.size = -1; 1863 1864 /* Set the progress data */ 1865 Curl_pgrsSetUploadCounter(data, 0); 1866 Curl_pgrsSetDownloadCounter(data, 0); 1867 Curl_pgrsSetUploadSize(data, 0); 1868 Curl_pgrsSetDownloadSize(data, 0); 1869 1870 /* Carry out the perform */ 1871 result = smtp_perform(conn, &connected, dophase_done); 1872 1873 /* Perform post DO phase operations if necessary */ 1874 if(!result && *dophase_done) 1875 result = smtp_dophase_done(conn, connected); 1876 1877 return result; 1878} 1879 1880static CURLcode smtp_setup_connection(struct connectdata *conn) 1881{ 1882 struct SessionHandle *data = conn->data; 1883 CURLcode result; 1884 1885 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { 1886 /* Unless we have asked to tunnel SMTP operations through the proxy, we 1887 switch and use HTTP operations only */ 1888#ifndef CURL_DISABLE_HTTP 1889 if(conn->handler == &Curl_handler_smtp) 1890 conn->handler = &Curl_handler_smtp_proxy; 1891 else { 1892#ifdef USE_SSL 1893 conn->handler = &Curl_handler_smtps_proxy; 1894#else 1895 failf(data, "SMTPS not supported!"); 1896 return CURLE_UNSUPPORTED_PROTOCOL; 1897#endif 1898 } 1899 /* set it up as a HTTP connection instead */ 1900 return conn->handler->setup_connection(conn); 1901 1902#else 1903 failf(data, "SMTP over http proxy requires HTTP support built-in!"); 1904 return CURLE_UNSUPPORTED_PROTOCOL; 1905#endif 1906 } 1907 1908 /* Initialise the SMTP layer */ 1909 result = smtp_init(conn); 1910 if(result) 1911 return result; 1912 1913 data->state.path++; /* don't include the initial slash */ 1914 1915 return CURLE_OK; 1916} 1917 1918/*********************************************************************** 1919 * 1920 * smtp_parse_url_options() 1921 * 1922 * Parse the URL login options. 1923 */ 1924static CURLcode smtp_parse_url_options(struct connectdata *conn) 1925{ 1926 CURLcode result = CURLE_OK; 1927 struct smtp_conn *smtpc = &conn->proto.smtpc; 1928 const char *options = conn->options; 1929 const char *ptr = options; 1930 bool reset = TRUE; 1931 1932 while(ptr && *ptr) { 1933 const char *key = ptr; 1934 1935 while(*ptr && *ptr != '=') 1936 ptr++; 1937 1938 if(strnequal(key, "AUTH", 4)) { 1939 size_t len = 0; 1940 const char *value = ++ptr; 1941 1942 if(reset) { 1943 reset = FALSE; 1944 smtpc->prefmech = SASL_AUTH_NONE; 1945 } 1946 1947 while(*ptr && *ptr != ';') { 1948 ptr++; 1949 len++; 1950 } 1951 1952 if(strnequal(value, "*", len)) 1953 smtpc->prefmech = SASL_AUTH_ANY; 1954 else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) 1955 smtpc->prefmech |= SASL_MECH_LOGIN; 1956 else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) 1957 smtpc->prefmech |= SASL_MECH_PLAIN; 1958 else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) 1959 smtpc->prefmech |= SASL_MECH_CRAM_MD5; 1960 else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) 1961 smtpc->prefmech |= SASL_MECH_DIGEST_MD5; 1962 else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) 1963 smtpc->prefmech |= SASL_MECH_GSSAPI; 1964 else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) 1965 smtpc->prefmech |= SASL_MECH_NTLM; 1966 else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) 1967 smtpc->prefmech |= SASL_MECH_XOAUTH2; 1968 1969 if(*ptr == ';') 1970 ptr++; 1971 } 1972 else 1973 result = CURLE_URL_MALFORMAT; 1974 } 1975 1976 return result; 1977} 1978 1979/*********************************************************************** 1980 * 1981 * smtp_parse_url_path() 1982 * 1983 * Parse the URL path into separate path components. 1984 */ 1985static CURLcode smtp_parse_url_path(struct connectdata *conn) 1986{ 1987 /* The SMTP struct is already initialised in smtp_connect() */ 1988 struct SessionHandle *data = conn->data; 1989 struct smtp_conn *smtpc = &conn->proto.smtpc; 1990 const char *path = data->state.path; 1991 char localhost[HOSTNAME_MAX + 1]; 1992 1993 /* Calculate the path if necessary */ 1994 if(!*path) { 1995 if(!Curl_gethostname(localhost, sizeof(localhost))) 1996 path = localhost; 1997 else 1998 path = "localhost"; 1999 } 2000 2001 /* URL decode the path and use it as the domain in our EHLO */ 2002 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE); 2003} 2004 2005/*********************************************************************** 2006 * 2007 * smtp_parse_custom_request() 2008 * 2009 * Parse the custom request. 2010 */ 2011static CURLcode smtp_parse_custom_request(struct connectdata *conn) 2012{ 2013 CURLcode result = CURLE_OK; 2014 struct SessionHandle *data = conn->data; 2015 struct SMTP *smtp = data->req.protop; 2016 const char *custom = data->set.str[STRING_CUSTOMREQUEST]; 2017 2018 /* URL decode the custom request */ 2019 if(custom) 2020 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE); 2021 2022 return result; 2023} 2024 2025/*********************************************************************** 2026 * 2027 * smtp_calc_sasl_details() 2028 * 2029 * Calculate the required login details for SASL authentication. 2030 */ 2031static CURLcode smtp_calc_sasl_details(struct connectdata *conn, 2032 const char **mech, 2033 char **initresp, size_t *len, 2034 smtpstate *state1, smtpstate *state2) 2035{ 2036 CURLcode result = CURLE_OK; 2037 struct SessionHandle *data = conn->data; 2038 struct smtp_conn *smtpc = &conn->proto.smtpc; 2039 2040 /* Calculate the supported authentication mechanism, by decreasing order of 2041 security, as well as the initial response where appropriate */ 2042#ifndef CURL_DISABLE_CRYPTO_AUTH 2043 if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) && 2044 (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) { 2045 *mech = SASL_MECH_STRING_DIGEST_MD5; 2046 *state1 = SMTP_AUTH_DIGESTMD5; 2047 smtpc->authused = SASL_MECH_DIGEST_MD5; 2048 } 2049 else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) && 2050 (smtpc->prefmech & SASL_MECH_CRAM_MD5)) { 2051 *mech = SASL_MECH_STRING_CRAM_MD5; 2052 *state1 = SMTP_AUTH_CRAMMD5; 2053 smtpc->authused = SASL_MECH_CRAM_MD5; 2054 } 2055 else 2056#endif 2057#ifdef USE_NTLM 2058 if((smtpc->authmechs & SASL_MECH_NTLM) && 2059 (smtpc->prefmech & SASL_MECH_NTLM)) { 2060 *mech = SASL_MECH_STRING_NTLM; 2061 *state1 = SMTP_AUTH_NTLM; 2062 *state2 = SMTP_AUTH_NTLM_TYPE2MSG; 2063 smtpc->authused = SASL_MECH_NTLM; 2064 2065 if(data->set.sasl_ir) 2066 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, 2067 &conn->ntlm, 2068 initresp, len); 2069 } 2070 else 2071#endif 2072 if(((smtpc->authmechs & SASL_MECH_XOAUTH2) && 2073 (smtpc->prefmech & SASL_MECH_XOAUTH2) && 2074 (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { 2075 *mech = SASL_MECH_STRING_XOAUTH2; 2076 *state1 = SMTP_AUTH_XOAUTH2; 2077 *state2 = SMTP_AUTH_FINAL; 2078 smtpc->authused = SASL_MECH_XOAUTH2; 2079 2080 if(data->set.sasl_ir) 2081 result = Curl_sasl_create_xoauth2_message(data, conn->user, 2082 conn->xoauth2_bearer, 2083 initresp, len); 2084 } 2085 else if((smtpc->authmechs & SASL_MECH_LOGIN) && 2086 (smtpc->prefmech & SASL_MECH_LOGIN)) { 2087 *mech = SASL_MECH_STRING_LOGIN; 2088 *state1 = SMTP_AUTH_LOGIN; 2089 *state2 = SMTP_AUTH_LOGIN_PASSWD; 2090 smtpc->authused = SASL_MECH_LOGIN; 2091 2092 if(data->set.sasl_ir) 2093 result = Curl_sasl_create_login_message(data, conn->user, initresp, len); 2094 } 2095 else if((smtpc->authmechs & SASL_MECH_PLAIN) && 2096 (smtpc->prefmech & SASL_MECH_PLAIN)) { 2097 *mech = SASL_MECH_STRING_PLAIN; 2098 *state1 = SMTP_AUTH_PLAIN; 2099 *state2 = SMTP_AUTH_FINAL; 2100 smtpc->authused = SASL_MECH_PLAIN; 2101 2102 if(data->set.sasl_ir) 2103 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, 2104 initresp, len); 2105 } 2106 2107 return result; 2108} 2109 2110CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread) 2111{ 2112 /* When sending a SMTP payload we must detect CRLF. sequences making sure 2113 they are sent as CRLF.. instead, as a . on the beginning of a line will 2114 be deleted by the server when not part of an EOB terminator and a 2115 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of 2116 data by the server 2117 */ 2118 ssize_t i; 2119 ssize_t si; 2120 struct SessionHandle *data = conn->data; 2121 struct SMTP *smtp = data->req.protop; 2122 2123 /* Do we need to allocate the scatch buffer? */ 2124 if(!data->state.scratch) { 2125 data->state.scratch = malloc(2 * BUFSIZE); 2126 2127 if(!data->state.scratch) { 2128 failf (data, "Failed to alloc scratch buffer!"); 2129 return CURLE_OUT_OF_MEMORY; 2130 } 2131 } 2132 2133 /* This loop can be improved by some kind of Boyer-Moore style of 2134 approach but that is saved for later... */ 2135 for(i = 0, si = 0; i < nread; i++) { 2136 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) { 2137 smtp->eob++; 2138 2139 /* Is the EOB potentially the terminating CRLF? */ 2140 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob) 2141 smtp->trailing_crlf = TRUE; 2142 else 2143 smtp->trailing_crlf = FALSE; 2144 } 2145 else if(smtp->eob) { 2146 /* A previous substring matched so output that first */ 2147 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob); 2148 si += smtp->eob; 2149 2150 /* Then compare the first byte */ 2151 if(SMTP_EOB[0] == data->req.upload_fromhere[i]) 2152 smtp->eob = 1; 2153 else 2154 smtp->eob = 0; 2155 2156 /* Reset the trailing CRLF flag as there was more data */ 2157 smtp->trailing_crlf = FALSE; 2158 } 2159 2160 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */ 2161 if(SMTP_EOB_FIND_LEN == smtp->eob) { 2162 /* Copy the replacement data to the target buffer */ 2163 memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN); 2164 si += SMTP_EOB_REPL_LEN; 2165 smtp->eob = 0; 2166 } 2167 else if(!smtp->eob) 2168 data->state.scratch[si++] = data->req.upload_fromhere[i]; 2169 } 2170 2171 if(smtp->eob) { 2172 /* A substring matched before processing ended so output that now */ 2173 memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob); 2174 si += smtp->eob; 2175 smtp->eob = 0; 2176 } 2177 2178 if(si != nread) { 2179 /* Only use the new buffer if we replaced something */ 2180 nread = si; 2181 2182 /* Upload from the new (replaced) buffer instead */ 2183 data->req.upload_fromhere = data->state.scratch; 2184 2185 /* Set the new amount too */ 2186 data->req.upload_present = nread; 2187 } 2188 2189 return CURLE_OK; 2190} 2191 2192#endif /* CURL_DISABLE_SMTP */ 2193