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