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