pop3.c (52444B)
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 * RFC1734 POP3 Authentication 24 * RFC1939 POP3 protocol 25 * RFC2195 CRAM-MD5 authentication 26 * RFC2384 POP URL Scheme 27 * RFC2449 POP3 Extension Mechanism 28 * RFC2595 Using TLS with IMAP, POP3 and ACAP 29 * RFC2831 DIGEST-MD5 authentication 30 * RFC4422 Simple Authentication and Security Layer (SASL) 31 * RFC4616 PLAIN authentication 32 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism 33 * RFC5034 POP3 SASL Authentication Mechanism 34 * RFC6749 OAuth 2.0 Authorization Framework 35 * RFC8314 Use of TLS for Email Submission and Access 36 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> 37 * 38 ***************************************************************************/ 39 40 #include "curl_setup.h" 41 42 #ifndef CURL_DISABLE_POP3 43 44 #ifdef HAVE_NETINET_IN_H 45 #include <netinet/in.h> 46 #endif 47 #ifdef HAVE_ARPA_INET_H 48 #include <arpa/inet.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 #include <curl/curl.h> 59 #include "urldata.h" 60 #include "sendf.h" 61 #include "hostip.h" 62 #include "progress.h" 63 #include "transfer.h" 64 #include "escape.h" 65 #include "http.h" /* for HTTP proxy tunnel stuff */ 66 #include "socks.h" 67 #include "pingpong.h" 68 #include "pop3.h" 69 #include "vtls/vtls.h" 70 #include "cfilters.h" 71 #include "connect.h" 72 #include "select.h" 73 #include "multiif.h" 74 #include "url.h" 75 #include "bufref.h" 76 #include "curl_sasl.h" 77 #include "curl_md5.h" 78 #include "curlx/warnless.h" 79 #include "strdup.h" 80 /* The last 3 #include files should be in this order */ 81 #include "curl_printf.h" 82 #include "curl_memory.h" 83 #include "memdebug.h" 84 85 /* Authentication type flags */ 86 #define POP3_TYPE_CLEARTEXT (1 << 0) 87 #define POP3_TYPE_APOP (1 << 1) 88 #define POP3_TYPE_SASL (1 << 2) 89 90 /* Authentication type values */ 91 #define POP3_TYPE_NONE 0 92 #define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL) 93 94 /* This is the 5-bytes End-Of-Body marker for POP3 */ 95 #define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" 96 #define POP3_EOB_LEN 5 97 98 /* meta key for storing protocol meta at easy handle */ 99 #define CURL_META_POP3_EASY "meta:proto:pop3:easy" 100 /* meta key for storing protocol meta at connection */ 101 #define CURL_META_POP3_CONN "meta:proto:pop3:conn" 102 103 /* 104 * POP3 easy handle state 105 */ 106 struct POP3 { 107 curl_pp_transfer transfer; 108 char *id; /* Message ID */ 109 char *custom; /* Custom Request */ 110 }; 111 112 /* 113 * POP3 connection state 114 */ 115 typedef enum { 116 POP3_STOP, /* do nothing state, stops the state machine */ 117 POP3_SERVERGREET, /* waiting for the initial greeting immediately after 118 a connect */ 119 POP3_CAPA, 120 POP3_STARTTLS, 121 POP3_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS 122 (multi mode only) */ 123 POP3_AUTH, 124 POP3_APOP, 125 POP3_USER, 126 POP3_PASS, 127 POP3_COMMAND, 128 POP3_QUIT, 129 POP3_LAST /* never used */ 130 } pop3state; 131 132 struct pop3_conn { 133 struct pingpong pp; 134 pop3state state; /* Always use pop3.c:state() to change state! */ 135 size_t eob; /* Number of bytes of the EOB (End Of Body) that 136 have been received so far */ 137 size_t strip; /* Number of bytes from the start to ignore as 138 non-body */ 139 struct SASL sasl; /* SASL-related storage */ 140 char *apoptimestamp; /* APOP timestamp from the server greeting */ 141 unsigned char authtypes; /* Accepted authentication types */ 142 unsigned char preftype; /* Preferred authentication type */ 143 BIT(ssldone); /* Is connect() over SSL done? */ 144 BIT(tls_supported); /* StartTLS capability supported by server */ 145 }; 146 147 /* Local API functions */ 148 static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done); 149 static CURLcode pop3_do(struct Curl_easy *data, bool *done); 150 static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, 151 bool premature); 152 static CURLcode pop3_connect(struct Curl_easy *data, bool *done); 153 static CURLcode pop3_disconnect(struct Curl_easy *data, 154 struct connectdata *conn, bool dead); 155 static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done); 156 static int pop3_getsock(struct Curl_easy *data, 157 struct connectdata *conn, curl_socket_t *socks); 158 static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done); 159 static CURLcode pop3_setup_connection(struct Curl_easy *data, 160 struct connectdata *conn); 161 static CURLcode pop3_parse_url_options(struct connectdata *conn); 162 static CURLcode pop3_parse_url_path(struct Curl_easy *data); 163 static CURLcode pop3_parse_custom_request(struct Curl_easy *data); 164 static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech, 165 const struct bufref *initresp); 166 static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech, 167 const struct bufref *resp); 168 static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech); 169 static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out); 170 171 /* This function scans the body after the end-of-body and writes everything 172 * until the end is found */ 173 static CURLcode pop3_write(struct Curl_easy *data, 174 const char *str, size_t nread, bool is_eos); 175 176 /* 177 * POP3 protocol handler. 178 */ 179 180 const struct Curl_handler Curl_handler_pop3 = { 181 "pop3", /* scheme */ 182 pop3_setup_connection, /* setup_connection */ 183 pop3_do, /* do_it */ 184 pop3_done, /* done */ 185 ZERO_NULL, /* do_more */ 186 pop3_connect, /* connect_it */ 187 pop3_multi_statemach, /* connecting */ 188 pop3_doing, /* doing */ 189 pop3_getsock, /* proto_getsock */ 190 pop3_getsock, /* doing_getsock */ 191 ZERO_NULL, /* domore_getsock */ 192 ZERO_NULL, /* perform_getsock */ 193 pop3_disconnect, /* disconnect */ 194 pop3_write, /* write_resp */ 195 ZERO_NULL, /* write_resp_hd */ 196 ZERO_NULL, /* connection_check */ 197 ZERO_NULL, /* attach connection */ 198 ZERO_NULL, /* follow */ 199 PORT_POP3, /* defport */ 200 CURLPROTO_POP3, /* protocol */ 201 CURLPROTO_POP3, /* family */ 202 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ 203 PROTOPT_URLOPTIONS 204 }; 205 206 #ifdef USE_SSL 207 /* 208 * POP3S protocol handler. 209 */ 210 211 const struct Curl_handler Curl_handler_pop3s = { 212 "pop3s", /* scheme */ 213 pop3_setup_connection, /* setup_connection */ 214 pop3_do, /* do_it */ 215 pop3_done, /* done */ 216 ZERO_NULL, /* do_more */ 217 pop3_connect, /* connect_it */ 218 pop3_multi_statemach, /* connecting */ 219 pop3_doing, /* doing */ 220 pop3_getsock, /* proto_getsock */ 221 pop3_getsock, /* doing_getsock */ 222 ZERO_NULL, /* domore_getsock */ 223 ZERO_NULL, /* perform_getsock */ 224 pop3_disconnect, /* disconnect */ 225 pop3_write, /* write_resp */ 226 ZERO_NULL, /* write_resp_hd */ 227 ZERO_NULL, /* connection_check */ 228 ZERO_NULL, /* attach connection */ 229 ZERO_NULL, /* follow */ 230 PORT_POP3S, /* defport */ 231 CURLPROTO_POP3S, /* protocol */ 232 CURLPROTO_POP3, /* family */ 233 PROTOPT_CLOSEACTION | PROTOPT_SSL 234 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */ 235 }; 236 #endif 237 238 /* SASL parameters for the pop3 protocol */ 239 static const struct SASLproto saslpop3 = { 240 "pop", /* The service name */ 241 pop3_perform_auth, /* Send authentication command */ 242 pop3_continue_auth, /* Send authentication continuation */ 243 pop3_cancel_auth, /* Send authentication cancellation */ 244 pop3_get_message, /* Get SASL response message */ 245 255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */ 246 '*', /* Code received when continuation is expected */ 247 '+', /* Code to receive upon authentication success */ 248 SASL_AUTH_DEFAULT, /* Default mechanisms */ 249 SASL_FLAG_BASE64 /* Configuration flags */ 250 }; 251 252 struct pop3_cmd { 253 const char *name; 254 unsigned short nlen; 255 BIT(multiline); /* response is multi-line with last '.' line */ 256 BIT(multiline_with_args); /* is multi-line when command has args */ 257 }; 258 259 static const struct pop3_cmd pop3cmds[] = { 260 { "APOP", 4, FALSE, FALSE }, 261 { "AUTH", 4, FALSE, FALSE }, 262 { "CAPA", 4, TRUE, TRUE }, 263 { "DELE", 4, FALSE, FALSE }, 264 { "LIST", 4, TRUE, FALSE }, 265 { "MSG", 3, TRUE, TRUE }, 266 { "NOOP", 4, FALSE, FALSE }, 267 { "PASS", 4, FALSE, FALSE }, 268 { "QUIT", 4, FALSE, FALSE }, 269 { "RETR", 4, TRUE, TRUE }, 270 { "RSET", 4, FALSE, FALSE }, 271 { "STAT", 4, FALSE, FALSE }, 272 { "STLS", 4, FALSE, FALSE }, 273 { "TOP", 3, TRUE, TRUE }, 274 { "UIDL", 4, TRUE, FALSE }, 275 { "USER", 4, FALSE, FALSE }, 276 { "UTF8", 4, FALSE, FALSE }, 277 { "XTND", 4, TRUE, TRUE }, 278 }; 279 280 /* Return iff a command is defined as "multi-line" (RFC 1939), 281 * has a response terminated by a last line with a '.'. 282 */ 283 static bool pop3_is_multiline(const char *cmdline) 284 { 285 size_t i; 286 for(i = 0; i < CURL_ARRAYSIZE(pop3cmds); ++i) { 287 if(curl_strnequal(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) { 288 if(!cmdline[pop3cmds[i].nlen]) 289 return pop3cmds[i].multiline; 290 else if(cmdline[pop3cmds[i].nlen] == ' ') 291 return pop3cmds[i].multiline_with_args; 292 } 293 } 294 /* Unknown command, assume multi-line for backward compatibility with 295 * earlier curl versions that only could do multi-line responses. */ 296 return TRUE; 297 } 298 299 /*********************************************************************** 300 * 301 * pop3_endofresp() 302 * 303 * Checks for an ending POP3 status code at the start of the given string, but 304 * also detects the APOP timestamp from the server greeting and various 305 * capabilities from the CAPA response including the supported authentication 306 * types and allowed SASL mechanisms. 307 */ 308 static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn, 309 const char *line, size_t len, int *resp) 310 { 311 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 312 (void)data; 313 DEBUGASSERT(pop3c); 314 if(!pop3c) /* internal error */ 315 return TRUE; 316 317 /* Do we have an error response? */ 318 if(len >= 4 && !memcmp("-ERR", line, 4)) { 319 *resp = '-'; 320 321 return TRUE; 322 } 323 324 /* Are we processing CAPA command responses? */ 325 if(pop3c->state == POP3_CAPA) { 326 /* Do we have the terminating line? */ 327 if(len >= 1 && line[0] == '.') 328 /* Treat the response as a success */ 329 *resp = '+'; 330 else 331 /* Treat the response as an untagged continuation */ 332 *resp = '*'; 333 334 return TRUE; 335 } 336 337 /* Do we have a success response? */ 338 if(len >= 3 && !memcmp("+OK", line, 3)) { 339 *resp = '+'; 340 341 return TRUE; 342 } 343 344 /* Do we have a continuation response? */ 345 if(len >= 1 && line[0] == '+') { 346 *resp = '*'; 347 348 return TRUE; 349 } 350 351 return FALSE; /* Nothing for us */ 352 } 353 354 /*********************************************************************** 355 * 356 * pop3_get_message() 357 * 358 * Gets the authentication message from the response buffer. 359 */ 360 static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out) 361 { 362 struct pop3_conn *pop3c = 363 Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); 364 char *message; 365 size_t len; 366 367 if(!pop3c) 368 return CURLE_FAILED_INIT; 369 message = curlx_dyn_ptr(&pop3c->pp.recvbuf); 370 len = pop3c->pp.nfinal; 371 if(len > 2) { 372 /* Find the start of the message */ 373 len -= 2; 374 for(message += 2; *message == ' ' || *message == '\t'; message++, len--) 375 ; 376 377 /* Find the end of the message */ 378 while(len--) 379 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && 380 message[len] != '\t') 381 break; 382 383 /* Terminate the message */ 384 message[++len] = '\0'; 385 Curl_bufref_set(out, message, len, NULL); 386 } 387 else 388 /* junk input => zero length output */ 389 Curl_bufref_set(out, "", 0, NULL); 390 391 return CURLE_OK; 392 } 393 394 /*********************************************************************** 395 * 396 * pop3_state() 397 * 398 * This is the ONLY way to change POP3 state! 399 */ 400 static void pop3_state(struct Curl_easy *data, pop3state newstate) 401 { 402 struct pop3_conn *pop3c = 403 Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); 404 if(pop3c) { 405 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 406 /* for debug purposes */ 407 static const char * const names[] = { 408 "STOP", 409 "SERVERGREET", 410 "CAPA", 411 "STARTTLS", 412 "UPGRADETLS", 413 "AUTH", 414 "APOP", 415 "USER", 416 "PASS", 417 "COMMAND", 418 "QUIT", 419 /* LAST */ 420 }; 421 422 if(pop3c->state != newstate) 423 infof(data, "POP3 %p state change from %s to %s", 424 (void *)pop3c, names[pop3c->state], names[newstate]); 425 #endif 426 427 pop3c->state = newstate; 428 } 429 } 430 431 /*********************************************************************** 432 * 433 * pop3_perform_capa() 434 * 435 * Sends the CAPA command in order to obtain a list of server side supported 436 * capabilities. 437 */ 438 static CURLcode pop3_perform_capa(struct Curl_easy *data, 439 struct connectdata *conn) 440 { 441 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 442 CURLcode result = CURLE_OK; 443 444 if(!pop3c) 445 return CURLE_FAILED_INIT; 446 447 pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */ 448 pop3c->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */ 449 pop3c->tls_supported = FALSE; /* Clear the TLS capability */ 450 451 /* Send the CAPA command */ 452 result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA"); 453 454 if(!result) 455 pop3_state(data, POP3_CAPA); 456 457 return result; 458 } 459 460 /*********************************************************************** 461 * 462 * pop3_perform_starttls() 463 * 464 * Sends the STLS command to start the upgrade to TLS. 465 */ 466 static CURLcode pop3_perform_starttls(struct Curl_easy *data, 467 struct connectdata *conn) 468 { 469 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 470 CURLcode result; 471 472 if(!pop3c) 473 return CURLE_FAILED_INIT; 474 475 /* Send the STLS command */ 476 result = Curl_pp_sendf(data, &pop3c->pp, "%s", "STLS"); 477 if(!result) 478 pop3_state(data, POP3_STARTTLS); 479 480 return result; 481 } 482 483 /*********************************************************************** 484 * 485 * pop3_perform_upgrade_tls() 486 * 487 * Performs the upgrade to TLS. 488 */ 489 static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, 490 struct connectdata *conn) 491 { 492 #ifdef USE_SSL 493 /* Start the SSL connection */ 494 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 495 CURLcode result; 496 bool ssldone = FALSE; 497 498 if(!pop3c) 499 return CURLE_FAILED_INIT; 500 501 if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { 502 result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); 503 if(result) 504 goto out; 505 /* Change the connection handler */ 506 conn->handler = &Curl_handler_pop3s; 507 } 508 509 DEBUGASSERT(!pop3c->ssldone); 510 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); 511 DEBUGF(infof(data, "pop3_perform_upgrade_tls, connect -> %d, %d", 512 result, ssldone)); 513 if(!result && ssldone) { 514 pop3c->ssldone = ssldone; 515 /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */ 516 result = pop3_perform_capa(data, conn); 517 } 518 out: 519 return result; 520 #else 521 (void)data; 522 (void)conn; 523 return CURLE_NOT_BUILT_IN; 524 #endif 525 } 526 527 /*********************************************************************** 528 * 529 * pop3_perform_user() 530 * 531 * Sends a clear text USER command to authenticate with. 532 */ 533 static CURLcode pop3_perform_user(struct Curl_easy *data, 534 struct connectdata *conn) 535 { 536 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 537 CURLcode result = CURLE_OK; 538 539 if(!pop3c) 540 return CURLE_FAILED_INIT; 541 542 /* Check we have a username and password to authenticate with and end the 543 connect phase if we do not */ 544 if(!data->state.aptr.user) { 545 pop3_state(data, POP3_STOP); 546 547 return result; 548 } 549 550 /* Send the USER command */ 551 result = Curl_pp_sendf(data, &pop3c->pp, "USER %s", 552 conn->user ? conn->user : ""); 553 if(!result) 554 pop3_state(data, POP3_USER); 555 556 return result; 557 } 558 559 #ifndef CURL_DISABLE_DIGEST_AUTH 560 /*********************************************************************** 561 * 562 * pop3_perform_apop() 563 * 564 * Sends an APOP command to authenticate with. 565 */ 566 static CURLcode pop3_perform_apop(struct Curl_easy *data, 567 struct connectdata *conn) 568 { 569 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 570 CURLcode result = CURLE_OK; 571 size_t i; 572 struct MD5_context *ctxt; 573 unsigned char digest[MD5_DIGEST_LEN]; 574 char secret[2 * MD5_DIGEST_LEN + 1]; 575 576 if(!pop3c) 577 return CURLE_FAILED_INIT; 578 579 /* Check we have a username and password to authenticate with and end the 580 connect phase if we do not */ 581 if(!data->state.aptr.user) { 582 pop3_state(data, POP3_STOP); 583 584 return result; 585 } 586 587 /* Create the digest */ 588 ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); 589 if(!ctxt) 590 return CURLE_OUT_OF_MEMORY; 591 592 Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp, 593 curlx_uztoui(strlen(pop3c->apoptimestamp))); 594 595 Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd, 596 curlx_uztoui(strlen(conn->passwd))); 597 598 /* Finalise the digest */ 599 Curl_MD5_final(ctxt, digest); 600 601 /* Convert the calculated 16 octet digest into a 32 byte hex string */ 602 for(i = 0; i < MD5_DIGEST_LEN; i++) 603 msnprintf(&secret[2 * i], 3, "%02x", digest[i]); 604 605 result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); 606 607 if(!result) 608 pop3_state(data, POP3_APOP); 609 610 return result; 611 } 612 #endif 613 614 /*********************************************************************** 615 * 616 * pop3_perform_auth() 617 * 618 * Sends an AUTH command allowing the client to login with the given SASL 619 * authentication mechanism. 620 */ 621 static CURLcode pop3_perform_auth(struct Curl_easy *data, 622 const char *mech, 623 const struct bufref *initresp) 624 { 625 struct pop3_conn *pop3c = 626 Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); 627 CURLcode result = CURLE_OK; 628 const char *ir = (const char *) Curl_bufref_ptr(initresp); 629 630 if(!pop3c) 631 return CURLE_FAILED_INIT; 632 633 if(ir) { /* AUTH <mech> ...<crlf> */ 634 /* Send the AUTH command with the initial response */ 635 result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, ir); 636 } 637 else { 638 /* Send the AUTH command */ 639 result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s", mech); 640 } 641 642 return result; 643 } 644 645 /*********************************************************************** 646 * 647 * pop3_continue_auth() 648 * 649 * Sends SASL continuation data. 650 */ 651 static CURLcode pop3_continue_auth(struct Curl_easy *data, 652 const char *mech, 653 const struct bufref *resp) 654 { 655 struct pop3_conn *pop3c = 656 Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); 657 658 (void)mech; 659 if(!pop3c) 660 return CURLE_FAILED_INIT; 661 662 return Curl_pp_sendf(data, &pop3c->pp, 663 "%s", (const char *) Curl_bufref_ptr(resp)); 664 } 665 666 /*********************************************************************** 667 * 668 * pop3_cancel_auth() 669 * 670 * Sends SASL cancellation. 671 */ 672 static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech) 673 { 674 struct pop3_conn *pop3c = 675 Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); 676 677 (void)mech; 678 if(!pop3c) 679 return CURLE_FAILED_INIT; 680 681 return Curl_pp_sendf(data, &pop3c->pp, "*"); 682 } 683 684 /*********************************************************************** 685 * 686 * pop3_perform_authentication() 687 * 688 * Initiates the authentication sequence, with the appropriate SASL 689 * authentication mechanism, falling back to APOP and clear text should a 690 * common mechanism not be available between the client and server. 691 */ 692 static CURLcode pop3_perform_authentication(struct Curl_easy *data, 693 struct connectdata *conn) 694 { 695 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 696 CURLcode result = CURLE_OK; 697 saslprogress progress = SASL_IDLE; 698 699 if(!pop3c) 700 return CURLE_FAILED_INIT; 701 702 /* Check we have enough data to authenticate with and end the 703 connect phase if we do not */ 704 if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) { 705 pop3_state(data, POP3_STOP); 706 return result; 707 } 708 709 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) { 710 /* Calculate the SASL login details */ 711 result = Curl_sasl_start(&pop3c->sasl, data, FALSE, &progress); 712 713 if(!result) 714 if(progress == SASL_INPROGRESS) 715 pop3_state(data, POP3_AUTH); 716 } 717 718 if(!result && progress == SASL_IDLE) { 719 #ifndef CURL_DISABLE_DIGEST_AUTH 720 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) 721 /* Perform APOP authentication */ 722 result = pop3_perform_apop(data, conn); 723 else 724 #endif 725 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT) 726 /* Perform clear text authentication */ 727 result = pop3_perform_user(data, conn); 728 else 729 result = Curl_sasl_is_blocked(&pop3c->sasl, data); 730 } 731 732 return result; 733 } 734 735 /*********************************************************************** 736 * 737 * pop3_perform_command() 738 * 739 * Sends a POP3 based command. 740 */ 741 static CURLcode pop3_perform_command(struct Curl_easy *data) 742 { 743 CURLcode result = CURLE_OK; 744 struct connectdata *conn = data->conn; 745 struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); 746 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 747 const char *command = NULL; 748 749 if(!pop3 || !pop3c) 750 return CURLE_FAILED_INIT; 751 752 /* Calculate the default command */ 753 if(pop3->id[0] == '\0' || data->set.list_only) { 754 command = "LIST"; 755 756 if(pop3->id[0] != '\0') 757 /* Message specific LIST so skip the BODY transfer */ 758 pop3->transfer = PPTRANSFER_INFO; 759 } 760 else 761 command = "RETR"; 762 763 if(pop3->custom && pop3->custom[0] != '\0') 764 command = pop3->custom; 765 766 /* Send the command */ 767 if(pop3->id[0] != '\0') 768 result = Curl_pp_sendf(data, &pop3c->pp, "%s %s", command, pop3->id); 769 else 770 result = Curl_pp_sendf(data, &pop3c->pp, "%s", command); 771 772 if(!result) { 773 pop3_state(data, POP3_COMMAND); 774 data->req.no_body = !pop3_is_multiline(command); 775 } 776 777 return result; 778 } 779 780 /*********************************************************************** 781 * 782 * pop3_perform_quit() 783 * 784 * Performs the quit action prior to sclose() be called. 785 */ 786 static CURLcode pop3_perform_quit(struct Curl_easy *data, 787 struct connectdata *conn) 788 { 789 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 790 CURLcode result; 791 792 if(!pop3c) 793 return CURLE_FAILED_INIT; 794 795 /* Send the QUIT command */ 796 result = Curl_pp_sendf(data, &pop3c->pp, "%s", "QUIT"); 797 if(!result) 798 pop3_state(data, POP3_QUIT); 799 800 return result; 801 } 802 803 /* For the initial server greeting */ 804 static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data, 805 int pop3code, 806 pop3state instate) 807 { 808 CURLcode result = CURLE_OK; 809 struct connectdata *conn = data->conn; 810 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 811 const char *line; 812 size_t len; 813 814 (void)instate; /* no use for this yet */ 815 if(!pop3c) 816 return CURLE_FAILED_INIT; 817 818 line = curlx_dyn_ptr(&pop3c->pp.recvbuf); 819 len = pop3c->pp.nfinal; 820 821 if(pop3code != '+') { 822 failf(data, "Got unexpected pop3-server response"); 823 result = CURLE_WEIRD_SERVER_REPLY; 824 } 825 else if(len > 3) { 826 /* Does the server support APOP authentication? */ 827 char *lt; 828 char *gt = NULL; 829 830 /* Look for the APOP timestamp */ 831 lt = memchr(line, '<', len); 832 if(lt) 833 /* search the remainder for '>' */ 834 gt = memchr(lt, '>', len - (lt - line)); 835 if(gt) { 836 /* the length of the timestamp, including the brackets */ 837 size_t timestamplen = gt - lt + 1; 838 char *at = memchr(lt, '@', timestamplen); 839 /* If the timestamp does not contain '@' it is not (as required by 840 RFC-1939) conformant to the RFC-822 message id syntax, and we 841 therefore do not use APOP authentication. */ 842 if(at) { 843 /* dupe the timestamp */ 844 pop3c->apoptimestamp = Curl_memdup0(lt, timestamplen); 845 if(!pop3c->apoptimestamp) 846 return CURLE_OUT_OF_MEMORY; 847 /* Store the APOP capability */ 848 pop3c->authtypes |= POP3_TYPE_APOP; 849 } 850 } 851 852 if(!result) 853 result = pop3_perform_capa(data, conn); 854 } 855 856 return result; 857 } 858 859 /* For CAPA responses */ 860 static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code, 861 pop3state instate) 862 { 863 CURLcode result = CURLE_OK; 864 struct connectdata *conn = data->conn; 865 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 866 const char *line; 867 size_t len; 868 869 (void)instate; /* no use for this yet */ 870 if(!pop3c) 871 return CURLE_FAILED_INIT; 872 873 line = curlx_dyn_ptr(&pop3c->pp.recvbuf); 874 len = pop3c->pp.nfinal; 875 876 /* Do we have an untagged continuation response? */ 877 if(pop3code == '*') { 878 /* Does the server support the STLS capability? */ 879 if(len >= 4 && !memcmp(line, "STLS", 4)) 880 pop3c->tls_supported = TRUE; 881 882 /* Does the server support clear text authentication? */ 883 else if(len >= 4 && !memcmp(line, "USER", 4)) 884 pop3c->authtypes |= POP3_TYPE_CLEARTEXT; 885 886 /* Does the server support SASL based authentication? */ 887 else if(len >= 5 && !memcmp(line, "SASL ", 5)) { 888 pop3c->authtypes |= POP3_TYPE_SASL; 889 890 /* Advance past the SASL keyword */ 891 line += 5; 892 len -= 5; 893 894 /* Loop through the data line */ 895 for(;;) { 896 size_t llen; 897 size_t wordlen; 898 unsigned short mechbit; 899 900 while(len && 901 (*line == ' ' || *line == '\t' || 902 *line == '\r' || *line == '\n')) { 903 904 line++; 905 len--; 906 } 907 908 if(!len) 909 break; 910 911 /* Extract the word */ 912 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && 913 line[wordlen] != '\t' && line[wordlen] != '\r' && 914 line[wordlen] != '\n';) 915 wordlen++; 916 917 /* Test the word for a matching authentication mechanism */ 918 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen); 919 if(mechbit && llen == wordlen) 920 pop3c->sasl.authmechs |= mechbit; 921 922 line += wordlen; 923 len -= wordlen; 924 } 925 } 926 } 927 else { 928 /* Clear text is supported when CAPA is not recognised */ 929 if(pop3code != '+') 930 pop3c->authtypes |= POP3_TYPE_CLEARTEXT; 931 932 if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET)) 933 result = pop3_perform_authentication(data, conn); 934 else if(pop3code == '+' && pop3c->tls_supported) 935 /* Switch to TLS connection now */ 936 result = pop3_perform_starttls(data, conn); 937 else if(data->set.use_ssl <= CURLUSESSL_TRY) 938 /* Fallback and carry on with authentication */ 939 result = pop3_perform_authentication(data, conn); 940 else { 941 failf(data, "STLS not supported."); 942 result = CURLE_USE_SSL_FAILED; 943 } 944 } 945 946 return result; 947 } 948 949 /* For STARTTLS responses */ 950 static CURLcode pop3_state_starttls_resp(struct Curl_easy *data, 951 struct connectdata *conn, 952 int pop3code, 953 pop3state instate) 954 { 955 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 956 CURLcode result = CURLE_OK; 957 (void)instate; /* no use for this yet */ 958 959 if(!pop3c) 960 return CURLE_FAILED_INIT; 961 962 /* Pipelining in response is forbidden. */ 963 if(pop3c->pp.overflow) 964 return CURLE_WEIRD_SERVER_REPLY; 965 966 if(pop3code != '+') { 967 if(data->set.use_ssl != CURLUSESSL_TRY) { 968 failf(data, "STARTTLS denied"); 969 result = CURLE_USE_SSL_FAILED; 970 } 971 else 972 result = pop3_perform_authentication(data, conn); 973 } 974 else 975 pop3_state(data, POP3_UPGRADETLS); 976 977 return result; 978 } 979 980 /* For SASL authentication responses */ 981 static CURLcode pop3_state_auth_resp(struct Curl_easy *data, 982 int pop3code, 983 pop3state instate) 984 { 985 CURLcode result = CURLE_OK; 986 struct connectdata *conn = data->conn; 987 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 988 saslprogress progress; 989 990 (void)instate; /* no use for this yet */ 991 if(!pop3c) 992 return CURLE_FAILED_INIT; 993 994 result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress); 995 if(!result) 996 switch(progress) { 997 case SASL_DONE: 998 pop3_state(data, POP3_STOP); /* Authenticated */ 999 break; 1000 case SASL_IDLE: /* No mechanism left after cancellation */ 1001 #ifndef CURL_DISABLE_DIGEST_AUTH 1002 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP) 1003 /* Perform APOP authentication */ 1004 result = pop3_perform_apop(data, conn); 1005 else 1006 #endif 1007 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT) 1008 /* Perform clear text authentication */ 1009 result = pop3_perform_user(data, conn); 1010 else { 1011 failf(data, "Authentication cancelled"); 1012 result = CURLE_LOGIN_DENIED; 1013 } 1014 break; 1015 default: 1016 break; 1017 } 1018 1019 return result; 1020 } 1021 1022 #ifndef CURL_DISABLE_DIGEST_AUTH 1023 /* For APOP responses */ 1024 static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code, 1025 pop3state instate) 1026 { 1027 CURLcode result = CURLE_OK; 1028 (void)instate; /* no use for this yet */ 1029 1030 if(pop3code != '+') { 1031 failf(data, "Authentication failed: %d", pop3code); 1032 result = CURLE_LOGIN_DENIED; 1033 } 1034 else 1035 /* End of connect phase */ 1036 pop3_state(data, POP3_STOP); 1037 1038 return result; 1039 } 1040 #endif 1041 1042 /* For USER responses */ 1043 static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code, 1044 pop3state instate) 1045 { 1046 CURLcode result = CURLE_OK; 1047 struct connectdata *conn = data->conn; 1048 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1049 (void)instate; /* no use for this yet */ 1050 1051 if(!pop3c) 1052 return CURLE_FAILED_INIT; 1053 1054 if(pop3code != '+') { 1055 failf(data, "Access denied. %c", pop3code); 1056 result = CURLE_LOGIN_DENIED; 1057 } 1058 else 1059 /* Send the PASS command */ 1060 result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", 1061 conn->passwd ? conn->passwd : ""); 1062 if(!result) 1063 pop3_state(data, POP3_PASS); 1064 1065 return result; 1066 } 1067 1068 /* For PASS responses */ 1069 static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code, 1070 pop3state instate) 1071 { 1072 CURLcode result = CURLE_OK; 1073 (void)instate; /* no use for this yet */ 1074 1075 if(pop3code != '+') { 1076 failf(data, "Access denied. %c", pop3code); 1077 result = CURLE_LOGIN_DENIED; 1078 } 1079 else 1080 /* End of connect phase */ 1081 pop3_state(data, POP3_STOP); 1082 1083 return result; 1084 } 1085 1086 /* For command responses */ 1087 static CURLcode pop3_state_command_resp(struct Curl_easy *data, 1088 int pop3code, 1089 pop3state instate) 1090 { 1091 CURLcode result = CURLE_OK; 1092 struct connectdata *conn = data->conn; 1093 struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); 1094 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1095 struct pingpong *pp; 1096 1097 (void)instate; /* no use for this yet */ 1098 if(!pop3 || !pop3c) 1099 return CURLE_FAILED_INIT; 1100 1101 pp = &pop3c->pp; 1102 if(pop3code != '+') { 1103 pop3_state(data, POP3_STOP); 1104 return CURLE_WEIRD_SERVER_REPLY; 1105 } 1106 1107 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the 1108 EOB string so count this is two matching bytes. This is necessary to make 1109 the code detect the EOB if the only data than comes now is %2e CR LF like 1110 when there is no body to return. */ 1111 pop3c->eob = 2; 1112 1113 /* But since this initial CR LF pair is not part of the actual body, we set 1114 the strip counter here so that these bytes will not be delivered. */ 1115 pop3c->strip = 2; 1116 1117 if(pop3->transfer == PPTRANSFER_BODY) { 1118 /* POP3 download */ 1119 Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); 1120 1121 if(pp->overflow) { 1122 /* The recv buffer contains data that is actually body content so send 1123 it as such. Note that there may even be additional "headers" after 1124 the body */ 1125 1126 /* keep only the overflow */ 1127 curlx_dyn_tail(&pp->recvbuf, pp->overflow); 1128 pp->nfinal = 0; /* done */ 1129 1130 if(!data->req.no_body) { 1131 result = pop3_write(data, curlx_dyn_ptr(&pp->recvbuf), 1132 curlx_dyn_len(&pp->recvbuf), FALSE); 1133 if(result) 1134 return result; 1135 } 1136 1137 /* reset the buffer */ 1138 curlx_dyn_reset(&pp->recvbuf); 1139 pp->overflow = 0; 1140 } 1141 } 1142 else 1143 pp->overflow = 0; 1144 1145 /* End of DO phase */ 1146 pop3_state(data, POP3_STOP); 1147 1148 return result; 1149 } 1150 1151 static CURLcode pop3_statemachine(struct Curl_easy *data, 1152 struct connectdata *conn) 1153 { 1154 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1155 CURLcode result = CURLE_OK; 1156 int pop3code; 1157 struct pingpong *pp; 1158 size_t nread = 0; 1159 (void)data; 1160 1161 if(!pop3c) 1162 return CURLE_FAILED_INIT; 1163 1164 pp = &pop3c->pp; 1165 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */ 1166 upgrade_tls: 1167 if(pop3c->state == POP3_UPGRADETLS) { 1168 result = pop3_perform_upgrade_tls(data, conn); 1169 if(result || (pop3c->state == POP3_UPGRADETLS)) 1170 return result; 1171 } 1172 1173 /* Flush any data that needs to be sent */ 1174 if(pp->sendleft) 1175 return Curl_pp_flushsend(data, pp); 1176 1177 do { 1178 /* Read the response from the server */ 1179 result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread); 1180 if(result) 1181 return result; 1182 1183 if(!pop3code) 1184 break; 1185 1186 /* We have now received a full POP3 server response */ 1187 switch(pop3c->state) { 1188 case POP3_SERVERGREET: 1189 result = pop3_state_servergreet_resp(data, pop3code, pop3c->state); 1190 break; 1191 1192 case POP3_CAPA: 1193 result = pop3_state_capa_resp(data, pop3code, pop3c->state); 1194 break; 1195 1196 case POP3_STARTTLS: 1197 result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state); 1198 /* During UPGRADETLS, leave the read loop as we need to connect 1199 * (e.g. TLS handshake) before we continue sending/receiving. */ 1200 if(!result && (pop3c->state == POP3_UPGRADETLS)) 1201 goto upgrade_tls; 1202 break; 1203 1204 case POP3_AUTH: 1205 result = pop3_state_auth_resp(data, pop3code, pop3c->state); 1206 break; 1207 1208 #ifndef CURL_DISABLE_DIGEST_AUTH 1209 case POP3_APOP: 1210 result = pop3_state_apop_resp(data, pop3code, pop3c->state); 1211 break; 1212 #endif 1213 1214 case POP3_USER: 1215 result = pop3_state_user_resp(data, pop3code, pop3c->state); 1216 break; 1217 1218 case POP3_PASS: 1219 result = pop3_state_pass_resp(data, pop3code, pop3c->state); 1220 break; 1221 1222 case POP3_COMMAND: 1223 result = pop3_state_command_resp(data, pop3code, pop3c->state); 1224 break; 1225 1226 case POP3_QUIT: 1227 pop3_state(data, POP3_STOP); 1228 break; 1229 1230 default: 1231 /* internal error */ 1232 pop3_state(data, POP3_STOP); 1233 break; 1234 } 1235 } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp)); 1236 1237 return result; 1238 } 1239 1240 /* Called repeatedly until done from multi.c */ 1241 static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done) 1242 { 1243 CURLcode result = CURLE_OK; 1244 struct connectdata *conn = data->conn; 1245 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1246 1247 if(!pop3c) 1248 return CURLE_FAILED_INIT; 1249 result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE); 1250 *done = (pop3c->state == POP3_STOP); 1251 1252 return result; 1253 } 1254 1255 static CURLcode pop3_block_statemach(struct Curl_easy *data, 1256 struct connectdata *conn, 1257 bool disconnecting) 1258 { 1259 CURLcode result = CURLE_OK; 1260 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1261 1262 if(!pop3c) 1263 return CURLE_FAILED_INIT; 1264 1265 while(pop3c->state != POP3_STOP && !result) 1266 result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting); 1267 1268 return result; 1269 } 1270 1271 /* For the POP3 "protocol connect" and "doing" phases only */ 1272 static int pop3_getsock(struct Curl_easy *data, 1273 struct connectdata *conn, curl_socket_t *socks) 1274 { 1275 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1276 if(pop3c) 1277 return Curl_pp_getsock(data, &pop3c->pp, socks); 1278 return GETSOCK_BLANK; 1279 } 1280 1281 /*********************************************************************** 1282 * 1283 * pop3_connect() 1284 * 1285 * This function should do everything that is to be considered a part of the 1286 * connection phase. 1287 * 1288 * The variable 'done' points to will be TRUE if the protocol-layer connect 1289 * phase is done when this function returns, or FALSE if not. 1290 */ 1291 static CURLcode pop3_connect(struct Curl_easy *data, bool *done) 1292 { 1293 CURLcode result = CURLE_OK; 1294 struct connectdata *conn = data->conn; 1295 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1296 struct pingpong *pp = pop3c ? &pop3c->pp : NULL; 1297 1298 *done = FALSE; /* default to not done yet */ 1299 if(!pop3c) 1300 return CURLE_FAILED_INIT; 1301 1302 /* We always support persistent connections in POP3 */ 1303 connkeep(conn, "POP3 default"); 1304 1305 PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp); 1306 1307 /* Set the default preferred authentication type and mechanism */ 1308 pop3c->preftype = POP3_TYPE_ANY; 1309 Curl_sasl_init(&pop3c->sasl, data, &saslpop3); 1310 1311 /* Initialise the pingpong layer */ 1312 Curl_pp_init(pp); 1313 1314 /* Parse the URL options */ 1315 result = pop3_parse_url_options(conn); 1316 if(result) 1317 return result; 1318 1319 /* Start off waiting for the server greeting response */ 1320 pop3_state(data, POP3_SERVERGREET); 1321 1322 result = pop3_multi_statemach(data, done); 1323 1324 return result; 1325 } 1326 1327 /*********************************************************************** 1328 * 1329 * pop3_done() 1330 * 1331 * The DONE function. This does what needs to be done after a single DO has 1332 * performed. 1333 * 1334 * Input argument is already checked for validity. 1335 */ 1336 static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, 1337 bool premature) 1338 { 1339 CURLcode result = CURLE_OK; 1340 struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); 1341 1342 (void)premature; 1343 1344 if(!pop3) 1345 return CURLE_OK; 1346 1347 if(status) { 1348 connclose(data->conn, "POP3 done with bad status"); 1349 result = status; /* use the already set error code */ 1350 } 1351 1352 /* Cleanup our per-request based variables */ 1353 Curl_safefree(pop3->id); 1354 Curl_safefree(pop3->custom); 1355 1356 /* Clear the transfer mode for the next request */ 1357 pop3->transfer = PPTRANSFER_BODY; 1358 1359 return result; 1360 } 1361 1362 /*********************************************************************** 1363 * 1364 * pop3_perform() 1365 * 1366 * This is the actual DO function for POP3. Get a message/listing according to 1367 * the options previously setup. 1368 */ 1369 static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, 1370 bool *dophase_done) 1371 { 1372 /* This is POP3 and no proxy */ 1373 CURLcode result = CURLE_OK; 1374 struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); 1375 1376 if(!pop3) 1377 return CURLE_FAILED_INIT; 1378 1379 DEBUGF(infof(data, "DO phase starts")); 1380 1381 if(data->req.no_body) { 1382 /* Requested no body means no transfer */ 1383 pop3->transfer = PPTRANSFER_INFO; 1384 } 1385 1386 *dophase_done = FALSE; /* not done yet */ 1387 1388 /* Start the first command in the DO phase */ 1389 result = pop3_perform_command(data); 1390 if(result) 1391 return result; 1392 1393 /* Run the state-machine */ 1394 result = pop3_multi_statemach(data, dophase_done); 1395 *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); 1396 1397 if(*dophase_done) 1398 DEBUGF(infof(data, "DO phase is complete")); 1399 1400 return result; 1401 } 1402 1403 /*********************************************************************** 1404 * 1405 * pop3_do() 1406 * 1407 * This function is registered as 'curl_do' function. It decodes the path 1408 * parts etc as a wrapper to the actual DO function (pop3_perform). 1409 * 1410 * The input argument is already checked for validity. 1411 */ 1412 static CURLcode pop3_do(struct Curl_easy *data, bool *done) 1413 { 1414 CURLcode result = CURLE_OK; 1415 *done = FALSE; /* default to false */ 1416 1417 /* Parse the URL path */ 1418 result = pop3_parse_url_path(data); 1419 if(result) 1420 return result; 1421 1422 /* Parse the custom request */ 1423 result = pop3_parse_custom_request(data); 1424 if(result) 1425 return result; 1426 1427 result = pop3_regular_transfer(data, done); 1428 1429 return result; 1430 } 1431 1432 /*********************************************************************** 1433 * 1434 * pop3_disconnect() 1435 * 1436 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection 1437 * resources. BLOCKING. 1438 */ 1439 static CURLcode pop3_disconnect(struct Curl_easy *data, 1440 struct connectdata *conn, bool dead_connection) 1441 { 1442 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1443 (void)data; 1444 1445 if(!pop3c) 1446 return CURLE_FAILED_INIT; 1447 1448 /* We cannot send quit unconditionally. If this connection is stale or 1449 bad in any way, sending quit and waiting around here will make the 1450 disconnect wait in vain and cause more problems than we need to. */ 1451 1452 if(!dead_connection && conn->bits.protoconnstart && 1453 !Curl_pp_needs_flush(data, &pop3c->pp)) { 1454 if(!pop3_perform_quit(data, conn)) 1455 (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */ 1456 } 1457 1458 /* Disconnect from the server */ 1459 Curl_pp_disconnect(&pop3c->pp); 1460 1461 /* Cleanup our connection based variables */ 1462 Curl_safefree(pop3c->apoptimestamp); 1463 1464 return CURLE_OK; 1465 } 1466 1467 /* Call this when the DO phase has completed */ 1468 static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected) 1469 { 1470 (void)data; 1471 (void)connected; 1472 1473 return CURLE_OK; 1474 } 1475 1476 /* Called from multi.c while DOing */ 1477 static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done) 1478 { 1479 CURLcode result = pop3_multi_statemach(data, dophase_done); 1480 1481 if(result) 1482 DEBUGF(infof(data, "DO phase failed")); 1483 else if(*dophase_done) { 1484 result = pop3_dophase_done(data, FALSE /* not connected */); 1485 1486 DEBUGF(infof(data, "DO phase is complete")); 1487 } 1488 1489 return result; 1490 } 1491 1492 /*********************************************************************** 1493 * 1494 * pop3_regular_transfer() 1495 * 1496 * The input argument is already checked for validity. 1497 * 1498 * Performs all commands done before a regular transfer between a local and a 1499 * remote host. 1500 */ 1501 static CURLcode pop3_regular_transfer(struct Curl_easy *data, 1502 bool *dophase_done) 1503 { 1504 CURLcode result = CURLE_OK; 1505 bool connected = FALSE; 1506 1507 /* Make sure size is unknown at this point */ 1508 data->req.size = -1; 1509 1510 /* Set the progress data */ 1511 Curl_pgrsSetUploadCounter(data, 0); 1512 Curl_pgrsSetDownloadCounter(data, 0); 1513 Curl_pgrsSetUploadSize(data, -1); 1514 Curl_pgrsSetDownloadSize(data, -1); 1515 1516 /* Carry out the perform */ 1517 result = pop3_perform(data, &connected, dophase_done); 1518 1519 /* Perform post DO phase operations if necessary */ 1520 if(!result && *dophase_done) 1521 result = pop3_dophase_done(data, connected); 1522 1523 return result; 1524 } 1525 1526 static void pop3_easy_dtor(void *key, size_t klen, void *entry) 1527 { 1528 struct POP3 *pop3 = entry; 1529 (void)key; 1530 (void)klen; 1531 DEBUGASSERT(pop3); 1532 /* Cleanup our per-request based variables */ 1533 Curl_safefree(pop3->id); 1534 Curl_safefree(pop3->custom); 1535 free(pop3); 1536 } 1537 1538 static void pop3_conn_dtor(void *key, size_t klen, void *entry) 1539 { 1540 struct pop3_conn *pop3c = entry; 1541 (void)key; 1542 (void)klen; 1543 DEBUGASSERT(pop3c); 1544 Curl_pp_disconnect(&pop3c->pp); 1545 Curl_safefree(pop3c->apoptimestamp); 1546 free(pop3c); 1547 } 1548 1549 static CURLcode pop3_setup_connection(struct Curl_easy *data, 1550 struct connectdata *conn) 1551 { 1552 struct pop3_conn *pop3c; 1553 struct POP3 *pop3 = calloc(1, sizeof(*pop3)); 1554 if(!pop3 || 1555 Curl_meta_set(data, CURL_META_POP3_EASY, pop3, pop3_easy_dtor)) 1556 return CURLE_OUT_OF_MEMORY; 1557 1558 pop3c = calloc(1, sizeof(*pop3c)); 1559 if(!pop3c || 1560 Curl_conn_meta_set(conn, CURL_META_POP3_CONN, pop3c, pop3_conn_dtor)) 1561 return CURLE_OUT_OF_MEMORY; 1562 1563 return CURLE_OK; 1564 } 1565 1566 /*********************************************************************** 1567 * 1568 * pop3_parse_url_options() 1569 * 1570 * Parse the URL login options. 1571 */ 1572 static CURLcode pop3_parse_url_options(struct connectdata *conn) 1573 { 1574 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1575 CURLcode result = CURLE_OK; 1576 const char *ptr = conn->options; 1577 1578 if(!pop3c) 1579 return CURLE_FAILED_INIT; 1580 1581 while(!result && ptr && *ptr) { 1582 const char *key = ptr; 1583 const char *value; 1584 1585 while(*ptr && *ptr != '=') 1586 ptr++; 1587 1588 value = ptr + 1; 1589 1590 while(*ptr && *ptr != ';') 1591 ptr++; 1592 1593 if(curl_strnequal(key, "AUTH=", 5)) { 1594 result = Curl_sasl_parse_url_auth_option(&pop3c->sasl, 1595 value, ptr - value); 1596 1597 if(result && curl_strnequal(value, "+APOP", ptr - value)) { 1598 pop3c->preftype = POP3_TYPE_APOP; 1599 pop3c->sasl.prefmech = SASL_AUTH_NONE; 1600 result = CURLE_OK; 1601 } 1602 } 1603 else 1604 result = CURLE_URL_MALFORMAT; 1605 1606 if(*ptr == ';') 1607 ptr++; 1608 } 1609 1610 if(pop3c->preftype != POP3_TYPE_APOP) 1611 switch(pop3c->sasl.prefmech) { 1612 case SASL_AUTH_NONE: 1613 pop3c->preftype = POP3_TYPE_NONE; 1614 break; 1615 case SASL_AUTH_DEFAULT: 1616 pop3c->preftype = POP3_TYPE_ANY; 1617 break; 1618 default: 1619 pop3c->preftype = POP3_TYPE_SASL; 1620 break; 1621 } 1622 1623 return result; 1624 } 1625 1626 /*********************************************************************** 1627 * 1628 * pop3_parse_url_path() 1629 * 1630 * Parse the URL path into separate path components. 1631 */ 1632 static CURLcode pop3_parse_url_path(struct Curl_easy *data) 1633 { 1634 /* The POP3 struct is already initialised in pop3_connect() */ 1635 struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); 1636 const char *path = &data->state.up.path[1]; /* skip leading path */ 1637 1638 if(!pop3) 1639 return CURLE_FAILED_INIT; 1640 /* URL decode the path for the message ID */ 1641 return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL); 1642 } 1643 1644 /*********************************************************************** 1645 * 1646 * pop3_parse_custom_request() 1647 * 1648 * Parse the custom request. 1649 */ 1650 static CURLcode pop3_parse_custom_request(struct Curl_easy *data) 1651 { 1652 CURLcode result = CURLE_OK; 1653 struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY); 1654 const char *custom = data->set.str[STRING_CUSTOMREQUEST]; 1655 1656 if(!pop3) 1657 return CURLE_FAILED_INIT; 1658 /* URL decode the custom request */ 1659 if(custom) 1660 result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL); 1661 1662 return result; 1663 } 1664 1665 /*********************************************************************** 1666 * 1667 * pop3_write() 1668 * 1669 * This function scans the body after the end-of-body and writes everything 1670 * until the end is found. 1671 */ 1672 static CURLcode pop3_write(struct Curl_easy *data, const char *str, 1673 size_t nread, bool is_eos) 1674 { 1675 /* This code could be made into a special function in the handler struct */ 1676 CURLcode result = CURLE_OK; 1677 struct SingleRequest *k = &data->req; 1678 struct connectdata *conn = data->conn; 1679 struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); 1680 bool strip_dot = FALSE; 1681 size_t last = 0; 1682 size_t i; 1683 (void)is_eos; 1684 1685 if(!pop3c) 1686 return CURLE_FAILED_INIT; 1687 1688 /* Search through the buffer looking for the end-of-body marker which is 1689 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches 1690 the eob so the server will have prefixed it with an extra dot which we 1691 need to strip out. Additionally the marker could of course be spread out 1692 over 5 different data chunks. */ 1693 for(i = 0; i < nread; i++) { 1694 size_t prev = pop3c->eob; 1695 1696 switch(str[i]) { 1697 case 0x0d: 1698 if(pop3c->eob == 0) { 1699 pop3c->eob++; 1700 1701 if(i) { 1702 /* Write out the body part that did not match */ 1703 result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], 1704 i - last); 1705 1706 if(result) 1707 return result; 1708 1709 last = i; 1710 } 1711 } 1712 else if(pop3c->eob == 3) 1713 pop3c->eob++; 1714 else 1715 /* If the character match was not at position 0 or 3 then restart the 1716 pattern matching */ 1717 pop3c->eob = 1; 1718 break; 1719 1720 case 0x0a: 1721 if(pop3c->eob == 1 || pop3c->eob == 4) 1722 pop3c->eob++; 1723 else 1724 /* If the character match was not at position 1 or 4 then start the 1725 search again */ 1726 pop3c->eob = 0; 1727 break; 1728 1729 case 0x2e: 1730 if(pop3c->eob == 2) 1731 pop3c->eob++; 1732 else if(pop3c->eob == 3) { 1733 /* We have an extra dot after the CRLF which we need to strip off */ 1734 strip_dot = TRUE; 1735 pop3c->eob = 0; 1736 } 1737 else 1738 /* If the character match was not at position 2 then start the search 1739 again */ 1740 pop3c->eob = 0; 1741 break; 1742 1743 default: 1744 pop3c->eob = 0; 1745 break; 1746 } 1747 1748 /* Did we have a partial match which has subsequently failed? */ 1749 if(prev && prev >= pop3c->eob) { 1750 /* Strip can only be non-zero for the very first mismatch after CRLF 1751 and then both prev and strip are equal and nothing will be output 1752 below */ 1753 while(prev && pop3c->strip) { 1754 prev--; 1755 pop3c->strip--; 1756 } 1757 1758 if(prev) { 1759 /* If the partial match was the CRLF and dot then only write the CRLF 1760 as the server would have inserted the dot */ 1761 if(strip_dot && prev - 1 > 0) { 1762 result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 1763 prev - 1); 1764 } 1765 else if(!strip_dot) { 1766 result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 1767 prev); 1768 } 1769 else { 1770 result = CURLE_OK; 1771 } 1772 1773 if(result) 1774 return result; 1775 1776 last = i; 1777 strip_dot = FALSE; 1778 } 1779 } 1780 } 1781 1782 if(pop3c->eob == POP3_EOB_LEN) { 1783 /* We have a full match so the transfer is done, however we must transfer 1784 the CRLF at the start of the EOB as this is considered to be part of the 1785 message as per RFC-1939, sect. 3 */ 1786 result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2); 1787 1788 k->keepon &= ~KEEP_RECV; 1789 pop3c->eob = 0; 1790 1791 return result; 1792 } 1793 1794 if(pop3c->eob) 1795 /* While EOB is matching nothing should be output */ 1796 return CURLE_OK; 1797 1798 if(nread - last) { 1799 result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last], 1800 nread - last); 1801 } 1802 1803 return result; 1804 } 1805 1806 #endif /* CURL_DISABLE_POP3 */