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