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