Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/smtp.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1998 - 2019, 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.haxx.se/docs/copyright.html. | |
| 13 * | |
| 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell | |
| 15 * copies of the Software, and permit persons to whom the Software is | |
| 16 * furnished to do so, under the terms of the COPYING file. | |
| 17 * | |
| 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | |
| 19 * KIND, either express or implied. | |
| 20 * | |
| 21 * RFC1870 SMTP Service Extension for Message Size | |
| 22 * RFC2195 CRAM-MD5 authentication | |
| 23 * RFC2831 DIGEST-MD5 authentication | |
| 24 * RFC3207 SMTP over TLS | |
| 25 * RFC4422 Simple Authentication and Security Layer (SASL) | |
| 26 * RFC4616 PLAIN authentication | |
| 27 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism | |
| 28 * RFC4954 SMTP Authentication | |
| 29 * RFC5321 SMTP protocol | |
| 30 * RFC6749 OAuth 2.0 Authorization Framework | |
| 31 * RFC8314 Use of TLS for Email Submission and Access | |
| 32 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt> | |
| 33 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> | |
| 34 * | |
| 35 ***************************************************************************/ | |
| 36 | |
| 37 #include "curl_setup.h" | |
| 38 | |
| 39 #ifndef CURL_DISABLE_SMTP | |
| 40 | |
| 41 #ifdef HAVE_NETINET_IN_H | |
| 42 #include <netinet/in.h> | |
| 43 #endif | |
| 44 #ifdef HAVE_ARPA_INET_H | |
| 45 #include <arpa/inet.h> | |
| 46 #endif | |
| 47 #ifdef HAVE_UTSNAME_H | |
| 48 #include <sys/utsname.h> | |
| 49 #endif | |
| 50 #ifdef HAVE_NETDB_H | |
| 51 #include <netdb.h> | |
| 52 #endif | |
| 53 #ifdef __VMS | |
| 54 #include <in.h> | |
| 55 #include <inet.h> | |
| 56 #endif | |
| 57 | |
| 58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__)) | |
| 59 #undef in_addr_t | |
| 60 #define in_addr_t unsigned long | |
| 61 #endif | |
| 62 | |
| 63 #include <curl/curl.h> | |
| 64 #include "urldata.h" | |
| 65 #include "sendf.h" | |
| 66 #include "hostip.h" | |
| 67 #include "progress.h" | |
| 68 #include "transfer.h" | |
| 69 #include "escape.h" | |
| 70 #include "http.h" /* for HTTP proxy tunnel stuff */ | |
| 71 #include "mime.h" | |
| 72 #include "socks.h" | |
| 73 #include "smtp.h" | |
| 74 #include "strtoofft.h" | |
| 75 #include "strcase.h" | |
| 76 #include "vtls/vtls.h" | |
| 77 #include "connect.h" | |
| 78 #include "strerror.h" | |
| 79 #include "select.h" | |
| 80 #include "multiif.h" | |
| 81 #include "url.h" | |
| 82 #include "curl_gethostname.h" | |
| 83 #include "curl_sasl.h" | |
| 84 #include "warnless.h" | |
| 85 /* The last 3 #include files should be in this order */ | |
| 86 #include "curl_printf.h" | |
| 87 #include "curl_memory.h" | |
| 88 #include "memdebug.h" | |
| 89 | |
| 90 /* Local API functions */ | |
| 91 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done); | |
| 92 static CURLcode smtp_do(struct connectdata *conn, bool *done); | |
| 93 static CURLcode smtp_done(struct connectdata *conn, CURLcode status, | |
| 94 bool premature); | |
| 95 static CURLcode smtp_connect(struct connectdata *conn, bool *done); | |
| 96 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead); | |
| 97 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done); | |
| 98 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks); | |
| 99 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done); | |
| 100 static CURLcode smtp_setup_connection(struct connectdata *conn); | |
| 101 static CURLcode smtp_parse_url_options(struct connectdata *conn); | |
| 102 static CURLcode smtp_parse_url_path(struct connectdata *conn); | |
| 103 static CURLcode smtp_parse_custom_request(struct connectdata *conn); | |
| 104 static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech, | |
| 105 const char *initresp); | |
| 106 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp); | |
| 107 static void smtp_get_message(char *buffer, char **outptr); | |
| 108 | |
| 109 /* | |
| 110 * SMTP protocol handler. | |
| 111 */ | |
| 112 | |
| 113 const struct Curl_handler Curl_handler_smtp = { | |
| 114 "SMTP", /* scheme */ | |
| 115 smtp_setup_connection, /* setup_connection */ | |
| 116 smtp_do, /* do_it */ | |
| 117 smtp_done, /* done */ | |
| 118 ZERO_NULL, /* do_more */ | |
| 119 smtp_connect, /* connect_it */ | |
| 120 smtp_multi_statemach, /* connecting */ | |
| 121 smtp_doing, /* doing */ | |
| 122 smtp_getsock, /* proto_getsock */ | |
| 123 smtp_getsock, /* doing_getsock */ | |
| 124 ZERO_NULL, /* domore_getsock */ | |
| 125 ZERO_NULL, /* perform_getsock */ | |
| 126 smtp_disconnect, /* disconnect */ | |
| 127 ZERO_NULL, /* readwrite */ | |
| 128 ZERO_NULL, /* connection_check */ | |
| 129 PORT_SMTP, /* defport */ | |
| 130 CURLPROTO_SMTP, /* protocol */ | |
| 131 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ | |
| 132 PROTOPT_URLOPTIONS | |
| 133 }; | |
| 134 | |
| 135 #ifdef USE_SSL | |
| 136 /* | |
| 137 * SMTPS protocol handler. | |
| 138 */ | |
| 139 | |
| 140 const struct Curl_handler Curl_handler_smtps = { | |
| 141 "SMTPS", /* scheme */ | |
| 142 smtp_setup_connection, /* setup_connection */ | |
| 143 smtp_do, /* do_it */ | |
| 144 smtp_done, /* done */ | |
| 145 ZERO_NULL, /* do_more */ | |
| 146 smtp_connect, /* connect_it */ | |
| 147 smtp_multi_statemach, /* connecting */ | |
| 148 smtp_doing, /* doing */ | |
| 149 smtp_getsock, /* proto_getsock */ | |
| 150 smtp_getsock, /* doing_getsock */ | |
| 151 ZERO_NULL, /* domore_getsock */ | |
| 152 ZERO_NULL, /* perform_getsock */ | |
| 153 smtp_disconnect, /* disconnect */ | |
| 154 ZERO_NULL, /* readwrite */ | |
| 155 ZERO_NULL, /* connection_check */ | |
| 156 PORT_SMTPS, /* defport */ | |
| 157 CURLPROTO_SMTPS, /* protocol */ | |
| 158 PROTOPT_CLOSEACTION | PROTOPT_SSL | |
| 159 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */ | |
| 160 }; | |
| 161 #endif | |
| 162 | |
| 163 /* SASL parameters for the smtp protocol */ | |
| 164 static const struct SASLproto saslsmtp = { | |
| 165 "smtp", /* The service name */ | |
| 166 334, /* Code received when continuation is expected */ | |
| 167 235, /* Code to receive upon authentication success */ | |
| 168 512 - 8, /* Maximum initial response length (no max) */ | |
| 169 smtp_perform_auth, /* Send authentication command */ | |
| 170 smtp_continue_auth, /* Send authentication continuation */ | |
| 171 smtp_get_message /* Get SASL response message */ | |
| 172 }; | |
| 173 | |
| 174 #ifdef USE_SSL | |
| 175 static void smtp_to_smtps(struct connectdata *conn) | |
| 176 { | |
| 177 /* Change the connection handler */ | |
| 178 conn->handler = &Curl_handler_smtps; | |
| 179 | |
| 180 /* Set the connection's upgraded to TLS flag */ | |
| 181 conn->tls_upgraded = TRUE; | |
| 182 } | |
| 183 #else | |
| 184 #define smtp_to_smtps(x) Curl_nop_stmt | |
| 185 #endif | |
| 186 | |
| 187 /*********************************************************************** | |
| 188 * | |
| 189 * smtp_endofresp() | |
| 190 * | |
| 191 * Checks for an ending SMTP status code at the start of the given string, but | |
| 192 * also detects various capabilities from the EHLO response including the | |
| 193 * supported authentication mechanisms. | |
| 194 */ | |
| 195 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len, | |
| 196 int *resp) | |
| 197 { | |
| 198 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 199 bool result = FALSE; | |
| 200 | |
| 201 /* Nothing for us */ | |
| 202 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2])) | |
| 203 return FALSE; | |
| 204 | |
| 205 /* Do we have a command response? This should be the response code followed | |
| 206 by a space and optionally some text as per RFC-5321 and as outlined in | |
| 207 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and | |
| 208 only send the response code instead as per Section 4.2. */ | |
| 209 if(line[3] == ' ' || len == 5) { | |
| 210 char tmpline[6]; | |
| 211 | |
| 212 result = TRUE; | |
| 213 memset(tmpline, '\0', sizeof(tmpline)); | |
| 214 memcpy(tmpline, line, (len == 5 ? 5 : 3)); | |
| 215 *resp = curlx_sltosi(strtol(tmpline, NULL, 10)); | |
| 216 | |
| 217 /* Make sure real server never sends internal value */ | |
| 218 if(*resp == 1) | |
| 219 *resp = 0; | |
| 220 } | |
| 221 /* Do we have a multiline (continuation) response? */ | |
| 222 else if(line[3] == '-' && | |
| 223 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { | |
| 224 result = TRUE; | |
| 225 *resp = 1; /* Internal response code */ | |
| 226 } | |
| 227 | |
| 228 return result; | |
| 229 } | |
| 230 | |
| 231 /*********************************************************************** | |
| 232 * | |
| 233 * smtp_get_message() | |
| 234 * | |
| 235 * Gets the authentication message from the response buffer. | |
| 236 */ | |
| 237 static void smtp_get_message(char *buffer, char **outptr) | |
| 238 { | |
| 239 size_t len = strlen(buffer); | |
| 240 char *message = NULL; | |
| 241 | |
| 242 if(len > 4) { | |
| 243 /* Find the start of the message */ | |
| 244 len -= 4; | |
| 245 for(message = buffer + 4; *message == ' ' || *message == '\t'; | |
| 246 message++, len--) | |
| 247 ; | |
| 248 | |
| 249 /* Find the end of the message */ | |
| 250 for(; len--;) | |
| 251 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && | |
| 252 message[len] != '\t') | |
| 253 break; | |
| 254 | |
| 255 /* Terminate the message */ | |
| 256 if(++len) { | |
| 257 message[len] = '\0'; | |
| 258 } | |
| 259 } | |
| 260 else | |
| 261 /* junk input => zero length output */ | |
| 262 message = &buffer[len]; | |
| 263 | |
| 264 *outptr = message; | |
| 265 } | |
| 266 | |
| 267 /*********************************************************************** | |
| 268 * | |
| 269 * state() | |
| 270 * | |
| 271 * This is the ONLY way to change SMTP state! | |
| 272 */ | |
| 273 static void state(struct connectdata *conn, smtpstate newstate) | |
| 274 { | |
| 275 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 276 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) | |
| 277 /* for debug purposes */ | |
| 278 static const char * const names[] = { | |
| 279 "STOP", | |
| 280 "SERVERGREET", | |
| 281 "EHLO", | |
| 282 "HELO", | |
| 283 "STARTTLS", | |
| 284 "UPGRADETLS", | |
| 285 "AUTH", | |
| 286 "COMMAND", | |
| 287 "MAIL", | |
| 288 "RCPT", | |
| 289 "DATA", | |
| 290 "POSTDATA", | |
| 291 "QUIT", | |
| 292 /* LAST */ | |
| 293 }; | |
| 294 | |
| 295 if(smtpc->state != newstate) | |
| 296 infof(conn->data, "SMTP %p state change from %s to %s\n", | |
| 297 (void *)smtpc, names[smtpc->state], names[newstate]); | |
| 298 #endif | |
| 299 | |
| 300 smtpc->state = newstate; | |
| 301 } | |
| 302 | |
| 303 /*********************************************************************** | |
| 304 * | |
| 305 * smtp_perform_ehlo() | |
| 306 * | |
| 307 * Sends the EHLO command to not only initialise communication with the ESMTP | |
| 308 * server but to also obtain a list of server side supported capabilities. | |
| 309 */ | |
| 310 static CURLcode smtp_perform_ehlo(struct connectdata *conn) | |
| 311 { | |
| 312 CURLcode result = CURLE_OK; | |
| 313 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 314 | |
| 315 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */ | |
| 316 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism | |
| 317 used for esmtp connections */ | |
| 318 smtpc->tls_supported = FALSE; /* Clear the TLS capability */ | |
| 319 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */ | |
| 320 | |
| 321 /* Send the EHLO command */ | |
| 322 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain); | |
| 323 | |
| 324 if(!result) | |
| 325 state(conn, SMTP_EHLO); | |
| 326 | |
| 327 return result; | |
| 328 } | |
| 329 | |
| 330 /*********************************************************************** | |
| 331 * | |
| 332 * smtp_perform_helo() | |
| 333 * | |
| 334 * Sends the HELO command to initialise communication with the SMTP server. | |
| 335 */ | |
| 336 static CURLcode smtp_perform_helo(struct connectdata *conn) | |
| 337 { | |
| 338 CURLcode result = CURLE_OK; | |
| 339 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 340 | |
| 341 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used | |
| 342 in smtp connections */ | |
| 343 | |
| 344 /* Send the HELO command */ | |
| 345 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain); | |
| 346 | |
| 347 if(!result) | |
| 348 state(conn, SMTP_HELO); | |
| 349 | |
| 350 return result; | |
| 351 } | |
| 352 | |
| 353 /*********************************************************************** | |
| 354 * | |
| 355 * smtp_perform_starttls() | |
| 356 * | |
| 357 * Sends the STLS command to start the upgrade to TLS. | |
| 358 */ | |
| 359 static CURLcode smtp_perform_starttls(struct connectdata *conn) | |
| 360 { | |
| 361 /* Send the STARTTLS command */ | |
| 362 CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS"); | |
| 363 | |
| 364 if(!result) | |
| 365 state(conn, SMTP_STARTTLS); | |
| 366 | |
| 367 return result; | |
| 368 } | |
| 369 | |
| 370 /*********************************************************************** | |
| 371 * | |
| 372 * smtp_perform_upgrade_tls() | |
| 373 * | |
| 374 * Performs the upgrade to TLS. | |
| 375 */ | |
| 376 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn) | |
| 377 { | |
| 378 /* Start the SSL connection */ | |
| 379 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 380 CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, | |
| 381 &smtpc->ssldone); | |
| 382 | |
| 383 if(!result) { | |
| 384 if(smtpc->state != SMTP_UPGRADETLS) | |
| 385 state(conn, SMTP_UPGRADETLS); | |
| 386 | |
| 387 if(smtpc->ssldone) { | |
| 388 smtp_to_smtps(conn); | |
| 389 result = smtp_perform_ehlo(conn); | |
| 390 } | |
| 391 } | |
| 392 | |
| 393 return result; | |
| 394 } | |
| 395 | |
| 396 /*********************************************************************** | |
| 397 * | |
| 398 * smtp_perform_auth() | |
| 399 * | |
| 400 * Sends an AUTH command allowing the client to login with the given SASL | |
| 401 * authentication mechanism. | |
| 402 */ | |
| 403 static CURLcode smtp_perform_auth(struct connectdata *conn, | |
| 404 const char *mech, | |
| 405 const char *initresp) | |
| 406 { | |
| 407 CURLcode result = CURLE_OK; | |
| 408 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 409 | |
| 410 if(initresp) { /* AUTH <mech> ...<crlf> */ | |
| 411 /* Send the AUTH command with the initial response */ | |
| 412 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp); | |
| 413 } | |
| 414 else { | |
| 415 /* Send the AUTH command */ | |
| 416 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech); | |
| 417 } | |
| 418 | |
| 419 return result; | |
| 420 } | |
| 421 | |
| 422 /*********************************************************************** | |
| 423 * | |
| 424 * smtp_continue_auth() | |
| 425 * | |
| 426 * Sends SASL continuation data or cancellation. | |
| 427 */ | |
| 428 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp) | |
| 429 { | |
| 430 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 431 | |
| 432 return Curl_pp_sendf(&smtpc->pp, "%s", resp); | |
| 433 } | |
| 434 | |
| 435 /*********************************************************************** | |
| 436 * | |
| 437 * smtp_perform_authentication() | |
| 438 * | |
| 439 * Initiates the authentication sequence, with the appropriate SASL | |
| 440 * authentication mechanism. | |
| 441 */ | |
| 442 static CURLcode smtp_perform_authentication(struct connectdata *conn) | |
| 443 { | |
| 444 CURLcode result = CURLE_OK; | |
| 445 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 446 saslprogress progress; | |
| 447 | |
| 448 /* Check we have enough data to authenticate with, and the | |
| 449 server supports authentiation, and end the connect phase if not */ | |
| 450 if(!smtpc->auth_supported || | |
| 451 !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) { | |
| 452 state(conn, SMTP_STOP); | |
| 453 return result; | |
| 454 } | |
| 455 | |
| 456 /* Calculate the SASL login details */ | |
| 457 result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress); | |
| 458 | |
| 459 if(!result) { | |
| 460 if(progress == SASL_INPROGRESS) | |
| 461 state(conn, SMTP_AUTH); | |
| 462 else { | |
| 463 /* Other mechanisms not supported */ | |
| 464 infof(conn->data, "No known authentication mechanisms supported!\n"); | |
| 465 result = CURLE_LOGIN_DENIED; | |
| 466 } | |
| 467 } | |
| 468 | |
| 469 return result; | |
| 470 } | |
| 471 | |
| 472 /*********************************************************************** | |
| 473 * | |
| 474 * smtp_perform_command() | |
| 475 * | |
| 476 * Sends a SMTP based command. | |
| 477 */ | |
| 478 static CURLcode smtp_perform_command(struct connectdata *conn) | |
| 479 { | |
| 480 CURLcode result = CURLE_OK; | |
| 481 struct Curl_easy *data = conn->data; | |
| 482 struct SMTP *smtp = data->req.protop; | |
| 483 | |
| 484 /* Send the command */ | |
| 485 if(smtp->rcpt) | |
| 486 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s", | |
| 487 smtp->custom && smtp->custom[0] != '\0' ? | |
| 488 smtp->custom : "VRFY", | |
| 489 smtp->rcpt->data); | |
| 490 else | |
| 491 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", | |
| 492 smtp->custom && smtp->custom[0] != '\0' ? | |
| 493 smtp->custom : "HELP"); | |
| 494 | |
| 495 if(!result) | |
| 496 state(conn, SMTP_COMMAND); | |
| 497 | |
| 498 return result; | |
| 499 } | |
| 500 | |
| 501 /*********************************************************************** | |
| 502 * | |
| 503 * smtp_perform_mail() | |
| 504 * | |
| 505 * Sends an MAIL command to initiate the upload of a message. | |
| 506 */ | |
| 507 static CURLcode smtp_perform_mail(struct connectdata *conn) | |
| 508 { | |
| 509 char *from = NULL; | |
| 510 char *auth = NULL; | |
| 511 char *size = NULL; | |
| 512 CURLcode result = CURLE_OK; | |
| 513 struct Curl_easy *data = conn->data; | |
| 514 | |
| 515 /* Calculate the FROM parameter */ | |
| 516 if(!data->set.str[STRING_MAIL_FROM]) | |
| 517 /* Null reverse-path, RFC-5321, sect. 3.6.3 */ | |
| 518 from = strdup("<>"); | |
| 519 else if(data->set.str[STRING_MAIL_FROM][0] == '<') | |
| 520 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]); | |
| 521 else | |
| 522 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]); | |
| 523 | |
| 524 if(!from) | |
| 525 return CURLE_OUT_OF_MEMORY; | |
| 526 | |
| 527 /* Calculate the optional AUTH parameter */ | |
| 528 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) { | |
| 529 if(data->set.str[STRING_MAIL_AUTH][0] != '\0') | |
| 530 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]); | |
| 531 else | |
| 532 /* Empty AUTH, RFC-2554, sect. 5 */ | |
| 533 auth = strdup("<>"); | |
| 534 | |
| 535 if(!auth) { | |
| 536 free(from); | |
| 537 | |
| 538 return CURLE_OUT_OF_MEMORY; | |
| 539 } | |
| 540 } | |
| 541 | |
| 542 /* Prepare the mime data if some. */ | |
| 543 if(data->set.mimepost.kind != MIMEKIND_NONE) { | |
| 544 /* Use the whole structure as data. */ | |
| 545 data->set.mimepost.flags &= ~MIME_BODY_ONLY; | |
| 546 | |
| 547 /* Add external headers and mime version. */ | |
| 548 curl_mime_headers(&data->set.mimepost, data->set.headers, 0); | |
| 549 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL, | |
| 550 NULL, MIMESTRATEGY_MAIL); | |
| 551 | |
| 552 if(!result) | |
| 553 if(!Curl_checkheaders(conn, "Mime-Version")) | |
| 554 result = Curl_mime_add_header(&data->set.mimepost.curlheaders, | |
| 555 "Mime-Version: 1.0"); | |
| 556 | |
| 557 /* Make sure we will read the entire mime structure. */ | |
| 558 if(!result) | |
| 559 result = Curl_mime_rewind(&data->set.mimepost); | |
| 560 | |
| 561 if(result) { | |
| 562 free(from); | |
| 563 free(auth); | |
| 564 return result; | |
| 565 } | |
| 566 | |
| 567 data->state.infilesize = Curl_mime_size(&data->set.mimepost); | |
| 568 | |
| 569 /* Read from mime structure. */ | |
| 570 data->state.fread_func = (curl_read_callback) Curl_mime_read; | |
| 571 data->state.in = (void *) &data->set.mimepost; | |
| 572 } | |
| 573 | |
| 574 /* Calculate the optional SIZE parameter */ | |
| 575 if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) { | |
| 576 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize); | |
| 577 | |
| 578 if(!size) { | |
| 579 free(from); | |
| 580 free(auth); | |
| 581 | |
| 582 return CURLE_OUT_OF_MEMORY; | |
| 583 } | |
| 584 } | |
| 585 | |
| 586 /* Send the MAIL command */ | |
| 587 if(!auth && !size) | |
| 588 result = Curl_pp_sendf(&conn->proto.smtpc.pp, | |
| 589 "MAIL FROM:%s", from); | |
| 590 else if(auth && !size) | |
| 591 result = Curl_pp_sendf(&conn->proto.smtpc.pp, | |
| 592 "MAIL FROM:%s AUTH=%s", from, auth); | |
| 593 else if(auth && size) | |
| 594 result = Curl_pp_sendf(&conn->proto.smtpc.pp, | |
| 595 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size); | |
| 596 else | |
| 597 result = Curl_pp_sendf(&conn->proto.smtpc.pp, | |
| 598 "MAIL FROM:%s SIZE=%s", from, size); | |
| 599 | |
| 600 free(from); | |
| 601 free(auth); | |
| 602 free(size); | |
| 603 | |
| 604 if(!result) | |
| 605 state(conn, SMTP_MAIL); | |
| 606 | |
| 607 return result; | |
| 608 } | |
| 609 | |
| 610 /*********************************************************************** | |
| 611 * | |
| 612 * smtp_perform_rcpt_to() | |
| 613 * | |
| 614 * Sends a RCPT TO command for a given recipient as part of the message upload | |
| 615 * process. | |
| 616 */ | |
| 617 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn) | |
| 618 { | |
| 619 CURLcode result = CURLE_OK; | |
| 620 struct Curl_easy *data = conn->data; | |
| 621 struct SMTP *smtp = data->req.protop; | |
| 622 | |
| 623 /* Send the RCPT TO command */ | |
| 624 if(smtp->rcpt->data[0] == '<') | |
| 625 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s", | |
| 626 smtp->rcpt->data); | |
| 627 else | |
| 628 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", | |
| 629 smtp->rcpt->data); | |
| 630 if(!result) | |
| 631 state(conn, SMTP_RCPT); | |
| 632 | |
| 633 return result; | |
| 634 } | |
| 635 | |
| 636 /*********************************************************************** | |
| 637 * | |
| 638 * smtp_perform_quit() | |
| 639 * | |
| 640 * Performs the quit action prior to sclose() being called. | |
| 641 */ | |
| 642 static CURLcode smtp_perform_quit(struct connectdata *conn) | |
| 643 { | |
| 644 /* Send the QUIT command */ | |
| 645 CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT"); | |
| 646 | |
| 647 if(!result) | |
| 648 state(conn, SMTP_QUIT); | |
| 649 | |
| 650 return result; | |
| 651 } | |
| 652 | |
| 653 /* For the initial server greeting */ | |
| 654 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn, | |
| 655 int smtpcode, | |
| 656 smtpstate instate) | |
| 657 { | |
| 658 CURLcode result = CURLE_OK; | |
| 659 struct Curl_easy *data = conn->data; | |
| 660 | |
| 661 (void)instate; /* no use for this yet */ | |
| 662 | |
| 663 if(smtpcode/100 != 2) { | |
| 664 failf(data, "Got unexpected smtp-server response: %d", smtpcode); | |
| 665 result = CURLE_WEIRD_SERVER_REPLY; | |
| 666 } | |
| 667 else | |
| 668 result = smtp_perform_ehlo(conn); | |
| 669 | |
| 670 return result; | |
| 671 } | |
| 672 | |
| 673 /* For STARTTLS responses */ | |
| 674 static CURLcode smtp_state_starttls_resp(struct connectdata *conn, | |
| 675 int smtpcode, | |
| 676 smtpstate instate) | |
| 677 { | |
| 678 CURLcode result = CURLE_OK; | |
| 679 struct Curl_easy *data = conn->data; | |
| 680 | |
| 681 (void)instate; /* no use for this yet */ | |
| 682 | |
| 683 if(smtpcode != 220) { | |
| 684 if(data->set.use_ssl != CURLUSESSL_TRY) { | |
| 685 failf(data, "STARTTLS denied, code %d", smtpcode); | |
| 686 result = CURLE_USE_SSL_FAILED; | |
| 687 } | |
| 688 else | |
| 689 result = smtp_perform_authentication(conn); | |
| 690 } | |
| 691 else | |
| 692 result = smtp_perform_upgrade_tls(conn); | |
| 693 | |
| 694 return result; | |
| 695 } | |
| 696 | |
| 697 /* For EHLO responses */ | |
| 698 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, | |
| 699 smtpstate instate) | |
| 700 { | |
| 701 CURLcode result = CURLE_OK; | |
| 702 struct Curl_easy *data = conn->data; | |
| 703 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 704 const char *line = data->state.buffer; | |
| 705 size_t len = strlen(line); | |
| 706 | |
| 707 (void)instate; /* no use for this yet */ | |
| 708 | |
| 709 if(smtpcode/100 != 2 && smtpcode != 1) { | |
| 710 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) | |
| 711 result = smtp_perform_helo(conn); | |
| 712 else { | |
| 713 failf(data, "Remote access denied: %d", smtpcode); | |
| 714 result = CURLE_REMOTE_ACCESS_DENIED; | |
| 715 } | |
| 716 } | |
| 717 else if(len >= 4) { | |
| 718 line += 4; | |
| 719 len -= 4; | |
| 720 | |
| 721 /* Does the server support the STARTTLS capability? */ | |
| 722 if(len >= 8 && !memcmp(line, "STARTTLS", 8)) | |
| 723 smtpc->tls_supported = TRUE; | |
| 724 | |
| 725 /* Does the server support the SIZE capability? */ | |
| 726 else if(len >= 4 && !memcmp(line, "SIZE", 4)) | |
| 727 smtpc->size_supported = TRUE; | |
| 728 | |
| 729 /* Does the server support authentication? */ | |
| 730 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { | |
| 731 smtpc->auth_supported = TRUE; | |
| 732 | |
| 733 /* Advance past the AUTH keyword */ | |
| 734 line += 5; | |
| 735 len -= 5; | |
| 736 | |
| 737 /* Loop through the data line */ | |
| 738 for(;;) { | |
| 739 size_t llen; | |
| 740 size_t wordlen; | |
| 741 unsigned int mechbit; | |
| 742 | |
| 743 while(len && | |
| 744 (*line == ' ' || *line == '\t' || | |
| 745 *line == '\r' || *line == '\n')) { | |
| 746 | |
| 747 line++; | |
| 748 len--; | |
| 749 } | |
| 750 | |
| 751 if(!len) | |
| 752 break; | |
| 753 | |
| 754 /* Extract the word */ | |
| 755 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && | |
| 756 line[wordlen] != '\t' && line[wordlen] != '\r' && | |
| 757 line[wordlen] != '\n';) | |
| 758 wordlen++; | |
| 759 | |
| 760 /* Test the word for a matching authentication mechanism */ | |
| 761 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); | |
| 762 if(mechbit && llen == wordlen) | |
| 763 smtpc->sasl.authmechs |= mechbit; | |
| 764 | |
| 765 line += wordlen; | |
| 766 len -= wordlen; | |
| 767 } | |
| 768 } | |
| 769 | |
| 770 if(smtpcode != 1) { | |
| 771 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { | |
| 772 /* We don't have a SSL/TLS connection yet, but SSL is requested */ | |
| 773 if(smtpc->tls_supported) | |
| 774 /* Switch to TLS connection now */ | |
| 775 result = smtp_perform_starttls(conn); | |
| 776 else if(data->set.use_ssl == CURLUSESSL_TRY) | |
| 777 /* Fallback and carry on with authentication */ | |
| 778 result = smtp_perform_authentication(conn); | |
| 779 else { | |
| 780 failf(data, "STARTTLS not supported."); | |
| 781 result = CURLE_USE_SSL_FAILED; | |
| 782 } | |
| 783 } | |
| 784 else | |
| 785 result = smtp_perform_authentication(conn); | |
| 786 } | |
| 787 } | |
| 788 else { | |
| 789 failf(data, "Unexpectedly short EHLO response"); | |
| 790 result = CURLE_WEIRD_SERVER_REPLY; | |
| 791 } | |
| 792 | |
| 793 return result; | |
| 794 } | |
| 795 | |
| 796 /* For HELO responses */ | |
| 797 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode, | |
| 798 smtpstate instate) | |
| 799 { | |
| 800 CURLcode result = CURLE_OK; | |
| 801 struct Curl_easy *data = conn->data; | |
| 802 | |
| 803 (void)instate; /* no use for this yet */ | |
| 804 | |
| 805 if(smtpcode/100 != 2) { | |
| 806 failf(data, "Remote access denied: %d", smtpcode); | |
| 807 result = CURLE_REMOTE_ACCESS_DENIED; | |
| 808 } | |
| 809 else | |
| 810 /* End of connect phase */ | |
| 811 state(conn, SMTP_STOP); | |
| 812 | |
| 813 return result; | |
| 814 } | |
| 815 | |
| 816 /* For SASL authentication responses */ | |
| 817 static CURLcode smtp_state_auth_resp(struct connectdata *conn, | |
| 818 int smtpcode, | |
| 819 smtpstate instate) | |
| 820 { | |
| 821 CURLcode result = CURLE_OK; | |
| 822 struct Curl_easy *data = conn->data; | |
| 823 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 824 saslprogress progress; | |
| 825 | |
| 826 (void)instate; /* no use for this yet */ | |
| 827 | |
| 828 result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress); | |
| 829 if(!result) | |
| 830 switch(progress) { | |
| 831 case SASL_DONE: | |
| 832 state(conn, SMTP_STOP); /* Authenticated */ | |
| 833 break; | |
| 834 case SASL_IDLE: /* No mechanism left after cancellation */ | |
| 835 failf(data, "Authentication cancelled"); | |
| 836 result = CURLE_LOGIN_DENIED; | |
| 837 break; | |
| 838 default: | |
| 839 break; | |
| 840 } | |
| 841 | |
| 842 return result; | |
| 843 } | |
| 844 | |
| 845 /* For command responses */ | |
| 846 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode, | |
| 847 smtpstate instate) | |
| 848 { | |
| 849 CURLcode result = CURLE_OK; | |
| 850 struct Curl_easy *data = conn->data; | |
| 851 struct SMTP *smtp = data->req.protop; | |
| 852 char *line = data->state.buffer; | |
| 853 size_t len = strlen(line); | |
| 854 | |
| 855 (void)instate; /* no use for this yet */ | |
| 856 | |
| 857 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) || | |
| 858 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) { | |
| 859 failf(data, "Command failed: %d", smtpcode); | |
| 860 result = CURLE_RECV_ERROR; | |
| 861 } | |
| 862 else { | |
| 863 /* Temporarily add the LF character back and send as body to the client */ | |
| 864 if(!data->set.opt_no_body) { | |
| 865 line[len] = '\n'; | |
| 866 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1); | |
| 867 line[len] = '\0'; | |
| 868 } | |
| 869 | |
| 870 if(smtpcode != 1) { | |
| 871 if(smtp->rcpt) { | |
| 872 smtp->rcpt = smtp->rcpt->next; | |
| 873 | |
| 874 if(smtp->rcpt) { | |
| 875 /* Send the next command */ | |
| 876 result = smtp_perform_command(conn); | |
| 877 } | |
| 878 else | |
| 879 /* End of DO phase */ | |
| 880 state(conn, SMTP_STOP); | |
| 881 } | |
| 882 else | |
| 883 /* End of DO phase */ | |
| 884 state(conn, SMTP_STOP); | |
| 885 } | |
| 886 } | |
| 887 | |
| 888 return result; | |
| 889 } | |
| 890 | |
| 891 /* For MAIL responses */ | |
| 892 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode, | |
| 893 smtpstate instate) | |
| 894 { | |
| 895 CURLcode result = CURLE_OK; | |
| 896 struct Curl_easy *data = conn->data; | |
| 897 | |
| 898 (void)instate; /* no use for this yet */ | |
| 899 | |
| 900 if(smtpcode/100 != 2) { | |
| 901 failf(data, "MAIL failed: %d", smtpcode); | |
| 902 result = CURLE_SEND_ERROR; | |
| 903 } | |
| 904 else | |
| 905 /* Start the RCPT TO command */ | |
| 906 result = smtp_perform_rcpt_to(conn); | |
| 907 | |
| 908 return result; | |
| 909 } | |
| 910 | |
| 911 /* For RCPT responses */ | |
| 912 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, | |
| 913 smtpstate instate) | |
| 914 { | |
| 915 CURLcode result = CURLE_OK; | |
| 916 struct Curl_easy *data = conn->data; | |
| 917 struct SMTP *smtp = data->req.protop; | |
| 918 | |
| 919 (void)instate; /* no use for this yet */ | |
| 920 | |
| 921 if(smtpcode/100 != 2) { | |
| 922 failf(data, "RCPT failed: %d", smtpcode); | |
| 923 result = CURLE_SEND_ERROR; | |
| 924 } | |
| 925 else { | |
| 926 smtp->rcpt = smtp->rcpt->next; | |
| 927 | |
| 928 if(smtp->rcpt) | |
| 929 /* Send the next RCPT TO command */ | |
| 930 result = smtp_perform_rcpt_to(conn); | |
| 931 else { | |
| 932 /* Send the DATA command */ | |
| 933 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA"); | |
| 934 | |
| 935 if(!result) | |
| 936 state(conn, SMTP_DATA); | |
| 937 } | |
| 938 } | |
| 939 | |
| 940 return result; | |
| 941 } | |
| 942 | |
| 943 /* For DATA response */ | |
| 944 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode, | |
| 945 smtpstate instate) | |
| 946 { | |
| 947 CURLcode result = CURLE_OK; | |
| 948 struct Curl_easy *data = conn->data; | |
| 949 | |
| 950 (void)instate; /* no use for this yet */ | |
| 951 | |
| 952 if(smtpcode != 354) { | |
| 953 failf(data, "DATA failed: %d", smtpcode); | |
| 954 result = CURLE_SEND_ERROR; | |
| 955 } | |
| 956 else { | |
| 957 /* Set the progress upload size */ | |
| 958 Curl_pgrsSetUploadSize(data, data->state.infilesize); | |
| 959 | |
| 960 /* SMTP upload */ | |
| 961 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); | |
| 962 | |
| 963 /* End of DO phase */ | |
| 964 state(conn, SMTP_STOP); | |
| 965 } | |
| 966 | |
| 967 return result; | |
| 968 } | |
| 969 | |
| 970 /* For POSTDATA responses, which are received after the entire DATA | |
| 971 part has been sent to the server */ | |
| 972 static CURLcode smtp_state_postdata_resp(struct connectdata *conn, | |
| 973 int smtpcode, | |
| 974 smtpstate instate) | |
| 975 { | |
| 976 CURLcode result = CURLE_OK; | |
| 977 | |
| 978 (void)instate; /* no use for this yet */ | |
| 979 | |
| 980 if(smtpcode != 250) | |
| 981 result = CURLE_RECV_ERROR; | |
| 982 | |
| 983 /* End of DONE phase */ | |
| 984 state(conn, SMTP_STOP); | |
| 985 | |
| 986 return result; | |
| 987 } | |
| 988 | |
| 989 static CURLcode smtp_statemach_act(struct connectdata *conn) | |
| 990 { | |
| 991 CURLcode result = CURLE_OK; | |
| 992 curl_socket_t sock = conn->sock[FIRSTSOCKET]; | |
| 993 struct Curl_easy *data = conn->data; | |
| 994 int smtpcode; | |
| 995 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 996 struct pingpong *pp = &smtpc->pp; | |
| 997 size_t nread = 0; | |
| 998 | |
| 999 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */ | |
| 1000 if(smtpc->state == SMTP_UPGRADETLS) | |
| 1001 return smtp_perform_upgrade_tls(conn); | |
| 1002 | |
| 1003 /* Flush any data that needs to be sent */ | |
| 1004 if(pp->sendleft) | |
| 1005 return Curl_pp_flushsend(pp); | |
| 1006 | |
| 1007 do { | |
| 1008 /* Read the response from the server */ | |
| 1009 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread); | |
| 1010 if(result) | |
| 1011 return result; | |
| 1012 | |
| 1013 /* Store the latest response for later retrieval if necessary */ | |
| 1014 if(smtpc->state != SMTP_QUIT && smtpcode != 1) | |
| 1015 data->info.httpcode = smtpcode; | |
| 1016 | |
| 1017 if(!smtpcode) | |
| 1018 break; | |
| 1019 | |
| 1020 /* We have now received a full SMTP server response */ | |
| 1021 switch(smtpc->state) { | |
| 1022 case SMTP_SERVERGREET: | |
| 1023 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state); | |
| 1024 break; | |
| 1025 | |
| 1026 case SMTP_EHLO: | |
| 1027 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state); | |
| 1028 break; | |
| 1029 | |
| 1030 case SMTP_HELO: | |
| 1031 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state); | |
| 1032 break; | |
| 1033 | |
| 1034 case SMTP_STARTTLS: | |
| 1035 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state); | |
| 1036 break; | |
| 1037 | |
| 1038 case SMTP_AUTH: | |
| 1039 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state); | |
| 1040 break; | |
| 1041 | |
| 1042 case SMTP_COMMAND: | |
| 1043 result = smtp_state_command_resp(conn, smtpcode, smtpc->state); | |
| 1044 break; | |
| 1045 | |
| 1046 case SMTP_MAIL: | |
| 1047 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state); | |
| 1048 break; | |
| 1049 | |
| 1050 case SMTP_RCPT: | |
| 1051 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state); | |
| 1052 break; | |
| 1053 | |
| 1054 case SMTP_DATA: | |
| 1055 result = smtp_state_data_resp(conn, smtpcode, smtpc->state); | |
| 1056 break; | |
| 1057 | |
| 1058 case SMTP_POSTDATA: | |
| 1059 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state); | |
| 1060 break; | |
| 1061 | |
| 1062 case SMTP_QUIT: | |
| 1063 /* fallthrough, just stop! */ | |
| 1064 default: | |
| 1065 /* internal error */ | |
| 1066 state(conn, SMTP_STOP); | |
| 1067 break; | |
| 1068 } | |
| 1069 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp)); | |
| 1070 | |
| 1071 return result; | |
| 1072 } | |
| 1073 | |
| 1074 /* Called repeatedly until done from multi.c */ | |
| 1075 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done) | |
| 1076 { | |
| 1077 CURLcode result = CURLE_OK; | |
| 1078 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 1079 | |
| 1080 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) { | |
| 1081 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone); | |
| 1082 if(result || !smtpc->ssldone) | |
| 1083 return result; | |
| 1084 } | |
| 1085 | |
| 1086 result = Curl_pp_statemach(&smtpc->pp, FALSE, FALSE); | |
| 1087 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE; | |
| 1088 | |
| 1089 return result; | |
| 1090 } | |
| 1091 | |
| 1092 static CURLcode smtp_block_statemach(struct connectdata *conn, | |
| 1093 bool disconnecting) | |
| 1094 { | |
| 1095 CURLcode result = CURLE_OK; | |
| 1096 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 1097 | |
| 1098 while(smtpc->state != SMTP_STOP && !result) | |
| 1099 result = Curl_pp_statemach(&smtpc->pp, TRUE, disconnecting); | |
| 1100 | |
| 1101 return result; | |
| 1102 } | |
| 1103 | |
| 1104 /* Allocate and initialize the SMTP struct for the current Curl_easy if | |
| 1105 required */ | |
| 1106 static CURLcode smtp_init(struct connectdata *conn) | |
| 1107 { | |
| 1108 CURLcode result = CURLE_OK; | |
| 1109 struct Curl_easy *data = conn->data; | |
| 1110 struct SMTP *smtp; | |
| 1111 | |
| 1112 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1); | |
| 1113 if(!smtp) | |
| 1114 result = CURLE_OUT_OF_MEMORY; | |
| 1115 | |
| 1116 return result; | |
| 1117 } | |
| 1118 | |
| 1119 /* For the SMTP "protocol connect" and "doing" phases only */ | |
| 1120 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks) | |
| 1121 { | |
| 1122 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks); | |
| 1123 } | |
| 1124 | |
| 1125 /*********************************************************************** | |
| 1126 * | |
| 1127 * smtp_connect() | |
| 1128 * | |
| 1129 * This function should do everything that is to be considered a part of | |
| 1130 * the connection phase. | |
| 1131 * | |
| 1132 * The variable pointed to by 'done' will be TRUE if the protocol-layer | |
| 1133 * connect phase is done when this function returns, or FALSE if not. | |
| 1134 */ | |
| 1135 static CURLcode smtp_connect(struct connectdata *conn, bool *done) | |
| 1136 { | |
| 1137 CURLcode result = CURLE_OK; | |
| 1138 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 1139 struct pingpong *pp = &smtpc->pp; | |
| 1140 | |
| 1141 *done = FALSE; /* default to not done yet */ | |
| 1142 | |
| 1143 /* We always support persistent connections in SMTP */ | |
| 1144 connkeep(conn, "SMTP default"); | |
| 1145 | |
| 1146 /* Set the default response time-out */ | |
| 1147 pp->response_time = RESP_TIMEOUT; | |
| 1148 pp->statemach_act = smtp_statemach_act; | |
| 1149 pp->endofresp = smtp_endofresp; | |
| 1150 pp->conn = conn; | |
| 1151 | |
| 1152 /* Initialize the SASL storage */ | |
| 1153 Curl_sasl_init(&smtpc->sasl, &saslsmtp); | |
| 1154 | |
| 1155 /* Initialise the pingpong layer */ | |
| 1156 Curl_pp_init(pp); | |
| 1157 | |
| 1158 /* Parse the URL options */ | |
| 1159 result = smtp_parse_url_options(conn); | |
| 1160 if(result) | |
| 1161 return result; | |
| 1162 | |
| 1163 /* Parse the URL path */ | |
| 1164 result = smtp_parse_url_path(conn); | |
| 1165 if(result) | |
| 1166 return result; | |
| 1167 | |
| 1168 /* Start off waiting for the server greeting response */ | |
| 1169 state(conn, SMTP_SERVERGREET); | |
| 1170 | |
| 1171 result = smtp_multi_statemach(conn, done); | |
| 1172 | |
| 1173 return result; | |
| 1174 } | |
| 1175 | |
| 1176 /*********************************************************************** | |
| 1177 * | |
| 1178 * smtp_done() | |
| 1179 * | |
| 1180 * The DONE function. This does what needs to be done after a single DO has | |
| 1181 * performed. | |
| 1182 * | |
| 1183 * Input argument is already checked for validity. | |
| 1184 */ | |
| 1185 static CURLcode smtp_done(struct connectdata *conn, CURLcode status, | |
| 1186 bool premature) | |
| 1187 { | |
| 1188 CURLcode result = CURLE_OK; | |
| 1189 struct Curl_easy *data = conn->data; | |
| 1190 struct SMTP *smtp = data->req.protop; | |
| 1191 struct pingpong *pp = &conn->proto.smtpc.pp; | |
| 1192 char *eob; | |
| 1193 ssize_t len; | |
| 1194 ssize_t bytes_written; | |
| 1195 | |
| 1196 (void)premature; | |
| 1197 | |
| 1198 if(!smtp || !pp->conn) | |
| 1199 return CURLE_OK; | |
| 1200 | |
| 1201 /* Cleanup our per-request based variables */ | |
| 1202 Curl_safefree(smtp->custom); | |
| 1203 | |
| 1204 if(status) { | |
| 1205 connclose(conn, "SMTP done with bad status"); /* marked for closure */ | |
| 1206 result = status; /* use the already set error code */ | |
| 1207 } | |
| 1208 else if(!data->set.connect_only && data->set.mail_rcpt && | |
| 1209 (data->set.upload || data->set.mimepost.kind)) { | |
| 1210 /* Calculate the EOB taking into account any terminating CRLF from the | |
| 1211 previous line of the email or the CRLF of the DATA command when there | |
| 1212 is "no mail data". RFC-5321, sect. 4.1.1.4. | |
| 1213 | |
| 1214 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to | |
| 1215 fail when using a different pointer following a previous write, that | |
| 1216 returned CURLE_AGAIN, we duplicate the EOB now rather than when the | |
| 1217 bytes written doesn't equal len. */ | |
| 1218 if(smtp->trailing_crlf || !conn->data->state.infilesize) { | |
| 1219 eob = strdup(&SMTP_EOB[2]); | |
| 1220 len = SMTP_EOB_LEN - 2; | |
| 1221 } | |
| 1222 else { | |
| 1223 eob = strdup(SMTP_EOB); | |
| 1224 len = SMTP_EOB_LEN; | |
| 1225 } | |
| 1226 | |
| 1227 if(!eob) | |
| 1228 return CURLE_OUT_OF_MEMORY; | |
| 1229 | |
| 1230 /* Send the end of block data */ | |
| 1231 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written); | |
| 1232 if(result) { | |
| 1233 free(eob); | |
| 1234 return result; | |
| 1235 } | |
| 1236 | |
| 1237 if(bytes_written != len) { | |
| 1238 /* The whole chunk was not sent so keep it around and adjust the | |
| 1239 pingpong structure accordingly */ | |
| 1240 pp->sendthis = eob; | |
| 1241 pp->sendsize = len; | |
| 1242 pp->sendleft = len - bytes_written; | |
| 1243 } | |
| 1244 else { | |
| 1245 /* Successfully sent so adjust the response timeout relative to now */ | |
| 1246 pp->response = Curl_now(); | |
| 1247 | |
| 1248 free(eob); | |
| 1249 } | |
| 1250 | |
| 1251 state(conn, SMTP_POSTDATA); | |
| 1252 | |
| 1253 /* Run the state-machine */ | |
| 1254 result = smtp_block_statemach(conn, FALSE); | |
| 1255 } | |
| 1256 | |
| 1257 /* Clear the transfer mode for the next request */ | |
| 1258 smtp->transfer = FTPTRANSFER_BODY; | |
| 1259 | |
| 1260 return result; | |
| 1261 } | |
| 1262 | |
| 1263 /*********************************************************************** | |
| 1264 * | |
| 1265 * smtp_perform() | |
| 1266 * | |
| 1267 * This is the actual DO function for SMTP. Transfer a mail, send a command | |
| 1268 * or get some data according to the options previously setup. | |
| 1269 */ | |
| 1270 static CURLcode smtp_perform(struct connectdata *conn, bool *connected, | |
| 1271 bool *dophase_done) | |
| 1272 { | |
| 1273 /* This is SMTP and no proxy */ | |
| 1274 CURLcode result = CURLE_OK; | |
| 1275 struct Curl_easy *data = conn->data; | |
| 1276 struct SMTP *smtp = data->req.protop; | |
| 1277 | |
| 1278 DEBUGF(infof(conn->data, "DO phase starts\n")); | |
| 1279 | |
| 1280 if(data->set.opt_no_body) { | |
| 1281 /* Requested no body means no transfer */ | |
| 1282 smtp->transfer = FTPTRANSFER_INFO; | |
| 1283 } | |
| 1284 | |
| 1285 *dophase_done = FALSE; /* not done yet */ | |
| 1286 | |
| 1287 /* Store the first recipient (or NULL if not specified) */ | |
| 1288 smtp->rcpt = data->set.mail_rcpt; | |
| 1289 | |
| 1290 /* Initial data character is the first character in line: it is implicitly | |
| 1291 preceded by a virtual CRLF. */ | |
| 1292 smtp->trailing_crlf = TRUE; | |
| 1293 smtp->eob = 2; | |
| 1294 | |
| 1295 /* Start the first command in the DO phase */ | |
| 1296 if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt) | |
| 1297 /* MAIL transfer */ | |
| 1298 result = smtp_perform_mail(conn); | |
| 1299 else | |
| 1300 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */ | |
| 1301 result = smtp_perform_command(conn); | |
| 1302 | |
| 1303 if(result) | |
| 1304 return result; | |
| 1305 | |
| 1306 /* Run the state-machine */ | |
| 1307 result = smtp_multi_statemach(conn, dophase_done); | |
| 1308 | |
| 1309 *connected = conn->bits.tcpconnect[FIRSTSOCKET]; | |
| 1310 | |
| 1311 if(*dophase_done) | |
| 1312 DEBUGF(infof(conn->data, "DO phase is complete\n")); | |
| 1313 | |
| 1314 return result; | |
| 1315 } | |
| 1316 | |
| 1317 /*********************************************************************** | |
| 1318 * | |
| 1319 * smtp_do() | |
| 1320 * | |
| 1321 * This function is registered as 'curl_do' function. It decodes the path | |
| 1322 * parts etc as a wrapper to the actual DO function (smtp_perform). | |
| 1323 * | |
| 1324 * The input argument is already checked for validity. | |
| 1325 */ | |
| 1326 static CURLcode smtp_do(struct connectdata *conn, bool *done) | |
| 1327 { | |
| 1328 CURLcode result = CURLE_OK; | |
| 1329 | |
| 1330 *done = FALSE; /* default to false */ | |
| 1331 | |
| 1332 /* Parse the custom request */ | |
| 1333 result = smtp_parse_custom_request(conn); | |
| 1334 if(result) | |
| 1335 return result; | |
| 1336 | |
| 1337 result = smtp_regular_transfer(conn, done); | |
| 1338 | |
| 1339 return result; | |
| 1340 } | |
| 1341 | |
| 1342 /*********************************************************************** | |
| 1343 * | |
| 1344 * smtp_disconnect() | |
| 1345 * | |
| 1346 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection | |
| 1347 * resources. BLOCKING. | |
| 1348 */ | |
| 1349 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection) | |
| 1350 { | |
| 1351 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 1352 | |
| 1353 /* We cannot send quit unconditionally. If this connection is stale or | |
| 1354 bad in any way, sending quit and waiting around here will make the | |
| 1355 disconnect wait in vain and cause more problems than we need to. */ | |
| 1356 | |
| 1357 /* The SMTP session may or may not have been allocated/setup at this | |
| 1358 point! */ | |
| 1359 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart) | |
| 1360 if(!smtp_perform_quit(conn)) | |
| 1361 (void)smtp_block_statemach(conn, TRUE); /* ignore errors on QUIT */ | |
| 1362 | |
| 1363 /* Disconnect from the server */ | |
| 1364 Curl_pp_disconnect(&smtpc->pp); | |
| 1365 | |
| 1366 /* Cleanup the SASL module */ | |
| 1367 Curl_sasl_cleanup(conn, smtpc->sasl.authused); | |
| 1368 | |
| 1369 /* Cleanup our connection based variables */ | |
| 1370 Curl_safefree(smtpc->domain); | |
| 1371 | |
| 1372 return CURLE_OK; | |
| 1373 } | |
| 1374 | |
| 1375 /* Call this when the DO phase has completed */ | |
| 1376 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected) | |
| 1377 { | |
| 1378 struct SMTP *smtp = conn->data->req.protop; | |
| 1379 | |
| 1380 (void)connected; | |
| 1381 | |
| 1382 if(smtp->transfer != FTPTRANSFER_BODY) | |
| 1383 /* no data to transfer */ | |
| 1384 Curl_setup_transfer(conn->data, -1, -1, FALSE, -1); | |
| 1385 | |
| 1386 return CURLE_OK; | |
| 1387 } | |
| 1388 | |
| 1389 /* Called from multi.c while DOing */ | |
| 1390 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done) | |
| 1391 { | |
| 1392 CURLcode result = smtp_multi_statemach(conn, dophase_done); | |
| 1393 | |
| 1394 if(result) | |
| 1395 DEBUGF(infof(conn->data, "DO phase failed\n")); | |
| 1396 else if(*dophase_done) { | |
| 1397 result = smtp_dophase_done(conn, FALSE /* not connected */); | |
| 1398 | |
| 1399 DEBUGF(infof(conn->data, "DO phase is complete\n")); | |
| 1400 } | |
| 1401 | |
| 1402 return result; | |
| 1403 } | |
| 1404 | |
| 1405 /*********************************************************************** | |
| 1406 * | |
| 1407 * smtp_regular_transfer() | |
| 1408 * | |
| 1409 * The input argument is already checked for validity. | |
| 1410 * | |
| 1411 * Performs all commands done before a regular transfer between a local and a | |
| 1412 * remote host. | |
| 1413 */ | |
| 1414 static CURLcode smtp_regular_transfer(struct connectdata *conn, | |
| 1415 bool *dophase_done) | |
| 1416 { | |
| 1417 CURLcode result = CURLE_OK; | |
| 1418 bool connected = FALSE; | |
| 1419 struct Curl_easy *data = conn->data; | |
| 1420 | |
| 1421 /* Make sure size is unknown at this point */ | |
| 1422 data->req.size = -1; | |
| 1423 | |
| 1424 /* Set the progress data */ | |
| 1425 Curl_pgrsSetUploadCounter(data, 0); | |
| 1426 Curl_pgrsSetDownloadCounter(data, 0); | |
| 1427 Curl_pgrsSetUploadSize(data, -1); | |
| 1428 Curl_pgrsSetDownloadSize(data, -1); | |
| 1429 | |
| 1430 /* Carry out the perform */ | |
| 1431 result = smtp_perform(conn, &connected, dophase_done); | |
| 1432 | |
| 1433 /* Perform post DO phase operations if necessary */ | |
| 1434 if(!result && *dophase_done) | |
| 1435 result = smtp_dophase_done(conn, connected); | |
| 1436 | |
| 1437 return result; | |
| 1438 } | |
| 1439 | |
| 1440 static CURLcode smtp_setup_connection(struct connectdata *conn) | |
| 1441 { | |
| 1442 CURLcode result; | |
| 1443 | |
| 1444 /* Clear the TLS upgraded flag */ | |
| 1445 conn->tls_upgraded = FALSE; | |
| 1446 | |
| 1447 /* Initialise the SMTP layer */ | |
| 1448 result = smtp_init(conn); | |
| 1449 if(result) | |
| 1450 return result; | |
| 1451 | |
| 1452 return CURLE_OK; | |
| 1453 } | |
| 1454 | |
| 1455 /*********************************************************************** | |
| 1456 * | |
| 1457 * smtp_parse_url_options() | |
| 1458 * | |
| 1459 * Parse the URL login options. | |
| 1460 */ | |
| 1461 static CURLcode smtp_parse_url_options(struct connectdata *conn) | |
| 1462 { | |
| 1463 CURLcode result = CURLE_OK; | |
| 1464 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 1465 const char *ptr = conn->options; | |
| 1466 | |
| 1467 smtpc->sasl.resetprefs = TRUE; | |
| 1468 | |
| 1469 while(!result && ptr && *ptr) { | |
| 1470 const char *key = ptr; | |
| 1471 const char *value; | |
| 1472 | |
| 1473 while(*ptr && *ptr != '=') | |
| 1474 ptr++; | |
| 1475 | |
| 1476 value = ptr + 1; | |
| 1477 | |
| 1478 while(*ptr && *ptr != ';') | |
| 1479 ptr++; | |
| 1480 | |
| 1481 if(strncasecompare(key, "AUTH=", 5)) | |
| 1482 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, | |
| 1483 value, ptr - value); | |
| 1484 else | |
| 1485 result = CURLE_URL_MALFORMAT; | |
| 1486 | |
| 1487 if(*ptr == ';') | |
| 1488 ptr++; | |
| 1489 } | |
| 1490 | |
| 1491 return result; | |
| 1492 } | |
| 1493 | |
| 1494 /*********************************************************************** | |
| 1495 * | |
| 1496 * smtp_parse_url_path() | |
| 1497 * | |
| 1498 * Parse the URL path into separate path components. | |
| 1499 */ | |
| 1500 static CURLcode smtp_parse_url_path(struct connectdata *conn) | |
| 1501 { | |
| 1502 /* The SMTP struct is already initialised in smtp_connect() */ | |
| 1503 struct Curl_easy *data = conn->data; | |
| 1504 struct smtp_conn *smtpc = &conn->proto.smtpc; | |
| 1505 const char *path = &data->state.up.path[1]; /* skip leading path */ | |
| 1506 char localhost[HOSTNAME_MAX + 1]; | |
| 1507 | |
| 1508 /* Calculate the path if necessary */ | |
| 1509 if(!*path) { | |
| 1510 if(!Curl_gethostname(localhost, sizeof(localhost))) | |
| 1511 path = localhost; | |
| 1512 else | |
| 1513 path = "localhost"; | |
| 1514 } | |
| 1515 | |
| 1516 /* URL decode the path and use it as the domain in our EHLO */ | |
| 1517 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE); | |
| 1518 } | |
| 1519 | |
| 1520 /*********************************************************************** | |
| 1521 * | |
| 1522 * smtp_parse_custom_request() | |
| 1523 * | |
| 1524 * Parse the custom request. | |
| 1525 */ | |
| 1526 static CURLcode smtp_parse_custom_request(struct connectdata *conn) | |
| 1527 { | |
| 1528 CURLcode result = CURLE_OK; | |
| 1529 struct Curl_easy *data = conn->data; | |
| 1530 struct SMTP *smtp = data->req.protop; | |
| 1531 const char *custom = data->set.str[STRING_CUSTOMREQUEST]; | |
| 1532 | |
| 1533 /* URL decode the custom request */ | |
| 1534 if(custom) | |
| 1535 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE); | |
| 1536 | |
| 1537 return result; | |
| 1538 } | |
| 1539 | |
| 1540 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread) | |
| 1541 { | |
| 1542 /* When sending a SMTP payload we must detect CRLF. sequences making sure | |
| 1543 they are sent as CRLF.. instead, as a . on the beginning of a line will | |
| 1544 be deleted by the server when not part of an EOB terminator and a | |
| 1545 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of | |
| 1546 data by the server | |
| 1547 */ | |
| 1548 ssize_t i; | |
| 1549 ssize_t si; | |
| 1550 struct Curl_easy *data = conn->data; | |
| 1551 struct SMTP *smtp = data->req.protop; | |
| 1552 char *scratch = data->state.scratch; | |
| 1553 char *newscratch = NULL; | |
| 1554 char *oldscratch = NULL; | |
| 1555 size_t eob_sent; | |
| 1556 | |
| 1557 /* Do we need to allocate a scratch buffer? */ | |
| 1558 if(!scratch || data->set.crlf) { | |
| 1559 oldscratch = scratch; | |
| 1560 | |
| 1561 scratch = newscratch = malloc(2 * data->set.upload_buffer_size); | |
| 1562 if(!newscratch) { | |
| 1563 failf(data, "Failed to alloc scratch buffer!"); | |
| 1564 | |
| 1565 return CURLE_OUT_OF_MEMORY; | |
| 1566 } | |
| 1567 } | |
| 1568 DEBUGASSERT(data->set.upload_buffer_size >= (size_t)nread); | |
| 1569 | |
| 1570 /* Have we already sent part of the EOB? */ | |
| 1571 eob_sent = smtp->eob; | |
| 1572 | |
| 1573 /* This loop can be improved by some kind of Boyer-Moore style of | |
| 1574 approach but that is saved for later... */ | |
| 1575 for(i = 0, si = 0; i < nread; i++) { | |
| 1576 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) { | |
| 1577 smtp->eob++; | |
| 1578 | |
| 1579 /* Is the EOB potentially the terminating CRLF? */ | |
| 1580 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob) | |
| 1581 smtp->trailing_crlf = TRUE; | |
| 1582 else | |
| 1583 smtp->trailing_crlf = FALSE; | |
| 1584 } | |
| 1585 else if(smtp->eob) { | |
| 1586 /* A previous substring matched so output that first */ | |
| 1587 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); | |
| 1588 si += smtp->eob - eob_sent; | |
| 1589 | |
| 1590 /* Then compare the first byte */ | |
| 1591 if(SMTP_EOB[0] == data->req.upload_fromhere[i]) | |
| 1592 smtp->eob = 1; | |
| 1593 else | |
| 1594 smtp->eob = 0; | |
| 1595 | |
| 1596 eob_sent = 0; | |
| 1597 | |
| 1598 /* Reset the trailing CRLF flag as there was more data */ | |
| 1599 smtp->trailing_crlf = FALSE; | |
| 1600 } | |
| 1601 | |
| 1602 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */ | |
| 1603 if(SMTP_EOB_FIND_LEN == smtp->eob) { | |
| 1604 /* Copy the replacement data to the target buffer */ | |
| 1605 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent], | |
| 1606 SMTP_EOB_REPL_LEN - eob_sent); | |
| 1607 si += SMTP_EOB_REPL_LEN - eob_sent; | |
| 1608 smtp->eob = 0; | |
| 1609 eob_sent = 0; | |
| 1610 } | |
| 1611 else if(!smtp->eob) | |
| 1612 scratch[si++] = data->req.upload_fromhere[i]; | |
| 1613 } | |
| 1614 | |
| 1615 if(smtp->eob - eob_sent) { | |
| 1616 /* A substring matched before processing ended so output that now */ | |
| 1617 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent); | |
| 1618 si += smtp->eob - eob_sent; | |
| 1619 } | |
| 1620 | |
| 1621 /* Only use the new buffer if we replaced something */ | |
| 1622 if(si != nread) { | |
| 1623 /* Upload from the new (replaced) buffer instead */ | |
| 1624 data->req.upload_fromhere = scratch; | |
| 1625 | |
| 1626 /* Save the buffer so it can be freed later */ | |
| 1627 data->state.scratch = scratch; | |
| 1628 | |
| 1629 /* Free the old scratch buffer */ | |
| 1630 free(oldscratch); | |
| 1631 | |
| 1632 /* Set the new amount too */ | |
| 1633 data->req.upload_present = si; | |
| 1634 } | |
| 1635 else | |
| 1636 free(newscratch); | |
| 1637 | |
| 1638 return CURLE_OK; | |
| 1639 } | |
| 1640 | |
| 1641 #endif /* CURL_DISABLE_SMTP */ |
