smtp.c (64691B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 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 https://curl.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 * SPDX-License-Identifier: curl 22 * 23 * RFC1870 SMTP Service Extension for Message Size 24 * RFC2195 CRAM-MD5 authentication 25 * RFC2831 DIGEST-MD5 authentication 26 * RFC3207 SMTP over TLS 27 * RFC4422 Simple Authentication and Security Layer (SASL) 28 * RFC4616 PLAIN authentication 29 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism 30 * RFC4954 SMTP Authentication 31 * RFC5321 SMTP protocol 32 * RFC5890 Internationalized Domain Names for Applications (IDNA) 33 * RFC6531 SMTP Extension for Internationalized Email 34 * RFC6532 Internationalized Email Headers 35 * RFC6749 OAuth 2.0 Authorization Framework 36 * RFC8314 Use of TLS for Email Submission and Access 37 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt> 38 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> 39 * 40 ***************************************************************************/ 41 42 #include "curl_setup.h" 43 44 #ifndef CURL_DISABLE_SMTP 45 46 #ifdef HAVE_NETINET_IN_H 47 #include <netinet/in.h> 48 #endif 49 #ifdef HAVE_ARPA_INET_H 50 #include <arpa/inet.h> 51 #endif 52 #ifdef HAVE_NETDB_H 53 #include <netdb.h> 54 #endif 55 #ifdef __VMS 56 #include <in.h> 57 #include <inet.h> 58 #endif 59 60 #include <curl/curl.h> 61 #include "urldata.h" 62 #include "sendf.h" 63 #include "hostip.h" 64 #include "progress.h" 65 #include "transfer.h" 66 #include "escape.h" 67 #include "http.h" /* for HTTP proxy tunnel stuff */ 68 #include "mime.h" 69 #include "socks.h" 70 #include "smtp.h" 71 #include "vtls/vtls.h" 72 #include "cfilters.h" 73 #include "connect.h" 74 #include "select.h" 75 #include "multiif.h" 76 #include "url.h" 77 #include "curl_gethostname.h" 78 #include "bufref.h" 79 #include "curl_sasl.h" 80 #include "curlx/warnless.h" 81 #include "idn.h" 82 #include "curlx/strparse.h" 83 84 /* The last 3 #include files should be in this order */ 85 #include "curl_printf.h" 86 #include "curl_memory.h" 87 #include "memdebug.h" 88 89 /* meta key for storing protocol meta at easy handle */ 90 #define CURL_META_SMTP_EASY "meta:proto:smtp:easy" 91 /* meta key for storing protocol meta at connection */ 92 #define CURL_META_SMTP_CONN "meta:proto:smtp:conn" 93 94 /**************************************************************************** 95 * SMTP unique setup 96 ***************************************************************************/ 97 typedef enum { 98 SMTP_STOP, /* do nothing state, stops the state machine */ 99 SMTP_SERVERGREET, /* waiting for the initial greeting immediately after 100 a connect */ 101 SMTP_EHLO, 102 SMTP_HELO, 103 SMTP_STARTTLS, 104 SMTP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS 105 (multi mode only) */ 106 SMTP_AUTH, 107 SMTP_COMMAND, /* VRFY, EXPN, NOOP, RSET and HELP */ 108 SMTP_MAIL, /* MAIL FROM */ 109 SMTP_RCPT, /* RCPT TO */ 110 SMTP_DATA, 111 SMTP_POSTDATA, 112 SMTP_QUIT, 113 SMTP_LAST /* never used */ 114 } smtpstate; 115 116 /* smtp_conn is used for struct connection-oriented data in the connectdata 117 struct */ 118 struct smtp_conn { 119 struct pingpong pp; 120 struct SASL sasl; /* SASL-related storage */ 121 smtpstate state; /* Always use smtp.c:state() to change state! */ 122 char *domain; /* Client address/name to send in the EHLO */ 123 BIT(ssldone); /* Is connect() over SSL done? */ 124 BIT(tls_supported); /* StartTLS capability supported by server */ 125 BIT(size_supported); /* If server supports SIZE extension according to 126 RFC 1870 */ 127 BIT(utf8_supported); /* If server supports SMTPUTF8 extension according 128 to RFC 6531 */ 129 BIT(auth_supported); /* AUTH capability supported by server */ 130 }; 131 132 /* This SMTP struct is used in the Curl_easy. All SMTP data that is 133 connection-oriented must be in smtp_conn to properly deal with the fact that 134 perhaps the Curl_easy is changed between the times the connection is 135 used. */ 136 struct SMTP { 137 curl_pp_transfer transfer; 138 char *custom; /* Custom Request */ 139 struct curl_slist *rcpt; /* Recipient list */ 140 int rcpt_last_error; /* The last error received for RCPT TO command */ 141 size_t eob; /* Number of bytes of the EOB (End Of Body) that 142 have been received so far */ 143 BIT(rcpt_had_ok); /* Whether any of RCPT TO commands (depends on 144 total number of recipients) succeeded so far */ 145 BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */ 146 }; 147 148 /* Local API functions */ 149 static CURLcode smtp_regular_transfer(struct Curl_easy *data, 150 struct smtp_conn *smtpc, 151 struct SMTP *smtp, 152 bool *done); 153 static CURLcode smtp_do(struct Curl_easy *data, bool *done); 154 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, 155 bool premature); 156 static CURLcode smtp_connect(struct Curl_easy *data, bool *done); 157 static CURLcode smtp_disconnect(struct Curl_easy *data, 158 struct connectdata *conn, bool dead); 159 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done); 160 static int smtp_getsock(struct Curl_easy *data, 161 struct connectdata *conn, curl_socket_t *socks); 162 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done); 163 static CURLcode smtp_setup_connection(struct Curl_easy *data, 164 struct connectdata *conn); 165 static CURLcode smtp_parse_url_options(struct connectdata *conn, 166 struct smtp_conn *smtpc); 167 static CURLcode smtp_parse_url_path(struct Curl_easy *data, 168 struct smtp_conn *smtpc); 169 static CURLcode smtp_parse_custom_request(struct Curl_easy *data, 170 struct SMTP *smtp); 171 static CURLcode smtp_parse_address(const char *fqma, 172 char **address, struct hostname *host); 173 static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech, 174 const struct bufref *initresp); 175 static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech, 176 const struct bufref *resp); 177 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech); 178 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out); 179 static CURLcode cr_eob_add(struct Curl_easy *data); 180 181 /* 182 * SMTP protocol handler. 183 */ 184 185 const struct Curl_handler Curl_handler_smtp = { 186 "smtp", /* scheme */ 187 smtp_setup_connection, /* setup_connection */ 188 smtp_do, /* do_it */ 189 smtp_done, /* done */ 190 ZERO_NULL, /* do_more */ 191 smtp_connect, /* connect_it */ 192 smtp_multi_statemach, /* connecting */ 193 smtp_doing, /* doing */ 194 smtp_getsock, /* proto_getsock */ 195 smtp_getsock, /* doing_getsock */ 196 ZERO_NULL, /* domore_getsock */ 197 ZERO_NULL, /* perform_getsock */ 198 smtp_disconnect, /* disconnect */ 199 ZERO_NULL, /* write_resp */ 200 ZERO_NULL, /* write_resp_hd */ 201 ZERO_NULL, /* connection_check */ 202 ZERO_NULL, /* attach connection */ 203 ZERO_NULL, /* follow */ 204 PORT_SMTP, /* defport */ 205 CURLPROTO_SMTP, /* protocol */ 206 CURLPROTO_SMTP, /* family */ 207 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ 208 PROTOPT_URLOPTIONS 209 }; 210 211 #ifdef USE_SSL 212 /* 213 * SMTPS protocol handler. 214 */ 215 216 const struct Curl_handler Curl_handler_smtps = { 217 "smtps", /* scheme */ 218 smtp_setup_connection, /* setup_connection */ 219 smtp_do, /* do_it */ 220 smtp_done, /* done */ 221 ZERO_NULL, /* do_more */ 222 smtp_connect, /* connect_it */ 223 smtp_multi_statemach, /* connecting */ 224 smtp_doing, /* doing */ 225 smtp_getsock, /* proto_getsock */ 226 smtp_getsock, /* doing_getsock */ 227 ZERO_NULL, /* domore_getsock */ 228 ZERO_NULL, /* perform_getsock */ 229 smtp_disconnect, /* disconnect */ 230 ZERO_NULL, /* write_resp */ 231 ZERO_NULL, /* write_resp_hd */ 232 ZERO_NULL, /* connection_check */ 233 ZERO_NULL, /* attach connection */ 234 ZERO_NULL, /* follow */ 235 PORT_SMTPS, /* defport */ 236 CURLPROTO_SMTPS, /* protocol */ 237 CURLPROTO_SMTP, /* family */ 238 PROTOPT_CLOSEACTION | PROTOPT_SSL 239 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */ 240 }; 241 #endif 242 243 /* SASL parameters for the smtp protocol */ 244 static const struct SASLproto saslsmtp = { 245 "smtp", /* The service name */ 246 smtp_perform_auth, /* Send authentication command */ 247 smtp_continue_auth, /* Send authentication continuation */ 248 smtp_cancel_auth, /* Cancel authentication */ 249 smtp_get_message, /* Get SASL response message */ 250 512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ 251 334, /* Code received when continuation is expected */ 252 235, /* Code to receive upon authentication success */ 253 SASL_AUTH_DEFAULT, /* Default mechanisms */ 254 SASL_FLAG_BASE64 /* Configuration flags */ 255 }; 256 257 /*********************************************************************** 258 * 259 * smtp_endofresp() 260 * 261 * Checks for an ending SMTP status code at the start of the given string, but 262 * also detects various capabilities from the EHLO response including the 263 * supported authentication mechanisms. 264 */ 265 static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn, 266 const char *line, size_t len, int *resp) 267 { 268 struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); 269 bool result = FALSE; 270 (void)data; 271 272 DEBUGASSERT(smtpc); 273 if(!smtpc) 274 return FALSE; 275 276 /* Nothing for us */ 277 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) 278 return FALSE; 279 280 /* Do we have a command response? This should be the response code followed 281 by a space and optionally some text as per RFC-5321 and as outlined in 282 Section 4. Examples of RFC-4954 but some email servers ignore this and 283 only send the response code instead as per Section 4.2. */ 284 if(line[3] == ' ' || len == 5) { 285 char tmpline[6]; 286 curl_off_t code; 287 const char *p = tmpline; 288 result = TRUE; 289 memcpy(tmpline, line, (len == 5 ? 5 : 3)); 290 tmpline[len == 5 ? 5 : 3 ] = 0; 291 if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999)) 292 return FALSE; 293 *resp = (int) code; 294 295 /* Make sure real server never sends internal value */ 296 if(*resp == 1) 297 *resp = 0; 298 } 299 /* Do we have a multiline (continuation) response? */ 300 else if(line[3] == '-' && 301 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { 302 result = TRUE; 303 *resp = 1; /* Internal response code */ 304 } 305 306 return result; 307 } 308 309 /*********************************************************************** 310 * 311 * smtp_get_message() 312 * 313 * Gets the authentication message from the response buffer. 314 */ 315 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) 316 { 317 struct smtp_conn *smtpc = 318 Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); 319 char *message; 320 size_t len; 321 322 if(!smtpc) 323 return CURLE_FAILED_INIT; 324 325 message = curlx_dyn_ptr(&smtpc->pp.recvbuf); 326 len = smtpc->pp.nfinal; 327 if(len > 4) { 328 /* Find the start of the message */ 329 len -= 4; 330 for(message += 4; *message == ' ' || *message == '\t'; message++, len--) 331 ; 332 333 /* Find the end of the message */ 334 while(len--) 335 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && 336 message[len] != '\t') 337 break; 338 339 /* Terminate the message */ 340 message[++len] = '\0'; 341 Curl_bufref_set(out, message, len, NULL); 342 } 343 else 344 /* junk input => zero length output */ 345 Curl_bufref_set(out, "", 0, NULL); 346 347 return CURLE_OK; 348 } 349 350 /*********************************************************************** 351 * 352 * smtp_state() 353 * 354 * This is the ONLY way to change SMTP state! 355 */ 356 static void smtp_state(struct Curl_easy *data, 357 struct smtp_conn *smtpc, 358 smtpstate newstate) 359 { 360 #if !defined(CURL_DISABLE_VERBOSE_STRINGS) 361 /* for debug purposes */ 362 static const char * const names[] = { 363 "STOP", 364 "SERVERGREET", 365 "EHLO", 366 "HELO", 367 "STARTTLS", 368 "UPGRADETLS", 369 "AUTH", 370 "COMMAND", 371 "MAIL", 372 "RCPT", 373 "DATA", 374 "POSTDATA", 375 "QUIT", 376 /* LAST */ 377 }; 378 379 if(smtpc->state != newstate) 380 CURL_TRC_SMTP(data, "state change from %s to %s", 381 names[smtpc->state], names[newstate]); 382 #endif 383 384 smtpc->state = newstate; 385 } 386 387 /*********************************************************************** 388 * 389 * smtp_perform_ehlo() 390 * 391 * Sends the EHLO command to not only initialise communication with the ESMTP 392 * server but to also obtain a list of server side supported capabilities. 393 */ 394 static CURLcode smtp_perform_ehlo(struct Curl_easy *data, 395 struct smtp_conn *smtpc) 396 { 397 CURLcode result = CURLE_OK; 398 399 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */ 400 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism 401 used for esmtp connections */ 402 smtpc->tls_supported = FALSE; /* Clear the TLS capability */ 403 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ 404 405 /* Send the EHLO command */ 406 result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain); 407 408 if(!result) 409 smtp_state(data, smtpc, SMTP_EHLO); 410 411 return result; 412 } 413 414 /*********************************************************************** 415 * 416 * smtp_perform_helo() 417 * 418 * Sends the HELO command to initialise communication with the SMTP server. 419 */ 420 static CURLcode smtp_perform_helo(struct Curl_easy *data, 421 struct smtp_conn *smtpc) 422 { 423 CURLcode result = CURLE_OK; 424 425 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used 426 in smtp connections */ 427 428 /* Send the HELO command */ 429 result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain); 430 431 if(!result) 432 smtp_state(data, smtpc, SMTP_HELO); 433 434 return result; 435 } 436 437 /*********************************************************************** 438 * 439 * smtp_perform_starttls() 440 * 441 * Sends the STLS command to start the upgrade to TLS. 442 */ 443 static CURLcode smtp_perform_starttls(struct Curl_easy *data, 444 struct smtp_conn *smtpc) 445 { 446 /* Send the STARTTLS command */ 447 CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "STARTTLS"); 448 449 if(!result) 450 smtp_state(data, smtpc, SMTP_STARTTLS); 451 452 return result; 453 } 454 455 /*********************************************************************** 456 * 457 * smtp_perform_upgrade_tls() 458 * 459 * Performs the upgrade to TLS. 460 */ 461 static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data, 462 struct smtp_conn *smtpc) 463 { 464 #ifdef USE_SSL 465 /* Start the SSL connection */ 466 struct connectdata *conn = data->conn; 467 CURLcode result; 468 bool ssldone = FALSE; 469 470 DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS); 471 if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { 472 result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); 473 if(result) 474 goto out; 475 /* Change the connection handler and SMTP state */ 476 conn->handler = &Curl_handler_smtps; 477 } 478 479 DEBUGASSERT(!smtpc->ssldone); 480 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); 481 DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d", 482 result, ssldone)); 483 if(!result && ssldone) { 484 smtpc->ssldone = ssldone; 485 /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */ 486 result = smtp_perform_ehlo(data, smtpc); 487 } 488 out: 489 return result; 490 #else 491 (void)data; 492 (void)smtpc; 493 return CURLE_NOT_BUILT_IN; 494 #endif 495 } 496 497 /*********************************************************************** 498 * 499 * smtp_perform_auth() 500 * 501 * Sends an AUTH command allowing the client to login with the given SASL 502 * authentication mechanism. 503 */ 504 static CURLcode smtp_perform_auth(struct Curl_easy *data, 505 const char *mech, 506 const struct bufref *initresp) 507 { 508 CURLcode result = CURLE_OK; 509 struct smtp_conn *smtpc = 510 Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); 511 const char *ir = (const char *) Curl_bufref_ptr(initresp); 512 513 if(!smtpc) 514 return CURLE_FAILED_INIT; 515 516 if(ir) { /* AUTH <mech> ...<crlf> */ 517 /* Send the AUTH command with the initial response */ 518 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir); 519 } 520 else { 521 /* Send the AUTH command */ 522 result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s", mech); 523 } 524 525 return result; 526 } 527 528 /*********************************************************************** 529 * 530 * smtp_continue_auth() 531 * 532 * Sends SASL continuation data. 533 */ 534 static CURLcode smtp_continue_auth(struct Curl_easy *data, 535 const char *mech, 536 const struct bufref *resp) 537 { 538 struct smtp_conn *smtpc = 539 Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); 540 541 (void)mech; 542 if(!smtpc) 543 return CURLE_FAILED_INIT; 544 return Curl_pp_sendf(data, &smtpc->pp, 545 "%s", (const char *) Curl_bufref_ptr(resp)); 546 } 547 548 /*********************************************************************** 549 * 550 * smtp_cancel_auth() 551 * 552 * Sends SASL cancellation. 553 */ 554 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech) 555 { 556 struct smtp_conn *smtpc = 557 Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); 558 559 (void)mech; 560 if(!smtpc) 561 return CURLE_FAILED_INIT; 562 return Curl_pp_sendf(data, &smtpc->pp, "*"); 563 } 564 565 /*********************************************************************** 566 * 567 * smtp_perform_authentication() 568 * 569 * Initiates the authentication sequence, with the appropriate SASL 570 * authentication mechanism. 571 */ 572 static CURLcode smtp_perform_authentication(struct Curl_easy *data, 573 struct smtp_conn *smtpc) 574 { 575 CURLcode result = CURLE_OK; 576 saslprogress progress; 577 578 /* Check we have enough data to authenticate with, and the 579 server supports authentication, and end the connect phase if not */ 580 if(!smtpc->auth_supported || 581 !Curl_sasl_can_authenticate(&smtpc->sasl, data)) { 582 smtp_state(data, smtpc, SMTP_STOP); 583 return result; 584 } 585 586 /* Calculate the SASL login details */ 587 result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress); 588 589 if(!result) { 590 if(progress == SASL_INPROGRESS) 591 smtp_state(data, smtpc, SMTP_AUTH); 592 else 593 result = Curl_sasl_is_blocked(&smtpc->sasl, data); 594 } 595 596 return result; 597 } 598 599 /*********************************************************************** 600 * 601 * smtp_perform_command() 602 * 603 * Sends an SMTP based command. 604 */ 605 static CURLcode smtp_perform_command(struct Curl_easy *data, 606 struct smtp_conn *smtpc, 607 struct SMTP *smtp) 608 { 609 CURLcode result = CURLE_OK; 610 611 if(smtp->rcpt) { 612 /* We notify the server we are sending UTF-8 data if a) it supports the 613 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in 614 either the local address or hostname parts. This is regardless of 615 whether the hostname is encoded using IDN ACE */ 616 bool utf8 = FALSE; 617 618 if((!smtp->custom) || (!smtp->custom[0])) { 619 char *address = NULL; 620 struct hostname host = { NULL, NULL, NULL, NULL }; 621 622 /* Parse the mailbox to verify into the local address and hostname 623 parts, converting the hostname to an IDN A-label if necessary */ 624 result = smtp_parse_address(smtp->rcpt->data, 625 &address, &host); 626 if(result) 627 return result; 628 629 /* Establish whether we should report SMTPUTF8 to the server for this 630 mailbox as per RFC-6531 sect. 3.1 point 6 */ 631 utf8 = (smtpc->utf8_supported) && 632 ((host.encalloc) || (!Curl_is_ASCII_name(address)) || 633 (!Curl_is_ASCII_name(host.name))); 634 635 /* Send the VRFY command (Note: The hostname part may be absent when the 636 host is a local system) */ 637 result = Curl_pp_sendf(data, &smtpc->pp, "VRFY %s%s%s%s", 638 address, 639 host.name ? "@" : "", 640 host.name ? host.name : "", 641 utf8 ? " SMTPUTF8" : ""); 642 643 Curl_free_idnconverted_hostname(&host); 644 free(address); 645 } 646 else { 647 /* Establish whether we should report that we support SMTPUTF8 for EXPN 648 commands to the server as per RFC-6531 sect. 3.1 point 6 */ 649 utf8 = (smtpc->utf8_supported) && (!strcmp(smtp->custom, "EXPN")); 650 651 /* Send the custom recipient based command such as the EXPN command */ 652 result = Curl_pp_sendf(data, &smtpc->pp, 653 "%s %s%s", smtp->custom, 654 smtp->rcpt->data, 655 utf8 ? " SMTPUTF8" : ""); 656 } 657 } 658 else 659 /* Send the non-recipient based command such as HELP */ 660 result = Curl_pp_sendf(data, &smtpc->pp, "%s", 661 smtp->custom && smtp->custom[0] != '\0' ? 662 smtp->custom : "HELP"); 663 664 if(!result) 665 smtp_state(data, smtpc, SMTP_COMMAND); 666 667 return result; 668 } 669 670 /*********************************************************************** 671 * 672 * smtp_perform_mail() 673 * 674 * Sends an MAIL command to initiate the upload of a message. 675 */ 676 static CURLcode smtp_perform_mail(struct Curl_easy *data, 677 struct smtp_conn *smtpc, 678 struct SMTP *smtp) 679 { 680 char *from = NULL; 681 char *auth = NULL; 682 char *size = NULL; 683 CURLcode result = CURLE_OK; 684 685 /* We notify the server we are sending UTF-8 data if a) it supports the 686 SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in 687 either the local address or hostname parts. This is regardless of 688 whether the hostname is encoded using IDN ACE */ 689 bool utf8 = FALSE; 690 691 /* Calculate the FROM parameter */ 692 if(data->set.str[STRING_MAIL_FROM]) { 693 char *address = NULL; 694 struct hostname host = { NULL, NULL, NULL, NULL }; 695 696 /* Parse the FROM mailbox into the local address and hostname parts, 697 converting the hostname to an IDN A-label if necessary */ 698 result = smtp_parse_address(data->set.str[STRING_MAIL_FROM], 699 &address, &host); 700 if(result) 701 goto out; 702 703 /* Establish whether we should report SMTPUTF8 to the server for this 704 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ 705 utf8 = (smtpc->utf8_supported) && 706 ((host.encalloc) || (!Curl_is_ASCII_name(address)) || 707 (!Curl_is_ASCII_name(host.name))); 708 709 if(host.name) { 710 from = aprintf("<%s@%s>", address, host.name); 711 712 Curl_free_idnconverted_hostname(&host); 713 } 714 else 715 /* An invalid mailbox was provided but we will simply let the server 716 worry about that and reply with a 501 error */ 717 from = aprintf("<%s>", address); 718 719 free(address); 720 } 721 else 722 /* Null reverse-path, RFC-5321, sect. 3.6.3 */ 723 from = strdup("<>"); 724 725 if(!from) { 726 result = CURLE_OUT_OF_MEMORY; 727 goto out; 728 } 729 730 /* Calculate the optional AUTH parameter */ 731 if(data->set.str[STRING_MAIL_AUTH] && smtpc->sasl.authused) { 732 if(data->set.str[STRING_MAIL_AUTH][0] != '\0') { 733 char *address = NULL; 734 struct hostname host = { NULL, NULL, NULL, NULL }; 735 736 /* Parse the AUTH mailbox into the local address and hostname parts, 737 converting the hostname to an IDN A-label if necessary */ 738 result = smtp_parse_address(data->set.str[STRING_MAIL_AUTH], 739 &address, &host); 740 if(result) 741 goto out; 742 743 /* Establish whether we should report SMTPUTF8 to the server for this 744 mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */ 745 if((!utf8) && (smtpc->utf8_supported) && 746 ((host.encalloc) || (!Curl_is_ASCII_name(address)) || 747 (!Curl_is_ASCII_name(host.name)))) 748 utf8 = TRUE; 749 750 if(host.name) { 751 auth = aprintf("<%s@%s>", address, host.name); 752 753 Curl_free_idnconverted_hostname(&host); 754 } 755 else 756 /* An invalid mailbox was provided but we will simply let the server 757 worry about it */ 758 auth = aprintf("<%s>", address); 759 free(address); 760 } 761 else 762 /* Empty AUTH, RFC-2554, sect. 5 */ 763 auth = strdup("<>"); 764 765 if(!auth) { 766 result = CURLE_OUT_OF_MEMORY; 767 goto out; 768 } 769 } 770 771 #ifndef CURL_DISABLE_MIME 772 /* Prepare the mime data if some. */ 773 if(data->set.mimepost.kind != MIMEKIND_NONE) { 774 /* Use the whole structure as data. */ 775 data->set.mimepost.flags &= ~(unsigned int)MIME_BODY_ONLY; 776 777 /* Add external headers and mime version. */ 778 curl_mime_headers(&data->set.mimepost, data->set.headers, 0); 779 result = Curl_mime_prepare_headers(data, &data->set.mimepost, NULL, 780 NULL, MIMESTRATEGY_MAIL); 781 782 if(!result) 783 if(!Curl_checkheaders(data, STRCONST("Mime-Version"))) 784 result = Curl_mime_add_header(&data->set.mimepost.curlheaders, 785 "Mime-Version: 1.0"); 786 787 if(!result) 788 result = Curl_creader_set_mime(data, &data->set.mimepost); 789 if(result) 790 goto out; 791 data->state.infilesize = Curl_creader_total_length(data); 792 } 793 else 794 #endif 795 { 796 result = Curl_creader_set_fread(data, data->state.infilesize); 797 if(result) 798 goto out; 799 } 800 801 /* Calculate the optional SIZE parameter */ 802 if(smtpc->size_supported && data->state.infilesize > 0) { 803 size = aprintf("%" FMT_OFF_T, data->state.infilesize); 804 805 if(!size) { 806 result = CURLE_OUT_OF_MEMORY; 807 goto out; 808 } 809 } 810 811 /* If the mailboxes in the FROM and AUTH parameters do not include a UTF-8 812 based address then quickly scan through the recipient list and check if 813 any there do, as we need to correctly identify our support for SMTPUTF8 814 in the envelope, as per RFC-6531 sect. 3.4 */ 815 if(smtpc->utf8_supported && !utf8) { 816 struct curl_slist *rcpt = smtp->rcpt; 817 818 while(rcpt && !utf8) { 819 /* Does the hostname contain non-ASCII characters? */ 820 if(!Curl_is_ASCII_name(rcpt->data)) 821 utf8 = TRUE; 822 823 rcpt = rcpt->next; 824 } 825 } 826 827 /* Add the client reader doing STMP EOB escaping */ 828 result = cr_eob_add(data); 829 if(result) 830 goto out; 831 832 /* Send the MAIL command */ 833 result = Curl_pp_sendf(data, &smtpc->pp, 834 "MAIL FROM:%s%s%s%s%s%s", 835 from, /* Mandatory */ 836 auth ? " AUTH=" : "", /* Optional on AUTH support */ 837 auth ? auth : "", /* */ 838 size ? " SIZE=" : "", /* Optional on SIZE support */ 839 size ? size : "", /* */ 840 utf8 ? " SMTPUTF8" /* Internationalised mailbox */ 841 : ""); /* included in our envelope */ 842 843 out: 844 free(from); 845 free(auth); 846 free(size); 847 848 if(!result) 849 smtp_state(data, smtpc, SMTP_MAIL); 850 851 return result; 852 } 853 854 /*********************************************************************** 855 * 856 * smtp_perform_rcpt_to() 857 * 858 * Sends a RCPT TO command for a given recipient as part of the message upload 859 * process. 860 */ 861 static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data, 862 struct smtp_conn *smtpc, 863 struct SMTP *smtp) 864 { 865 CURLcode result = CURLE_OK; 866 char *address = NULL; 867 struct hostname host = { NULL, NULL, NULL, NULL }; 868 869 /* Parse the recipient mailbox into the local address and hostname parts, 870 converting the hostname to an IDN A-label if necessary */ 871 result = smtp_parse_address(smtp->rcpt->data, 872 &address, &host); 873 if(result) 874 return result; 875 876 /* Send the RCPT TO command */ 877 if(host.name) 878 result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s@%s>", 879 address, host.name); 880 else 881 /* An invalid mailbox was provided but we will simply let the server worry 882 about that and reply with a 501 error */ 883 result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s>", address); 884 885 Curl_free_idnconverted_hostname(&host); 886 free(address); 887 888 if(!result) 889 smtp_state(data, smtpc, SMTP_RCPT); 890 891 return result; 892 } 893 894 /*********************************************************************** 895 * 896 * smtp_perform_quit() 897 * 898 * Performs the quit action prior to sclose() being called. 899 */ 900 static CURLcode smtp_perform_quit(struct Curl_easy *data, 901 struct smtp_conn *smtpc) 902 { 903 /* Send the QUIT command */ 904 CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "QUIT"); 905 906 if(!result) 907 smtp_state(data, smtpc, SMTP_QUIT); 908 909 return result; 910 } 911 912 /* For the initial server greeting */ 913 static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data, 914 struct smtp_conn *smtpc, 915 int smtpcode, 916 smtpstate instate) 917 { 918 CURLcode result = CURLE_OK; 919 (void)instate; /* no use for this yet */ 920 921 if(smtpcode/100 != 2) { 922 failf(data, "Got unexpected smtp-server response: %d", smtpcode); 923 result = CURLE_WEIRD_SERVER_REPLY; 924 } 925 else 926 result = smtp_perform_ehlo(data, smtpc); 927 928 return result; 929 } 930 931 /* For STARTTLS responses */ 932 static CURLcode smtp_state_starttls_resp(struct Curl_easy *data, 933 struct smtp_conn *smtpc, 934 int smtpcode, 935 smtpstate instate) 936 { 937 CURLcode result = CURLE_OK; 938 (void)instate; /* no use for this yet */ 939 940 /* Pipelining in response is forbidden. */ 941 if(smtpc->pp.overflow) 942 return CURLE_WEIRD_SERVER_REPLY; 943 944 if(smtpcode != 220) { 945 if(data->set.use_ssl != CURLUSESSL_TRY) { 946 failf(data, "STARTTLS denied, code %d", smtpcode); 947 result = CURLE_USE_SSL_FAILED; 948 } 949 else 950 result = smtp_perform_authentication(data, smtpc); 951 } 952 else 953 smtp_state(data, smtpc, SMTP_UPGRADETLS); 954 955 return result; 956 } 957 958 /* For EHLO responses */ 959 static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, 960 struct smtp_conn *smtpc, 961 int smtpcode, 962 smtpstate instate) 963 { 964 CURLcode result = CURLE_OK; 965 const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf); 966 size_t len = smtpc->pp.nfinal; 967 968 (void)instate; /* no use for this yet */ 969 970 if(smtpcode/100 != 2 && smtpcode != 1) { 971 if(data->set.use_ssl <= CURLUSESSL_TRY 972 || Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) 973 result = smtp_perform_helo(data, smtpc); 974 else { 975 failf(data, "Remote access denied: %d", smtpcode); 976 result = CURLE_REMOTE_ACCESS_DENIED; 977 } 978 } 979 else if(len >= 4) { 980 line += 4; 981 len -= 4; 982 983 /* Does the server support the STARTTLS capability? */ 984 if(len >= 8 && !memcmp(line, "STARTTLS", 8)) 985 smtpc->tls_supported = TRUE; 986 987 /* Does the server support the SIZE capability? */ 988 else if(len >= 4 && !memcmp(line, "SIZE", 4)) 989 smtpc->size_supported = TRUE; 990 991 /* Does the server support the UTF-8 capability? */ 992 else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8)) 993 smtpc->utf8_supported = TRUE; 994 995 /* Does the server support authentication? */ 996 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { 997 smtpc->auth_supported = TRUE; 998 999 /* Advance past the AUTH keyword */ 1000 line += 5; 1001 len -= 5; 1002 1003 /* Loop through the data line */ 1004 for(;;) { 1005 size_t llen; 1006 size_t wordlen; 1007 unsigned short mechbit; 1008 1009 while(len && 1010 (*line == ' ' || *line == '\t' || 1011 *line == '\r' || *line == '\n')) { 1012 1013 line++; 1014 len--; 1015 } 1016 1017 if(!len) 1018 break; 1019 1020 /* Extract the word */ 1021 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && 1022 line[wordlen] != '\t' && line[wordlen] != '\r' && 1023 line[wordlen] != '\n';) 1024 wordlen++; 1025 1026 /* Test the word for a matching authentication mechanism */ 1027 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); 1028 if(mechbit && llen == wordlen) 1029 smtpc->sasl.authmechs |= mechbit; 1030 1031 line += wordlen; 1032 len -= wordlen; 1033 } 1034 } 1035 1036 if(smtpcode != 1) { 1037 if(data->set.use_ssl && !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { 1038 /* We do not have an SSL/TLS connection yet, but SSL is requested */ 1039 if(smtpc->tls_supported) 1040 /* Switch to TLS connection now */ 1041 result = smtp_perform_starttls(data, smtpc); 1042 else if(data->set.use_ssl == CURLUSESSL_TRY) 1043 /* Fallback and carry on with authentication */ 1044 result = smtp_perform_authentication(data, smtpc); 1045 else { 1046 failf(data, "STARTTLS not supported."); 1047 result = CURLE_USE_SSL_FAILED; 1048 } 1049 } 1050 else 1051 result = smtp_perform_authentication(data, smtpc); 1052 } 1053 } 1054 else { 1055 failf(data, "Unexpectedly short EHLO response"); 1056 result = CURLE_WEIRD_SERVER_REPLY; 1057 } 1058 1059 return result; 1060 } 1061 1062 /* For HELO responses */ 1063 static CURLcode smtp_state_helo_resp(struct Curl_easy *data, 1064 struct smtp_conn *smtpc, 1065 int smtpcode, 1066 smtpstate instate) 1067 { 1068 CURLcode result = CURLE_OK; 1069 (void)instate; /* no use for this yet */ 1070 1071 if(smtpcode/100 != 2) { 1072 failf(data, "Remote access denied: %d", smtpcode); 1073 result = CURLE_REMOTE_ACCESS_DENIED; 1074 } 1075 else 1076 /* End of connect phase */ 1077 smtp_state(data, smtpc, SMTP_STOP); 1078 1079 return result; 1080 } 1081 1082 /* For SASL authentication responses */ 1083 static CURLcode smtp_state_auth_resp(struct Curl_easy *data, 1084 struct smtp_conn *smtpc, 1085 int smtpcode, 1086 smtpstate instate) 1087 { 1088 CURLcode result = CURLE_OK; 1089 saslprogress progress; 1090 1091 (void)instate; /* no use for this yet */ 1092 1093 result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress); 1094 if(!result) 1095 switch(progress) { 1096 case SASL_DONE: 1097 smtp_state(data, smtpc, SMTP_STOP); /* Authenticated */ 1098 break; 1099 case SASL_IDLE: /* No mechanism left after cancellation */ 1100 failf(data, "Authentication cancelled"); 1101 result = CURLE_LOGIN_DENIED; 1102 break; 1103 default: 1104 break; 1105 } 1106 1107 return result; 1108 } 1109 1110 /* For command responses */ 1111 static CURLcode smtp_state_command_resp(struct Curl_easy *data, 1112 struct smtp_conn *smtpc, 1113 struct SMTP *smtp, 1114 int smtpcode, 1115 smtpstate instate) 1116 { 1117 CURLcode result = CURLE_OK; 1118 char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf); 1119 size_t len = smtpc->pp.nfinal; 1120 1121 (void)instate; /* no use for this yet */ 1122 1123 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) || 1124 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) { 1125 failf(data, "Command failed: %d", smtpcode); 1126 result = CURLE_WEIRD_SERVER_REPLY; 1127 } 1128 else { 1129 if(!data->req.no_body) 1130 result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); 1131 1132 if(smtpcode != 1) { 1133 if(smtp->rcpt) { 1134 smtp->rcpt = smtp->rcpt->next; 1135 1136 if(smtp->rcpt) { 1137 /* Send the next command */ 1138 result = smtp_perform_command(data, smtpc, smtp); 1139 } 1140 else 1141 /* End of DO phase */ 1142 smtp_state(data, smtpc, SMTP_STOP); 1143 } 1144 else 1145 /* End of DO phase */ 1146 smtp_state(data, smtpc, SMTP_STOP); 1147 } 1148 } 1149 1150 return result; 1151 } 1152 1153 /* For MAIL responses */ 1154 static CURLcode smtp_state_mail_resp(struct Curl_easy *data, 1155 struct smtp_conn *smtpc, 1156 struct SMTP *smtp, 1157 int smtpcode, 1158 smtpstate instate) 1159 { 1160 CURLcode result = CURLE_OK; 1161 (void)instate; /* no use for this yet */ 1162 1163 if(smtpcode/100 != 2) { 1164 failf(data, "MAIL failed: %d", smtpcode); 1165 result = CURLE_SEND_ERROR; 1166 } 1167 else 1168 /* Start the RCPT TO command */ 1169 result = smtp_perform_rcpt_to(data, smtpc, smtp); 1170 1171 return result; 1172 } 1173 1174 /* For RCPT responses */ 1175 static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, 1176 struct smtp_conn *smtpc, 1177 struct SMTP *smtp, 1178 int smtpcode, 1179 smtpstate instate) 1180 { 1181 CURLcode result = CURLE_OK; 1182 bool is_smtp_err = FALSE; 1183 bool is_smtp_blocking_err = FALSE; 1184 1185 (void)instate; /* no use for this yet */ 1186 1187 is_smtp_err = (smtpcode/100 != 2); 1188 1189 /* If there is multiple RCPT TO to be issued, it is possible to ignore errors 1190 and proceed with only the valid addresses. */ 1191 is_smtp_blocking_err = (is_smtp_err && !data->set.mail_rcpt_allowfails); 1192 1193 if(is_smtp_err) { 1194 /* Remembering the last failure which we can report if all "RCPT TO" have 1195 failed and we cannot proceed. */ 1196 smtp->rcpt_last_error = smtpcode; 1197 1198 if(is_smtp_blocking_err) { 1199 failf(data, "RCPT failed: %d", smtpcode); 1200 result = CURLE_SEND_ERROR; 1201 } 1202 } 1203 else { 1204 /* Some RCPT TO commands have succeeded. */ 1205 smtp->rcpt_had_ok = TRUE; 1206 } 1207 1208 if(!is_smtp_blocking_err) { 1209 smtp->rcpt = smtp->rcpt->next; 1210 1211 if(smtp->rcpt) 1212 /* Send the next RCPT TO command */ 1213 result = smtp_perform_rcpt_to(data, smtpc, smtp); 1214 else { 1215 /* We were not able to issue a successful RCPT TO command while going 1216 over recipients (potentially multiple). Sending back last error. */ 1217 if(!smtp->rcpt_had_ok) { 1218 failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error); 1219 result = CURLE_SEND_ERROR; 1220 } 1221 else { 1222 /* Send the DATA command */ 1223 result = Curl_pp_sendf(data, &smtpc->pp, "%s", "DATA"); 1224 1225 if(!result) 1226 smtp_state(data, smtpc, SMTP_DATA); 1227 } 1228 } 1229 } 1230 1231 return result; 1232 } 1233 1234 /* For DATA response */ 1235 static CURLcode smtp_state_data_resp(struct Curl_easy *data, 1236 struct smtp_conn *smtpc, 1237 int smtpcode, 1238 smtpstate instate) 1239 { 1240 CURLcode result = CURLE_OK; 1241 (void)instate; /* no use for this yet */ 1242 1243 if(smtpcode != 354) { 1244 failf(data, "DATA failed: %d", smtpcode); 1245 result = CURLE_SEND_ERROR; 1246 } 1247 else { 1248 /* Set the progress upload size */ 1249 Curl_pgrsSetUploadSize(data, data->state.infilesize); 1250 1251 /* SMTP upload */ 1252 Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); 1253 1254 /* End of DO phase */ 1255 smtp_state(data, smtpc, SMTP_STOP); 1256 } 1257 1258 return result; 1259 } 1260 1261 /* For POSTDATA responses, which are received after the entire DATA 1262 part has been sent to the server */ 1263 static CURLcode smtp_state_postdata_resp(struct Curl_easy *data, 1264 struct smtp_conn *smtpc, 1265 int smtpcode, 1266 smtpstate instate) 1267 { 1268 CURLcode result = CURLE_OK; 1269 1270 (void)instate; /* no use for this yet */ 1271 1272 if(smtpcode != 250) 1273 result = CURLE_WEIRD_SERVER_REPLY; 1274 1275 /* End of DONE phase */ 1276 smtp_state(data, smtpc, SMTP_STOP); 1277 1278 return result; 1279 } 1280 1281 static CURLcode smtp_pp_statemachine(struct Curl_easy *data, 1282 struct connectdata *conn) 1283 { 1284 CURLcode result = CURLE_OK; 1285 int smtpcode; 1286 struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); 1287 struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY); 1288 size_t nread = 0; 1289 1290 if(!smtpc || !smtp) 1291 return CURLE_FAILED_INIT; 1292 1293 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ 1294 upgrade_tls: 1295 if(smtpc->state == SMTP_UPGRADETLS) { 1296 result = smtp_perform_upgrade_tls(data, smtpc); 1297 if(result || (smtpc->state == SMTP_UPGRADETLS)) 1298 return result; 1299 } 1300 1301 /* Flush any data that needs to be sent */ 1302 if(smtpc->pp.sendleft) 1303 return Curl_pp_flushsend(data, &smtpc->pp); 1304 1305 do { 1306 /* Read the response from the server */ 1307 result = Curl_pp_readresp(data, FIRSTSOCKET, &smtpc->pp, 1308 &smtpcode, &nread); 1309 if(result) 1310 return result; 1311 1312 /* Store the latest response for later retrieval if necessary */ 1313 if(smtpc->state != SMTP_QUIT && smtpcode != 1) 1314 data->info.httpcode = smtpcode; 1315 1316 if(!smtpcode) 1317 break; 1318 1319 /* We have now received a full SMTP server response */ 1320 switch(smtpc->state) { 1321 case SMTP_SERVERGREET: 1322 result = smtp_state_servergreet_resp(data, smtpc, 1323 smtpcode, smtpc->state); 1324 break; 1325 1326 case SMTP_EHLO: 1327 result = smtp_state_ehlo_resp(data, smtpc, smtpcode, smtpc->state); 1328 break; 1329 1330 case SMTP_HELO: 1331 result = smtp_state_helo_resp(data, smtpc, smtpcode, smtpc->state); 1332 break; 1333 1334 case SMTP_STARTTLS: 1335 result = smtp_state_starttls_resp(data, smtpc, smtpcode, smtpc->state); 1336 /* During UPGRADETLS, leave the read loop as we need to connect 1337 * (e.g. TLS handshake) before we continue sending/receiving. */ 1338 if(!result && (smtpc->state == SMTP_UPGRADETLS)) 1339 goto upgrade_tls; 1340 break; 1341 1342 case SMTP_AUTH: 1343 result = smtp_state_auth_resp(data, smtpc, smtpcode, smtpc->state); 1344 break; 1345 1346 case SMTP_COMMAND: 1347 result = smtp_state_command_resp(data, smtpc, smtp, 1348 smtpcode, smtpc->state); 1349 break; 1350 1351 case SMTP_MAIL: 1352 result = smtp_state_mail_resp(data, smtpc, smtp, smtpcode, smtpc->state); 1353 break; 1354 1355 case SMTP_RCPT: 1356 result = smtp_state_rcpt_resp(data, smtpc, smtp, smtpcode, smtpc->state); 1357 break; 1358 1359 case SMTP_DATA: 1360 result = smtp_state_data_resp(data, smtpc, smtpcode, smtpc->state); 1361 break; 1362 1363 case SMTP_POSTDATA: 1364 result = smtp_state_postdata_resp(data, smtpc, smtpcode, smtpc->state); 1365 break; 1366 1367 case SMTP_QUIT: 1368 default: 1369 /* internal error */ 1370 smtp_state(data, smtpc, SMTP_STOP); 1371 break; 1372 } 1373 } while(!result && smtpc->state != SMTP_STOP && 1374 Curl_pp_moredata(&smtpc->pp)); 1375 1376 return result; 1377 } 1378 1379 /* Called repeatedly until done from multi.c */ 1380 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done) 1381 { 1382 CURLcode result = CURLE_OK; 1383 struct smtp_conn *smtpc = 1384 Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); 1385 1386 *done = FALSE; 1387 if(!smtpc) 1388 return CURLE_FAILED_INIT; 1389 1390 result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE); 1391 *done = (smtpc->state == SMTP_STOP); 1392 return result; 1393 } 1394 1395 static CURLcode smtp_block_statemach(struct Curl_easy *data, 1396 struct smtp_conn *smtpc, 1397 bool disconnecting) 1398 { 1399 CURLcode result = CURLE_OK; 1400 1401 while(smtpc->state != SMTP_STOP && !result) 1402 result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting); 1403 1404 return result; 1405 } 1406 1407 /* For the SMTP "protocol connect" and "doing" phases only */ 1408 static int smtp_getsock(struct Curl_easy *data, 1409 struct connectdata *conn, curl_socket_t *socks) 1410 { 1411 struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); 1412 return smtpc ? 1413 Curl_pp_getsock(data, &smtpc->pp, socks) : GETSOCK_BLANK; 1414 } 1415 1416 /*********************************************************************** 1417 * 1418 * smtp_connect() 1419 * 1420 * This function should do everything that is to be considered a part of 1421 * the connection phase. 1422 * 1423 * The variable pointed to by 'done' will be TRUE if the protocol-layer 1424 * connect phase is done when this function returns, or FALSE if not. 1425 */ 1426 static CURLcode smtp_connect(struct Curl_easy *data, bool *done) 1427 { 1428 struct smtp_conn *smtpc = 1429 Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); 1430 CURLcode result = CURLE_OK; 1431 1432 *done = FALSE; /* default to not done yet */ 1433 if(!smtpc) 1434 return CURLE_FAILED_INIT; 1435 1436 /* We always support persistent connections in SMTP */ 1437 connkeep(data->conn, "SMTP default"); 1438 1439 PINGPONG_SETUP(&smtpc->pp, smtp_pp_statemachine, smtp_endofresp); 1440 1441 /* Initialize the SASL storage */ 1442 Curl_sasl_init(&smtpc->sasl, data, &saslsmtp); 1443 1444 /* Initialise the pingpong layer */ 1445 Curl_pp_init(&smtpc->pp); 1446 1447 /* Parse the URL options */ 1448 result = smtp_parse_url_options(data->conn, smtpc); 1449 if(result) 1450 return result; 1451 1452 /* Parse the URL path */ 1453 result = smtp_parse_url_path(data, smtpc); 1454 if(result) 1455 return result; 1456 1457 /* Start off waiting for the server greeting response */ 1458 smtp_state(data, smtpc, SMTP_SERVERGREET); 1459 1460 result = smtp_multi_statemach(data, done); 1461 1462 return result; 1463 } 1464 1465 /*********************************************************************** 1466 * 1467 * smtp_done() 1468 * 1469 * The DONE function. This does what needs to be done after a single DO has 1470 * performed. 1471 * 1472 * Input argument is already checked for validity. 1473 */ 1474 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, 1475 bool premature) 1476 { 1477 struct smtp_conn *smtpc = 1478 Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); 1479 CURLcode result = CURLE_OK; 1480 struct connectdata *conn = data->conn; 1481 struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY); 1482 1483 (void)premature; 1484 1485 if(!smtpc) 1486 return CURLE_FAILED_INIT; 1487 if(!smtp) 1488 return CURLE_OK; 1489 1490 /* Cleanup our per-request based variables */ 1491 Curl_safefree(smtp->custom); 1492 1493 if(status) { 1494 connclose(conn, "SMTP done with bad status"); /* marked for closure */ 1495 result = status; /* use the already set error code */ 1496 } 1497 else if(!data->set.connect_only && data->set.mail_rcpt && 1498 (data->state.upload || IS_MIME_POST(data))) { 1499 1500 smtp_state(data, smtpc, SMTP_POSTDATA); 1501 1502 /* Run the state-machine */ 1503 result = smtp_block_statemach(data, smtpc, FALSE); 1504 } 1505 1506 /* Clear the transfer mode for the next request */ 1507 smtp->transfer = PPTRANSFER_BODY; 1508 CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d", 1509 status, premature, result); 1510 return result; 1511 } 1512 1513 /*********************************************************************** 1514 * 1515 * smtp_perform() 1516 * 1517 * This is the actual DO function for SMTP. Transfer a mail, send a command 1518 * or get some data according to the options previously setup. 1519 */ 1520 static CURLcode smtp_perform(struct Curl_easy *data, 1521 struct smtp_conn *smtpc, 1522 struct SMTP *smtp, 1523 bool *connected, 1524 bool *dophase_done) 1525 { 1526 /* This is SMTP and no proxy */ 1527 CURLcode result = CURLE_OK; 1528 1529 CURL_TRC_SMTP(data, "smtp_perform(), start"); 1530 1531 if(data->req.no_body) { 1532 /* Requested no body means no transfer */ 1533 smtp->transfer = PPTRANSFER_INFO; 1534 } 1535 1536 *dophase_done = FALSE; /* not done yet */ 1537 1538 /* Store the first recipient (or NULL if not specified) */ 1539 smtp->rcpt = data->set.mail_rcpt; 1540 1541 /* Track of whether we have successfully sent at least one RCPT TO command */ 1542 smtp->rcpt_had_ok = FALSE; 1543 1544 /* Track of the last error we have received by sending RCPT TO command */ 1545 smtp->rcpt_last_error = 0; 1546 1547 /* Initial data character is the first character in line: it is implicitly 1548 preceded by a virtual CRLF. */ 1549 smtp->trailing_crlf = TRUE; 1550 smtp->eob = 2; 1551 1552 /* Start the first command in the DO phase */ 1553 if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt) 1554 /* MAIL transfer */ 1555 result = smtp_perform_mail(data, smtpc, smtp); 1556 else 1557 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */ 1558 result = smtp_perform_command(data, smtpc, smtp); 1559 1560 if(result) 1561 goto out; 1562 1563 /* Run the state-machine */ 1564 result = smtp_multi_statemach(data, dophase_done); 1565 1566 *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); 1567 1568 out: 1569 CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d", 1570 result, *connected, *dophase_done); 1571 return result; 1572 } 1573 1574 /*********************************************************************** 1575 * 1576 * smtp_do() 1577 * 1578 * This function is registered as 'curl_do' function. It decodes the path 1579 * parts etc as a wrapper to the actual DO function (smtp_perform). 1580 * 1581 * The input argument is already checked for validity. 1582 */ 1583 static CURLcode smtp_do(struct Curl_easy *data, bool *done) 1584 { 1585 struct smtp_conn *smtpc = 1586 Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); 1587 struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY); 1588 CURLcode result = CURLE_OK; 1589 1590 DEBUGASSERT(data); 1591 DEBUGASSERT(data->conn); 1592 *done = FALSE; /* default to false */ 1593 if(!smtpc || !smtp) 1594 return CURLE_FAILED_INIT; 1595 1596 /* Parse the custom request */ 1597 result = smtp_parse_custom_request(data, smtp); 1598 if(result) 1599 return result; 1600 1601 result = smtp_regular_transfer(data, smtpc, smtp, done); 1602 CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done); 1603 return result; 1604 } 1605 1606 /*********************************************************************** 1607 * 1608 * smtp_disconnect() 1609 * 1610 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection 1611 * resources. BLOCKING. 1612 */ 1613 static CURLcode smtp_disconnect(struct Curl_easy *data, 1614 struct connectdata *conn, 1615 bool dead_connection) 1616 { 1617 struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); 1618 1619 (void)data; 1620 if(!smtpc) 1621 return CURLE_FAILED_INIT; 1622 1623 /* We cannot send quit unconditionally. If this connection is stale or 1624 bad in any way, sending quit and waiting around here will make the 1625 disconnect wait in vain and cause more problems than we need to. */ 1626 1627 if(!dead_connection && conn->bits.protoconnstart && 1628 !Curl_pp_needs_flush(data, &smtpc->pp)) { 1629 if(!smtp_perform_quit(data, smtpc)) 1630 (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */ 1631 } 1632 1633 CURL_TRC_SMTP(data, "smtp_disconnect(), finished"); 1634 return CURLE_OK; 1635 } 1636 1637 /* Call this when the DO phase has completed */ 1638 static CURLcode smtp_dophase_done(struct Curl_easy *data, 1639 struct SMTP *smtp, 1640 bool connected) 1641 { 1642 (void)connected; 1643 1644 if(smtp->transfer != PPTRANSFER_BODY) 1645 /* no data to transfer */ 1646 Curl_xfer_setup_nop(data); 1647 1648 return CURLE_OK; 1649 } 1650 1651 /* Called from multi.c while DOing */ 1652 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) 1653 { 1654 struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY); 1655 CURLcode result; 1656 1657 if(!smtp) 1658 return CURLE_FAILED_INIT; 1659 result = smtp_multi_statemach(data, dophase_done); 1660 if(result) 1661 DEBUGF(infof(data, "DO phase failed")); 1662 else if(*dophase_done) { 1663 result = smtp_dophase_done(data, smtp, FALSE /* not connected */); 1664 1665 DEBUGF(infof(data, "DO phase is complete")); 1666 } 1667 1668 CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done); 1669 return result; 1670 } 1671 1672 /*********************************************************************** 1673 * 1674 * smtp_regular_transfer() 1675 * 1676 * The input argument is already checked for validity. 1677 * 1678 * Performs all commands done before a regular transfer between a local and a 1679 * remote host. 1680 */ 1681 static CURLcode smtp_regular_transfer(struct Curl_easy *data, 1682 struct smtp_conn *smtpc, 1683 struct SMTP *smtp, 1684 bool *dophase_done) 1685 { 1686 CURLcode result = CURLE_OK; 1687 bool connected = FALSE; 1688 1689 /* Make sure size is unknown at this point */ 1690 data->req.size = -1; 1691 1692 /* Set the progress data */ 1693 Curl_pgrsSetUploadCounter(data, 0); 1694 Curl_pgrsSetDownloadCounter(data, 0); 1695 Curl_pgrsSetUploadSize(data, -1); 1696 Curl_pgrsSetDownloadSize(data, -1); 1697 1698 /* Carry out the perform */ 1699 result = smtp_perform(data, smtpc, smtp, &connected, dophase_done); 1700 1701 /* Perform post DO phase operations if necessary */ 1702 if(!result && *dophase_done) 1703 result = smtp_dophase_done(data, smtp, connected); 1704 1705 CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d", 1706 result, *dophase_done); 1707 return result; 1708 } 1709 1710 1711 static void smtp_easy_dtor(void *key, size_t klen, void *entry) 1712 { 1713 struct SMTP *smtp = entry; 1714 (void)key; 1715 (void)klen; 1716 free(smtp); 1717 } 1718 1719 static void smtp_conn_dtor(void *key, size_t klen, void *entry) 1720 { 1721 struct smtp_conn *smtpc = entry; 1722 (void)key; 1723 (void)klen; 1724 Curl_pp_disconnect(&smtpc->pp); 1725 Curl_safefree(smtpc->domain); 1726 free(smtpc); 1727 } 1728 1729 static CURLcode smtp_setup_connection(struct Curl_easy *data, 1730 struct connectdata *conn) 1731 { 1732 struct smtp_conn *smtpc; 1733 struct SMTP *smtp; 1734 CURLcode result = CURLE_OK; 1735 1736 smtpc = calloc(1, sizeof(*smtpc)); 1737 if(!smtpc || 1738 Curl_conn_meta_set(conn, CURL_META_SMTP_CONN, smtpc, smtp_conn_dtor)) { 1739 result = CURLE_OUT_OF_MEMORY; 1740 goto out; 1741 } 1742 1743 smtp = calloc(1, sizeof(*smtp)); 1744 if(!smtp || 1745 Curl_meta_set(data, CURL_META_SMTP_EASY, smtp, smtp_easy_dtor)) 1746 result = CURLE_OUT_OF_MEMORY; 1747 1748 out: 1749 CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result); 1750 return result; 1751 } 1752 1753 /*********************************************************************** 1754 * 1755 * smtp_parse_url_options() 1756 * 1757 * Parse the URL login options. 1758 */ 1759 static CURLcode smtp_parse_url_options(struct connectdata *conn, 1760 struct smtp_conn *smtpc) 1761 { 1762 CURLcode result = CURLE_OK; 1763 const char *ptr = conn->options; 1764 1765 while(!result && ptr && *ptr) { 1766 const char *key = ptr; 1767 const char *value; 1768 1769 while(*ptr && *ptr != '=') 1770 ptr++; 1771 1772 value = ptr + 1; 1773 1774 while(*ptr && *ptr != ';') 1775 ptr++; 1776 1777 if(curl_strnequal(key, "AUTH=", 5)) 1778 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, 1779 value, ptr - value); 1780 else 1781 result = CURLE_URL_MALFORMAT; 1782 1783 if(*ptr == ';') 1784 ptr++; 1785 } 1786 1787 return result; 1788 } 1789 1790 /*********************************************************************** 1791 * 1792 * smtp_parse_url_path() 1793 * 1794 * Parse the URL path into separate path components. 1795 */ 1796 static CURLcode smtp_parse_url_path(struct Curl_easy *data, 1797 struct smtp_conn *smtpc) 1798 { 1799 /* The SMTP struct is already initialised in smtp_connect() */ 1800 const char *path = &data->state.up.path[1]; /* skip leading path */ 1801 char localhost[HOSTNAME_MAX + 1]; 1802 1803 /* Calculate the path if necessary */ 1804 if(!*path) { 1805 if(!Curl_gethostname(localhost, sizeof(localhost))) 1806 path = localhost; 1807 else 1808 path = "localhost"; 1809 } 1810 1811 /* URL decode the path and use it as the domain in our EHLO */ 1812 return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL); 1813 } 1814 1815 /*********************************************************************** 1816 * 1817 * smtp_parse_custom_request() 1818 * 1819 * Parse the custom request. 1820 */ 1821 static CURLcode smtp_parse_custom_request(struct Curl_easy *data, 1822 struct SMTP *smtp) 1823 { 1824 CURLcode result = CURLE_OK; 1825 const char *custom = data->set.str[STRING_CUSTOMREQUEST]; 1826 1827 /* URL decode the custom request */ 1828 if(custom) 1829 result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL); 1830 1831 return result; 1832 } 1833 1834 /*********************************************************************** 1835 * 1836 * smtp_parse_address() 1837 * 1838 * Parse the fully qualified mailbox address into a local address part and the 1839 * hostname, converting the hostname to an IDN A-label, as per RFC-5890, if 1840 * necessary. 1841 * 1842 * Parameters: 1843 * 1844 * conn [in] - The connection handle. 1845 * fqma [in] - The fully qualified mailbox address (which may or 1846 * may not contain UTF-8 characters). 1847 * address [in/out] - A new allocated buffer which holds the local 1848 * address part of the mailbox. This buffer must be 1849 * free'ed by the caller. 1850 * host [in/out] - The hostname structure that holds the original, 1851 * and optionally encoded, hostname. 1852 * Curl_free_idnconverted_hostname() must be called 1853 * once the caller has finished with the structure. 1854 * 1855 * Returns CURLE_OK on success. 1856 * 1857 * Notes: 1858 * 1859 * Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor 1860 * that conversion then we shall return success. This allow the caller to send 1861 * the data to the server as a U-label (as per RFC-6531 sect. 3.2). 1862 * 1863 * If an mailbox '@' separator cannot be located then the mailbox is considered 1864 * to be either a local mailbox or an invalid mailbox (depending on what the 1865 * calling function deems it to be) then the input will simply be returned in 1866 * the address part with the hostname being NULL. 1867 */ 1868 static CURLcode smtp_parse_address(const char *fqma, char **address, 1869 struct hostname *host) 1870 { 1871 CURLcode result = CURLE_OK; 1872 size_t length; 1873 1874 /* Duplicate the fully qualified email address so we can manipulate it, 1875 ensuring it does not contain the delimiters if specified */ 1876 char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma); 1877 if(!dup) 1878 return CURLE_OUT_OF_MEMORY; 1879 1880 length = strlen(dup); 1881 if(length) { 1882 if(dup[length - 1] == '>') 1883 dup[length - 1] = '\0'; 1884 } 1885 1886 /* Extract the hostname from the address (if we can) */ 1887 host->name = strpbrk(dup, "@"); 1888 if(host->name) { 1889 *host->name = '\0'; 1890 host->name = host->name + 1; 1891 1892 /* Attempt to convert the hostname to IDN ACE */ 1893 (void) Curl_idnconvert_hostname(host); 1894 1895 /* If Curl_idnconvert_hostname() fails then we shall attempt to continue 1896 and send the hostname using UTF-8 rather than as 7-bit ACE (which is 1897 our preference) */ 1898 } 1899 1900 /* Extract the local address from the mailbox */ 1901 *address = dup; 1902 1903 return result; 1904 } 1905 1906 struct cr_eob_ctx { 1907 struct Curl_creader super; 1908 struct bufq buf; 1909 size_t n_eob; /* how many EOB bytes we matched so far */ 1910 size_t eob; /* Number of bytes of the EOB (End Of Body) that 1911 have been received so far */ 1912 BIT(read_eos); /* we read an EOS from the next reader */ 1913 BIT(eos); /* we have returned an EOS */ 1914 }; 1915 1916 static CURLcode cr_eob_init(struct Curl_easy *data, 1917 struct Curl_creader *reader) 1918 { 1919 struct cr_eob_ctx *ctx = reader->ctx; 1920 (void)data; 1921 /* The first char we read is the first on a line, as if we had 1922 * read CRLF just before */ 1923 ctx->n_eob = 2; 1924 Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT); 1925 return CURLE_OK; 1926 } 1927 1928 static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader) 1929 { 1930 struct cr_eob_ctx *ctx = reader->ctx; 1931 (void)data; 1932 Curl_bufq_free(&ctx->buf); 1933 } 1934 1935 /* this is the 5-bytes End-Of-Body marker for SMTP */ 1936 #define SMTP_EOB "\r\n.\r\n" 1937 #define SMTP_EOB_FIND_LEN 3 1938 1939 /* client reader doing SMTP End-Of-Body escaping. */ 1940 static CURLcode cr_eob_read(struct Curl_easy *data, 1941 struct Curl_creader *reader, 1942 char *buf, size_t blen, 1943 size_t *pnread, bool *peos) 1944 { 1945 struct cr_eob_ctx *ctx = reader->ctx; 1946 CURLcode result = CURLE_OK; 1947 size_t nread, i, start, n; 1948 bool eos; 1949 1950 if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { 1951 /* Get more and convert it when needed */ 1952 result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); 1953 if(result) 1954 return result; 1955 1956 ctx->read_eos = eos; 1957 if(nread) { 1958 if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) { 1959 /* not in the middle of a match, no EOB start found, just pass */ 1960 *pnread = nread; 1961 *peos = FALSE; 1962 return CURLE_OK; 1963 } 1964 /* scan for EOB (continuation) and convert */ 1965 for(i = start = 0; i < nread; ++i) { 1966 if(ctx->n_eob >= SMTP_EOB_FIND_LEN) { 1967 /* matched the EOB prefix and seeing additional char, add '.' */ 1968 result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n); 1969 if(result) 1970 return result; 1971 result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n); 1972 if(result) 1973 return result; 1974 ctx->n_eob = 0; 1975 start = i; 1976 if(data->state.infilesize > 0) 1977 data->state.infilesize++; 1978 } 1979 1980 if(buf[i] != SMTP_EOB[ctx->n_eob]) 1981 ctx->n_eob = 0; 1982 1983 if(buf[i] == SMTP_EOB[ctx->n_eob]) { 1984 /* matching another char of the EOB */ 1985 ++ctx->n_eob; 1986 } 1987 } 1988 1989 /* add any remainder to buf */ 1990 if(start < nread) { 1991 result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n); 1992 if(result) 1993 return result; 1994 } 1995 } 1996 1997 if(ctx->read_eos) { 1998 /* if we last matched a CRLF or if the data was empty, add ".\r\n" 1999 * to end the body. If we sent something and it did not end with "\r\n", 2000 * add "\r\n.\r\n" to end the body */ 2001 const char *eob = SMTP_EOB; 2002 switch(ctx->n_eob) { 2003 case 2: 2004 /* seen a CRLF at the end, just add the remainder */ 2005 eob = &SMTP_EOB[2]; 2006 break; 2007 case 3: 2008 /* ended with '\r\n.', we should escape the last '.' */ 2009 eob = "." SMTP_EOB; 2010 break; 2011 default: 2012 break; 2013 } 2014 result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n); 2015 if(result) 2016 return result; 2017 } 2018 } 2019 2020 *peos = FALSE; 2021 if(!Curl_bufq_is_empty(&ctx->buf)) { 2022 result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread); 2023 } 2024 else 2025 *pnread = 0; 2026 2027 if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) { 2028 /* no more data, read all, done. */ 2029 ctx->eos = TRUE; 2030 } 2031 *peos = ctx->eos; 2032 DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d", 2033 blen, result, *pnread, *peos)); 2034 return result; 2035 } 2036 2037 static curl_off_t cr_eob_total_length(struct Curl_easy *data, 2038 struct Curl_creader *reader) 2039 { 2040 /* this reader changes length depending on input */ 2041 (void)data; 2042 (void)reader; 2043 return -1; 2044 } 2045 2046 static const struct Curl_crtype cr_eob = { 2047 "cr-smtp-eob", 2048 cr_eob_init, 2049 cr_eob_read, 2050 cr_eob_close, 2051 Curl_creader_def_needs_rewind, 2052 cr_eob_total_length, 2053 Curl_creader_def_resume_from, 2054 Curl_creader_def_rewind, 2055 Curl_creader_def_unpause, 2056 Curl_creader_def_is_paused, 2057 Curl_creader_def_done, 2058 sizeof(struct cr_eob_ctx) 2059 }; 2060 2061 static CURLcode cr_eob_add(struct Curl_easy *data) 2062 { 2063 struct Curl_creader *reader = NULL; 2064 CURLcode result; 2065 2066 result = Curl_creader_create(&reader, data, &cr_eob, 2067 CURL_CR_CONTENT_ENCODE); 2068 if(!result) 2069 result = Curl_creader_add(data, reader); 2070 2071 if(result && reader) 2072 Curl_creader_free(data, reader); 2073 return result; 2074 } 2075 2076 #endif /* CURL_DISABLE_SMTP */