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