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