1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2013, 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 http://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 * RFC5034 POP3 SASL Authentication Mechanism 31 * RFC6749 OAuth 2.0 Authorization Framework 32 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> 33 * 34 ***************************************************************************/ 35 36#include "curl_setup.h" 37 38#ifndef CURL_DISABLE_POP3 39 40#ifdef HAVE_NETINET_IN_H 41#include <netinet/in.h> 42#endif 43#ifdef HAVE_ARPA_INET_H 44#include <arpa/inet.h> 45#endif 46#ifdef HAVE_UTSNAME_H 47#include <sys/utsname.h> 48#endif 49#ifdef HAVE_NETDB_H 50#include <netdb.h> 51#endif 52#ifdef __VMS 53#include <in.h> 54#include <inet.h> 55#endif 56 57#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) 58#undef in_addr_t 59#define in_addr_t unsigned long 60#endif 61 62#include <curl/curl.h> 63#include "urldata.h" 64#include "sendf.h" 65#include "if2ip.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 74#include "strtoofft.h" 75#include "strequal.h" 76#include "vtls/vtls.h" 77#include "connect.h" 78#include "strerror.h" 79#include "select.h" 80#include "multiif.h" 81#include "url.h" 82#include "rawstr.h" 83#include "curl_sasl.h" 84#include "curl_md5.h" 85#include "warnless.h" 86 87#define _MPRINTF_REPLACE /* use our functions only */ 88#include <curl/mprintf.h> 89 90#include "curl_memory.h" 91/* The last #include file should be: */ 92#include "memdebug.h" 93 94/* Local API functions */ 95static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done); 96static CURLcode pop3_do(struct connectdata *conn, bool *done); 97static CURLcode pop3_done(struct connectdata *conn, CURLcode status, 98 bool premature); 99static CURLcode pop3_connect(struct connectdata *conn, bool *done); 100static CURLcode pop3_disconnect(struct connectdata *conn, bool dead); 101static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done); 102static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks, 103 int numsocks); 104static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done); 105static CURLcode pop3_setup_connection(struct connectdata *conn); 106static CURLcode pop3_parse_url_options(struct connectdata *conn); 107static CURLcode pop3_parse_url_path(struct connectdata *conn); 108static CURLcode pop3_parse_custom_request(struct connectdata *conn); 109static CURLcode pop3_calc_sasl_details(struct connectdata *conn, 110 const char **mech, 111 char **initresp, size_t *len, 112 pop3state *state1, pop3state *state2); 113 114/* 115 * POP3 protocol handler. 116 */ 117 118const struct Curl_handler Curl_handler_pop3 = { 119 "POP3", /* scheme */ 120 pop3_setup_connection, /* setup_connection */ 121 pop3_do, /* do_it */ 122 pop3_done, /* done */ 123 ZERO_NULL, /* do_more */ 124 pop3_connect, /* connect_it */ 125 pop3_multi_statemach, /* connecting */ 126 pop3_doing, /* doing */ 127 pop3_getsock, /* proto_getsock */ 128 pop3_getsock, /* doing_getsock */ 129 ZERO_NULL, /* domore_getsock */ 130 ZERO_NULL, /* perform_getsock */ 131 pop3_disconnect, /* disconnect */ 132 ZERO_NULL, /* readwrite */ 133 PORT_POP3, /* defport */ 134 CURLPROTO_POP3, /* protocol */ 135 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */ 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, /* readwrite */ 158 PORT_POP3S, /* defport */ 159 CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */ 160 PROTOPT_CLOSEACTION | PROTOPT_SSL 161 | PROTOPT_NOURLQUERY /* flags */ 162}; 163#endif 164 165#ifndef CURL_DISABLE_HTTP 166/* 167 * HTTP-proxyed POP3 protocol handler. 168 */ 169 170static const struct Curl_handler Curl_handler_pop3_proxy = { 171 "POP3", /* scheme */ 172 Curl_http_setup_conn, /* setup_connection */ 173 Curl_http, /* do_it */ 174 Curl_http_done, /* done */ 175 ZERO_NULL, /* do_more */ 176 ZERO_NULL, /* connect_it */ 177 ZERO_NULL, /* connecting */ 178 ZERO_NULL, /* doing */ 179 ZERO_NULL, /* proto_getsock */ 180 ZERO_NULL, /* doing_getsock */ 181 ZERO_NULL, /* domore_getsock */ 182 ZERO_NULL, /* perform_getsock */ 183 ZERO_NULL, /* disconnect */ 184 ZERO_NULL, /* readwrite */ 185 PORT_POP3, /* defport */ 186 CURLPROTO_HTTP, /* protocol */ 187 PROTOPT_NONE /* flags */ 188}; 189 190#ifdef USE_SSL 191/* 192 * HTTP-proxyed POP3S protocol handler. 193 */ 194 195static const struct Curl_handler Curl_handler_pop3s_proxy = { 196 "POP3S", /* scheme */ 197 Curl_http_setup_conn, /* setup_connection */ 198 Curl_http, /* do_it */ 199 Curl_http_done, /* done */ 200 ZERO_NULL, /* do_more */ 201 ZERO_NULL, /* connect_it */ 202 ZERO_NULL, /* connecting */ 203 ZERO_NULL, /* doing */ 204 ZERO_NULL, /* proto_getsock */ 205 ZERO_NULL, /* doing_getsock */ 206 ZERO_NULL, /* domore_getsock */ 207 ZERO_NULL, /* perform_getsock */ 208 ZERO_NULL, /* disconnect */ 209 ZERO_NULL, /* readwrite */ 210 PORT_POP3S, /* defport */ 211 CURLPROTO_HTTP, /* protocol */ 212 PROTOPT_NONE /* flags */ 213}; 214#endif 215#endif 216 217#ifdef USE_SSL 218static void pop3_to_pop3s(struct connectdata *conn) 219{ 220 conn->handler = &Curl_handler_pop3s; 221} 222#else 223#define pop3_to_pop3s(x) Curl_nop_stmt 224#endif 225 226/*********************************************************************** 227 * 228 * pop3_endofresp() 229 * 230 * Checks for an ending POP3 status code at the start of the given string, but 231 * also detects the APOP timestamp from the server greeting and various 232 * capabilities from the CAPA response including the supported authentication 233 * types and allowed SASL mechanisms. 234 */ 235static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len, 236 int *resp) 237{ 238 struct pop3_conn *pop3c = &conn->proto.pop3c; 239 240 /* Do we have an error response? */ 241 if(len >= 4 && !memcmp("-ERR", line, 4)) { 242 *resp = '-'; 243 244 return TRUE; 245 } 246 247 /* Are we processing CAPA command responses? */ 248 if(pop3c->state == POP3_CAPA) { 249 /* Do we have the terminating line? */ 250 if(len >= 1 && !memcmp(line, ".", 1)) 251 *resp = '+'; 252 else 253 *resp = '*'; 254 255 return TRUE; 256 } 257 258 /* Do we have a command or continuation response? */ 259 if((len >= 3 && !memcmp("+OK", line, 3)) || 260 (len >= 1 && !memcmp("+", line, 1))) { 261 *resp = '+'; 262 263 return TRUE; 264 } 265 266 return FALSE; /* Nothing for us */ 267} 268 269/*********************************************************************** 270 * 271 * pop3_get_message() 272 * 273 * Gets the authentication message from the response buffer. 274 */ 275static void pop3_get_message(char *buffer, char** outptr) 276{ 277 size_t len = 0; 278 char* message = NULL; 279 280 /* Find the start of the message */ 281 for(message = buffer + 2; *message == ' ' || *message == '\t'; message++) 282 ; 283 284 /* Find the end of the message */ 285 for(len = strlen(message); len--;) 286 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && 287 message[len] != '\t') 288 break; 289 290 /* Terminate the message */ 291 if(++len) { 292 message[len] = '\0'; 293 } 294 295 *outptr = message; 296} 297 298/*********************************************************************** 299 * 300 * state() 301 * 302 * This is the ONLY way to change POP3 state! 303 */ 304static void state(struct connectdata *conn, pop3state newstate) 305{ 306 struct pop3_conn *pop3c = &conn->proto.pop3c; 307#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 308 /* for debug purposes */ 309 static const char * const names[] = { 310 "STOP", 311 "SERVERGREET", 312 "CAPA", 313 "STARTTLS", 314 "UPGRADETLS", 315 "AUTH_PLAIN", 316 "AUTH_LOGIN", 317 "AUTH_LOGIN_PASSWD", 318 "AUTH_CRAMMD5", 319 "AUTH_DIGESTMD5", 320 "AUTH_DIGESTMD5_RESP", 321 "AUTH_NTLM", 322 "AUTH_NTLM_TYPE2MSG", 323 "AUTH_XOAUTH2", 324 "AUTH_CANCEL", 325 "AUTH_FINAL", 326 "APOP", 327 "USER", 328 "PASS", 329 "COMMAND", 330 "QUIT", 331 /* LAST */ 332 }; 333 334 if(pop3c->state != newstate) 335 infof(conn->data, "POP3 %p state change from %s to %s\n", 336 (void *)pop3c, names[pop3c->state], names[newstate]); 337#endif 338 339 pop3c->state = newstate; 340} 341 342/*********************************************************************** 343 * 344 * pop3_perform_capa() 345 * 346 * Sends the CAPA command in order to obtain a list of server side supported 347 * capabilities. 348 */ 349static CURLcode pop3_perform_capa(struct connectdata *conn) 350{ 351 CURLcode result = CURLE_OK; 352 struct pop3_conn *pop3c = &conn->proto.pop3c; 353 354 pop3c->authmechs = 0; /* No known authentication mechanisms yet */ 355 pop3c->authused = 0; /* Clear the authentication mechanism used */ 356 pop3c->tls_supported = FALSE; /* Clear the TLS capability */ 357 358 /* Send the CAPA command */ 359 result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA"); 360 361 if(!result) 362 state(conn, POP3_CAPA); 363 364 return result; 365} 366 367/*********************************************************************** 368 * 369 * pop3_perform_starttls() 370 * 371 * Sends the STLS command to start the upgrade to TLS. 372 */ 373static CURLcode pop3_perform_starttls(struct connectdata *conn) 374{ 375 CURLcode result = CURLE_OK; 376 377 /* Send the STLS command */ 378 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS"); 379 380 if(!result) 381 state(conn, POP3_STARTTLS); 382 383 return result; 384} 385 386/*********************************************************************** 387 * 388 * pop3_perform_upgrade_tls() 389 * 390 * Performs the upgrade to TLS. 391 */ 392static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn) 393{ 394 CURLcode result = CURLE_OK; 395 struct pop3_conn *pop3c = &conn->proto.pop3c; 396 397 /* Start the SSL connection */ 398 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); 399 400 if(!result) { 401 if(pop3c->state != POP3_UPGRADETLS) 402 state(conn, POP3_UPGRADETLS); 403 404 if(pop3c->ssldone) { 405 pop3_to_pop3s(conn); 406 result = pop3_perform_capa(conn); 407 } 408 } 409 410 return result; 411} 412 413/*********************************************************************** 414 * 415 * pop3_perform_user() 416 * 417 * Sends a clear text USER command to authenticate with. 418 */ 419static CURLcode pop3_perform_user(struct connectdata *conn) 420{ 421 CURLcode result = CURLE_OK; 422 423 /* Check we have a username and password to authenticate with and end the 424 connect phase if we don't */ 425 if(!conn->bits.user_passwd) { 426 state(conn, POP3_STOP); 427 428 return result; 429 } 430 431 /* Send the USER command */ 432 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s", 433 conn->user ? conn->user : ""); 434 if(!result) 435 state(conn, POP3_USER); 436 437 return result; 438} 439 440#ifndef CURL_DISABLE_CRYPTO_AUTH 441/*********************************************************************** 442 * 443 * pop3_perform_apop() 444 * 445 * Sends an APOP command to authenticate with. 446 */ 447static CURLcode pop3_perform_apop(struct connectdata *conn) 448{ 449 CURLcode result = CURLE_OK; 450 struct pop3_conn *pop3c = &conn->proto.pop3c; 451 size_t i; 452 MD5_context *ctxt; 453 unsigned char digest[MD5_DIGEST_LEN]; 454 char secret[2 * MD5_DIGEST_LEN + 1]; 455 456 /* Check we have a username and password to authenticate with and end the 457 connect phase if we don't */ 458 if(!conn->bits.user_passwd) { 459 state(conn, POP3_STOP); 460 461 return result; 462 } 463 464 /* Create the digest */ 465 ctxt = Curl_MD5_init(Curl_DIGEST_MD5); 466 if(!ctxt) 467 return CURLE_OUT_OF_MEMORY; 468 469 Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp, 470 curlx_uztoui(strlen(pop3c->apoptimestamp))); 471 472 Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd, 473 curlx_uztoui(strlen(conn->passwd))); 474 475 /* Finalise the digest */ 476 Curl_MD5_final(ctxt, digest); 477 478 /* Convert the calculated 16 octet digest into a 32 byte hex string */ 479 for(i = 0; i < MD5_DIGEST_LEN; i++) 480 snprintf(&secret[2 * i], 3, "%02x", digest[i]); 481 482 result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret); 483 484 if(!result) 485 state(conn, POP3_APOP); 486 487 return result; 488} 489#endif 490 491/*********************************************************************** 492 * 493 * pop3_perform_auth() 494 * 495 * Sends an AUTH command allowing the client to login with the given SASL 496 * authentication mechanism. 497 */ 498static CURLcode pop3_perform_auth(struct connectdata *conn, 499 const char *mech, 500 const char *initresp, size_t len, 501 pop3state state1, pop3state state2) 502{ 503 CURLcode result = CURLE_OK; 504 struct pop3_conn *pop3c = &conn->proto.pop3c; 505 506 if(initresp && 8 + strlen(mech) + len <= 255) { /* AUTH <mech> ...<crlf> */ 507 /* Send the AUTH command with the initial response */ 508 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp); 509 510 if(!result) 511 state(conn, state2); 512 } 513 else { 514 /* Send the AUTH command */ 515 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech); 516 517 if(!result) 518 state(conn, state1); 519 } 520 521 return result; 522} 523 524/*********************************************************************** 525 * 526 * pop3_perform_authentication() 527 * 528 * Initiates the authentication sequence, with the appropriate SASL 529 * authentication mechanism, falling back to APOP and clear text should a 530 * common mechanism not be available between the client and server. 531 */ 532static CURLcode pop3_perform_authentication(struct connectdata *conn) 533{ 534 CURLcode result = CURLE_OK; 535 struct pop3_conn *pop3c = &conn->proto.pop3c; 536 const char *mech = NULL; 537 char *initresp = NULL; 538 size_t len = 0; 539 pop3state state1 = POP3_STOP; 540 pop3state state2 = POP3_STOP; 541 542 /* Check we have a username and password to authenticate with and end the 543 connect phase if we don't */ 544 if(!conn->bits.user_passwd) { 545 state(conn, POP3_STOP); 546 547 return result; 548 } 549 550 /* Calculate the SASL login details */ 551 if(pop3c->authtypes & POP3_TYPE_SASL) 552 result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1, 553 &state2); 554 555 if(!result) { 556 if(mech && (pop3c->preftype & POP3_TYPE_SASL)) { 557 /* Perform SASL based authentication */ 558 result = pop3_perform_auth(conn, mech, initresp, len, state1, state2); 559 560 Curl_safefree(initresp); 561 } 562#ifndef CURL_DISABLE_CRYPTO_AUTH 563 else if((pop3c->authtypes & POP3_TYPE_APOP) && 564 (pop3c->preftype & POP3_TYPE_APOP)) 565 /* Perform APOP authentication */ 566 result = pop3_perform_apop(conn); 567#endif 568 else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) && 569 (pop3c->preftype & POP3_TYPE_CLEARTEXT)) 570 /* Perform clear text authentication */ 571 result = pop3_perform_user(conn); 572 else { 573 /* Other mechanisms not supported */ 574 infof(conn->data, "No known authentication mechanisms supported!\n"); 575 result = CURLE_LOGIN_DENIED; 576 } 577 } 578 579 return result; 580} 581 582/*********************************************************************** 583 * 584 * pop3_perform_command() 585 * 586 * Sends a POP3 based command. 587 */ 588static CURLcode pop3_perform_command(struct connectdata *conn) 589{ 590 CURLcode result = CURLE_OK; 591 struct SessionHandle *data = conn->data; 592 struct POP3 *pop3 = data->req.protop; 593 const char *command = NULL; 594 595 /* Calculate the default command */ 596 if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) { 597 command = "LIST"; 598 599 if(pop3->id[0] != '\0') 600 /* Message specific LIST so skip the BODY transfer */ 601 pop3->transfer = FTPTRANSFER_INFO; 602 } 603 else 604 command = "RETR"; 605 606 /* Send the command */ 607 if(pop3->id[0] != '\0') 608 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s", 609 (pop3->custom && pop3->custom[0] != '\0' ? 610 pop3->custom : command), pop3->id); 611 else 612 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", 613 (pop3->custom && pop3->custom[0] != '\0' ? 614 pop3->custom : command)); 615 616 if(!result) 617 state(conn, POP3_COMMAND); 618 619 return result; 620} 621 622/*********************************************************************** 623 * 624 * pop3_perform_quit() 625 * 626 * Performs the quit action prior to sclose() be called. 627 */ 628static CURLcode pop3_perform_quit(struct connectdata *conn) 629{ 630 CURLcode result = CURLE_OK; 631 632 /* Send the QUIT command */ 633 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT"); 634 635 if(!result) 636 state(conn, POP3_QUIT); 637 638 return result; 639} 640 641/* For the initial server greeting */ 642static CURLcode pop3_state_servergreet_resp(struct connectdata *conn, 643 int pop3code, 644 pop3state instate) 645{ 646 CURLcode result = CURLE_OK; 647 struct SessionHandle *data = conn->data; 648 struct pop3_conn *pop3c = &conn->proto.pop3c; 649 const char *line = data->state.buffer; 650 size_t len = strlen(line); 651 size_t i; 652 653 (void)instate; /* no use for this yet */ 654 655 if(pop3code != '+') { 656 failf(data, "Got unexpected pop3-server response"); 657 result = CURLE_FTP_WEIRD_SERVER_REPLY; 658 } 659 else { 660 /* Does the server support APOP authentication? */ 661 if(len >= 4 && line[len - 2] == '>') { 662 /* Look for the APOP timestamp */ 663 for(i = 3; i < len - 2; ++i) { 664 if(line[i] == '<') { 665 /* Calculate the length of the timestamp */ 666 size_t timestamplen = len - 1 - i; 667 if(!timestamplen) 668 break; 669 670 /* Allocate some memory for the timestamp */ 671 pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1); 672 673 if(!pop3c->apoptimestamp) 674 break; 675 676 /* Copy the timestamp */ 677 memcpy(pop3c->apoptimestamp, line + i, timestamplen); 678 pop3c->apoptimestamp[timestamplen] = '\0'; 679 680 /* Store the APOP capability */ 681 pop3c->authtypes |= POP3_TYPE_APOP; 682 break; 683 } 684 } 685 } 686 687 result = pop3_perform_capa(conn); 688 } 689 690 return result; 691} 692 693/* For CAPA responses */ 694static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code, 695 pop3state instate) 696{ 697 CURLcode result = CURLE_OK; 698 struct SessionHandle *data = conn->data; 699 struct pop3_conn *pop3c = &conn->proto.pop3c; 700 const char *line = data->state.buffer; 701 size_t len = strlen(line); 702 size_t wordlen; 703 704 (void)instate; /* no use for this yet */ 705 706 /* Do we have a untagged response? */ 707 if(pop3code == '*') { 708 /* Does the server support the STLS capability? */ 709 if(len >= 4 && !memcmp(line, "STLS", 4)) 710 pop3c->tls_supported = TRUE; 711 712 /* Does the server support clear text authentication? */ 713 else if(len >= 4 && !memcmp(line, "USER", 4)) 714 pop3c->authtypes |= POP3_TYPE_CLEARTEXT; 715 716 /* Does the server support SASL based authentication? */ 717 else if(len >= 5 && !memcmp(line, "SASL ", 5)) { 718 pop3c->authtypes |= POP3_TYPE_SASL; 719 720 /* Advance past the SASL keyword */ 721 line += 5; 722 len -= 5; 723 724 /* Loop through the data line */ 725 for(;;) { 726 while(len && 727 (*line == ' ' || *line == '\t' || 728 *line == '\r' || *line == '\n')) { 729 730 line++; 731 len--; 732 } 733 734 if(!len) 735 break; 736 737 /* Extract the word */ 738 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && 739 line[wordlen] != '\t' && line[wordlen] != '\r' && 740 line[wordlen] != '\n';) 741 wordlen++; 742 743 /* Test the word for a matching authentication mechanism */ 744 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) 745 pop3c->authmechs |= SASL_MECH_LOGIN; 746 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) 747 pop3c->authmechs |= SASL_MECH_PLAIN; 748 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) 749 pop3c->authmechs |= SASL_MECH_CRAM_MD5; 750 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) 751 pop3c->authmechs |= SASL_MECH_DIGEST_MD5; 752 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) 753 pop3c->authmechs |= SASL_MECH_GSSAPI; 754 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) 755 pop3c->authmechs |= SASL_MECH_EXTERNAL; 756 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) 757 pop3c->authmechs |= SASL_MECH_NTLM; 758 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) 759 pop3c->authmechs |= SASL_MECH_XOAUTH2; 760 761 line += wordlen; 762 len -= wordlen; 763 } 764 } 765 } 766 else if(pop3code == '+') { 767 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { 768 /* We don't have a SSL/TLS connection yet, but SSL is requested */ 769 if(pop3c->tls_supported) 770 /* Switch to TLS connection now */ 771 result = pop3_perform_starttls(conn); 772 else if(data->set.use_ssl == CURLUSESSL_TRY) 773 /* Fallback and carry on with authentication */ 774 result = pop3_perform_authentication(conn); 775 else { 776 failf(data, "STLS not supported."); 777 result = CURLE_USE_SSL_FAILED; 778 } 779 } 780 else 781 result = pop3_perform_authentication(conn); 782 } 783 else { 784 /* Clear text is supported when CAPA isn't recognised */ 785 pop3c->authtypes |= POP3_TYPE_CLEARTEXT; 786 787 result = pop3_perform_authentication(conn); 788 } 789 790 return result; 791} 792 793/* For STARTTLS responses */ 794static CURLcode pop3_state_starttls_resp(struct connectdata *conn, 795 int pop3code, 796 pop3state instate) 797{ 798 CURLcode result = CURLE_OK; 799 struct SessionHandle *data = conn->data; 800 801 (void)instate; /* no use for this yet */ 802 803 if(pop3code != '+') { 804 if(data->set.use_ssl != CURLUSESSL_TRY) { 805 failf(data, "STARTTLS denied. %c", pop3code); 806 result = CURLE_USE_SSL_FAILED; 807 } 808 else 809 result = pop3_perform_authentication(conn); 810 } 811 else 812 result = pop3_perform_upgrade_tls(conn); 813 814 return result; 815} 816 817/* For AUTH PLAIN (without initial response) responses */ 818static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn, 819 int pop3code, 820 pop3state instate) 821{ 822 CURLcode result = CURLE_OK; 823 struct SessionHandle *data = conn->data; 824 size_t len = 0; 825 char *plainauth = NULL; 826 827 (void)instate; /* no use for this yet */ 828 829 if(pop3code != '+') { 830 failf(data, "Access denied. %c", pop3code); 831 result = CURLE_LOGIN_DENIED; 832 } 833 else { 834 /* Create the authorisation message */ 835 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, 836 &plainauth, &len); 837 if(!result && plainauth) { 838 /* Send the message */ 839 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth); 840 841 if(!result) 842 state(conn, POP3_AUTH_FINAL); 843 } 844 } 845 846 Curl_safefree(plainauth); 847 848 return result; 849} 850 851/* For AUTH LOGIN (without initial response) responses */ 852static CURLcode pop3_state_auth_login_resp(struct connectdata *conn, 853 int pop3code, 854 pop3state instate) 855{ 856 CURLcode result = CURLE_OK; 857 struct SessionHandle *data = conn->data; 858 size_t len = 0; 859 char *authuser = NULL; 860 861 (void)instate; /* no use for this yet */ 862 863 if(pop3code != '+') { 864 failf(data, "Access denied: %d", pop3code); 865 result = CURLE_LOGIN_DENIED; 866 } 867 else { 868 /* Create the user message */ 869 result = Curl_sasl_create_login_message(data, conn->user, 870 &authuser, &len); 871 if(!result && authuser) { 872 /* Send the user */ 873 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser); 874 875 if(!result) 876 state(conn, POP3_AUTH_LOGIN_PASSWD); 877 } 878 } 879 880 Curl_safefree(authuser); 881 882 return result; 883} 884 885/* For AUTH LOGIN user entry responses */ 886static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn, 887 int pop3code, 888 pop3state instate) 889{ 890 CURLcode result = CURLE_OK; 891 struct SessionHandle *data = conn->data; 892 size_t len = 0; 893 char *authpasswd = NULL; 894 895 (void)instate; /* no use for this yet */ 896 897 if(pop3code != '+') { 898 failf(data, "Access denied: %d", pop3code); 899 result = CURLE_LOGIN_DENIED; 900 } 901 else { 902 /* Create the password message */ 903 result = Curl_sasl_create_login_message(data, conn->passwd, 904 &authpasswd, &len); 905 if(!result && authpasswd) { 906 /* Send the password */ 907 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd); 908 909 if(!result) 910 state(conn, POP3_AUTH_FINAL); 911 } 912 } 913 914 Curl_safefree(authpasswd); 915 916 return result; 917} 918 919#ifndef CURL_DISABLE_CRYPTO_AUTH 920/* For AUTH CRAM-MD5 responses */ 921static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn, 922 int pop3code, 923 pop3state instate) 924{ 925 CURLcode result = CURLE_OK; 926 struct SessionHandle *data = conn->data; 927 char *chlg = NULL; 928 char *chlg64 = NULL; 929 char *rplyb64 = NULL; 930 size_t len = 0; 931 932 (void)instate; /* no use for this yet */ 933 934 if(pop3code != '+') { 935 failf(data, "Access denied: %d", pop3code); 936 return CURLE_LOGIN_DENIED; 937 } 938 939 /* Get the challenge message */ 940 pop3_get_message(data->state.buffer, &chlg64); 941 942 /* Decode the challenge message */ 943 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len); 944 if(result) { 945 /* Send the cancellation */ 946 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*"); 947 948 if(!result) 949 state(conn, POP3_AUTH_CANCEL); 950 } 951 else { 952 /* Create the response message */ 953 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user, 954 conn->passwd, &rplyb64, &len); 955 if(!result && rplyb64) { 956 /* Send the response */ 957 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64); 958 959 if(!result) 960 state(conn, POP3_AUTH_FINAL); 961 } 962 } 963 964 Curl_safefree(chlg); 965 Curl_safefree(rplyb64); 966 967 return result; 968} 969 970/* For AUTH DIGEST-MD5 challenge responses */ 971static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn, 972 int pop3code, 973 pop3state instate) 974{ 975 CURLcode result = CURLE_OK; 976 struct SessionHandle *data = conn->data; 977 char *chlg64 = NULL; 978 char *rplyb64 = NULL; 979 size_t len = 0; 980 981 char nonce[64]; 982 char realm[128]; 983 char algorithm[64]; 984 985 (void)instate; /* no use for this yet */ 986 987 if(pop3code != '+') { 988 failf(data, "Access denied: %d", pop3code); 989 return CURLE_LOGIN_DENIED; 990 } 991 992 /* Get the challenge message */ 993 pop3_get_message(data->state.buffer, &chlg64); 994 995 /* Decode the challange message */ 996 result = Curl_sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce), 997 realm, sizeof(realm), 998 algorithm, sizeof(algorithm)); 999 if(result || strcmp(algorithm, "md5-sess") != 0) { 1000 /* Send the cancellation */ 1001 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*"); 1002 1003 if(!result) 1004 state(conn, POP3_AUTH_CANCEL); 1005 } 1006 else { 1007 /* Create the response message */ 1008 result = Curl_sasl_create_digest_md5_message(data, nonce, realm, 1009 conn->user, conn->passwd, 1010 "pop", &rplyb64, &len); 1011 if(!result && rplyb64) { 1012 /* Send the response */ 1013 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64); 1014 1015 if(!result) 1016 state(conn, POP3_AUTH_DIGESTMD5_RESP); 1017 } 1018 } 1019 1020 Curl_safefree(rplyb64); 1021 1022 return result; 1023} 1024 1025/* For AUTH DIGEST-MD5 challenge-response responses */ 1026static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn, 1027 int pop3code, 1028 pop3state instate) 1029{ 1030 CURLcode result = CURLE_OK; 1031 struct SessionHandle *data = conn->data; 1032 1033 (void)instate; /* no use for this yet */ 1034 1035 if(pop3code != '+') { 1036 failf(data, "Authentication failed: %d", pop3code); 1037 result = CURLE_LOGIN_DENIED; 1038 } 1039 else { 1040 /* Send an empty response */ 1041 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", ""); 1042 1043 if(!result) 1044 state(conn, POP3_AUTH_FINAL); 1045 } 1046 1047 return result; 1048} 1049#endif 1050 1051#ifdef USE_NTLM 1052/* For AUTH NTLM (without initial response) responses */ 1053static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn, 1054 int pop3code, 1055 pop3state instate) 1056{ 1057 CURLcode result = CURLE_OK; 1058 struct SessionHandle *data = conn->data; 1059 size_t len = 0; 1060 char *type1msg = NULL; 1061 1062 (void)instate; /* no use for this yet */ 1063 1064 if(pop3code != '+') { 1065 failf(data, "Access denied: %d", pop3code); 1066 result = CURLE_LOGIN_DENIED; 1067 } 1068 else { 1069 /* Create the type-1 message */ 1070 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, 1071 &conn->ntlm, 1072 &type1msg, &len); 1073 if(!result && type1msg) { 1074 /* Send the message */ 1075 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg); 1076 1077 if(!result) 1078 state(conn, POP3_AUTH_NTLM_TYPE2MSG); 1079 } 1080 } 1081 1082 Curl_safefree(type1msg); 1083 1084 return result; 1085} 1086 1087/* For NTLM type-2 responses (sent in reponse to our type-1 message) */ 1088static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn, 1089 int pop3code, 1090 pop3state instate) 1091{ 1092 CURLcode result = CURLE_OK; 1093 struct SessionHandle *data = conn->data; 1094 char *type2msg = NULL; 1095 char *type3msg = NULL; 1096 size_t len = 0; 1097 1098 (void)instate; /* no use for this yet */ 1099 1100 if(pop3code != '+') { 1101 failf(data, "Access denied: %d", pop3code); 1102 result = CURLE_LOGIN_DENIED; 1103 } 1104 else { 1105 /* Get the type-2 message */ 1106 pop3_get_message(data->state.buffer, &type2msg); 1107 1108 /* Decode the type-2 message */ 1109 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm); 1110 if(result) { 1111 /* Send the cancellation */ 1112 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*"); 1113 1114 if(!result) 1115 state(conn, POP3_AUTH_CANCEL); 1116 } 1117 else { 1118 /* Create the type-3 message */ 1119 result = Curl_sasl_create_ntlm_type3_message(data, conn->user, 1120 conn->passwd, &conn->ntlm, 1121 &type3msg, &len); 1122 if(!result && type3msg) { 1123 /* Send the message */ 1124 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg); 1125 1126 if(!result) 1127 state(conn, POP3_AUTH_FINAL); 1128 } 1129 } 1130 } 1131 1132 Curl_safefree(type3msg); 1133 1134 return result; 1135} 1136#endif 1137 1138/* For AUTH XOAUTH2 (without initial response) responses */ 1139static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn, 1140 int pop3code, pop3state instate) 1141{ 1142 CURLcode result = CURLE_OK; 1143 struct SessionHandle *data = conn->data; 1144 size_t len = 0; 1145 char *xoauth = NULL; 1146 1147 (void)instate; /* no use for this yet */ 1148 1149 if(pop3code != '+') { 1150 failf(data, "Access denied: %d", pop3code); 1151 result = CURLE_LOGIN_DENIED; 1152 } 1153 else { 1154 /* Create the authorisation message */ 1155 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, 1156 conn->xoauth2_bearer, 1157 &xoauth, &len); 1158 if(!result && xoauth) { 1159 /* Send the message */ 1160 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth); 1161 1162 if(!result) 1163 state(conn, POP3_AUTH_FINAL); 1164 } 1165 } 1166 1167 Curl_safefree(xoauth); 1168 1169 return result; 1170} 1171 1172/* For AUTH cancellation responses */ 1173static CURLcode pop3_state_auth_cancel_resp(struct connectdata *conn, 1174 int pop3code, 1175 pop3state instate) 1176{ 1177 CURLcode result = CURLE_OK; 1178 struct SessionHandle *data = conn->data; 1179 struct pop3_conn *pop3c = &conn->proto.pop3c; 1180 const char *mech = NULL; 1181 char *initresp = NULL; 1182 size_t len = 0; 1183 pop3state state1 = POP3_STOP; 1184 pop3state state2 = POP3_STOP; 1185 1186 (void)pop3code; 1187 (void)instate; /* no use for this yet */ 1188 1189 /* Remove the offending mechanism from the supported list */ 1190 pop3c->authmechs ^= pop3c->authused; 1191 1192 /* Calculate alternative SASL login details */ 1193 result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1, 1194 &state2); 1195 1196 if(!result) { 1197 /* Do we have any mechanisms left or can we fallback to another 1198 authentication type? */ 1199 if(mech) { 1200 /* Retry SASL based authentication */ 1201 result = pop3_perform_auth(conn, mech, initresp, len, state1, state2); 1202 1203 Curl_safefree(initresp); 1204 } 1205#ifndef CURL_DISABLE_CRYPTO_AUTH 1206 else if((pop3c->authtypes & POP3_TYPE_APOP) && 1207 (pop3c->preftype & POP3_TYPE_APOP)) 1208 /* Perform APOP authentication */ 1209 result = pop3_perform_apop(conn); 1210#endif 1211 else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) && 1212 (pop3c->preftype & POP3_TYPE_CLEARTEXT)) 1213 /* Perform clear text authentication */ 1214 result = pop3_perform_user(conn); 1215 else { 1216 failf(data, "Authentication cancelled"); 1217 1218 result = CURLE_LOGIN_DENIED; 1219 } 1220 } 1221 1222 return result; 1223} 1224 1225/* For final responses in the AUTH sequence */ 1226static CURLcode pop3_state_auth_final_resp(struct connectdata *conn, 1227 int pop3code, 1228 pop3state instate) 1229{ 1230 CURLcode result = CURLE_OK; 1231 struct SessionHandle *data = conn->data; 1232 1233 (void)instate; /* no use for this yet */ 1234 1235 if(pop3code != '+') { 1236 failf(data, "Authentication failed: %d", pop3code); 1237 result = CURLE_LOGIN_DENIED; 1238 } 1239 else 1240 /* End of connect phase */ 1241 state(conn, POP3_STOP); 1242 1243 return result; 1244} 1245 1246#ifndef CURL_DISABLE_CRYPTO_AUTH 1247/* For APOP responses */ 1248static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code, 1249 pop3state instate) 1250{ 1251 CURLcode result = CURLE_OK; 1252 struct SessionHandle *data = conn->data; 1253 1254 (void)instate; /* no use for this yet */ 1255 1256 if(pop3code != '+') { 1257 failf(data, "Authentication failed: %d", pop3code); 1258 result = CURLE_LOGIN_DENIED; 1259 } 1260 else 1261 /* End of connect phase */ 1262 state(conn, POP3_STOP); 1263 1264 return result; 1265} 1266#endif 1267 1268/* For USER responses */ 1269static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code, 1270 pop3state instate) 1271{ 1272 CURLcode result = CURLE_OK; 1273 struct SessionHandle *data = conn->data; 1274 1275 (void)instate; /* no use for this yet */ 1276 1277 if(pop3code != '+') { 1278 failf(data, "Access denied. %c", pop3code); 1279 result = CURLE_LOGIN_DENIED; 1280 } 1281 else 1282 /* Send the PASS command */ 1283 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s", 1284 conn->passwd ? conn->passwd : ""); 1285 if(!result) 1286 state(conn, POP3_PASS); 1287 1288 return result; 1289} 1290 1291/* For PASS responses */ 1292static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code, 1293 pop3state instate) 1294{ 1295 CURLcode result = CURLE_OK; 1296 struct SessionHandle *data = conn->data; 1297 1298 (void)instate; /* no use for this yet */ 1299 1300 if(pop3code != '+') { 1301 failf(data, "Access denied. %c", pop3code); 1302 result = CURLE_LOGIN_DENIED; 1303 } 1304 else 1305 /* End of connect phase */ 1306 state(conn, POP3_STOP); 1307 1308 return result; 1309} 1310 1311/* For command responses */ 1312static CURLcode pop3_state_command_resp(struct connectdata *conn, 1313 int pop3code, 1314 pop3state instate) 1315{ 1316 CURLcode result = CURLE_OK; 1317 struct SessionHandle *data = conn->data; 1318 struct POP3 *pop3 = data->req.protop; 1319 struct pop3_conn *pop3c = &conn->proto.pop3c; 1320 struct pingpong *pp = &pop3c->pp; 1321 1322 (void)instate; /* no use for this yet */ 1323 1324 if(pop3code != '+') { 1325 state(conn, POP3_STOP); 1326 return CURLE_RECV_ERROR; 1327 } 1328 1329 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the 1330 EOB string so count this is two matching bytes. This is necessary to make 1331 the code detect the EOB if the only data than comes now is %2e CR LF like 1332 when there is no body to return. */ 1333 pop3c->eob = 2; 1334 1335 /* But since this initial CR LF pair is not part of the actual body, we set 1336 the strip counter here so that these bytes won't be delivered. */ 1337 pop3c->strip = 2; 1338 1339 if(pop3->transfer == FTPTRANSFER_BODY) { 1340 /* POP3 download */ 1341 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL); 1342 1343 if(pp->cache) { 1344 /* The header "cache" contains a bunch of data that is actually body 1345 content so send it as such. Note that there may even be additional 1346 "headers" after the body */ 1347 1348 if(!data->set.opt_no_body) { 1349 result = Curl_pop3_write(conn, pp->cache, pp->cache_size); 1350 if(result) 1351 return result; 1352 } 1353 1354 /* Free the cache */ 1355 Curl_safefree(pp->cache); 1356 1357 /* Reset the cache size */ 1358 pp->cache_size = 0; 1359 } 1360 } 1361 1362 /* End of DO phase */ 1363 state(conn, POP3_STOP); 1364 1365 return result; 1366} 1367 1368static CURLcode pop3_statemach_act(struct connectdata *conn) 1369{ 1370 CURLcode result = CURLE_OK; 1371 curl_socket_t sock = conn->sock[FIRSTSOCKET]; 1372 int pop3code; 1373 struct pop3_conn *pop3c = &conn->proto.pop3c; 1374 struct pingpong *pp = &pop3c->pp; 1375 size_t nread = 0; 1376 1377 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */ 1378 if(pop3c->state == POP3_UPGRADETLS) 1379 return pop3_perform_upgrade_tls(conn); 1380 1381 /* Flush any data that needs to be sent */ 1382 if(pp->sendleft) 1383 return Curl_pp_flushsend(pp); 1384 1385 do { 1386 /* Read the response from the server */ 1387 result = Curl_pp_readresp(sock, pp, &pop3code, &nread); 1388 if(result) 1389 return result; 1390 1391 if(!pop3code) 1392 break; 1393 1394 /* We have now received a full POP3 server response */ 1395 switch(pop3c->state) { 1396 case POP3_SERVERGREET: 1397 result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state); 1398 break; 1399 1400 case POP3_CAPA: 1401 result = pop3_state_capa_resp(conn, pop3code, pop3c->state); 1402 break; 1403 1404 case POP3_STARTTLS: 1405 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state); 1406 break; 1407 1408 case POP3_AUTH_PLAIN: 1409 result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state); 1410 break; 1411 1412 case POP3_AUTH_LOGIN: 1413 result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state); 1414 break; 1415 1416 case POP3_AUTH_LOGIN_PASSWD: 1417 result = pop3_state_auth_login_password_resp(conn, pop3code, 1418 pop3c->state); 1419 break; 1420 1421#ifndef CURL_DISABLE_CRYPTO_AUTH 1422 case POP3_AUTH_CRAMMD5: 1423 result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state); 1424 break; 1425 1426 case POP3_AUTH_DIGESTMD5: 1427 result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state); 1428 break; 1429 1430 case POP3_AUTH_DIGESTMD5_RESP: 1431 result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state); 1432 break; 1433#endif 1434 1435#ifdef USE_NTLM 1436 case POP3_AUTH_NTLM: 1437 result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state); 1438 break; 1439 1440 case POP3_AUTH_NTLM_TYPE2MSG: 1441 result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code, 1442 pop3c->state); 1443 break; 1444#endif 1445 1446 case POP3_AUTH_XOAUTH2: 1447 result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state); 1448 break; 1449 1450 case POP3_AUTH_CANCEL: 1451 result = pop3_state_auth_cancel_resp(conn, pop3code, pop3c->state); 1452 break; 1453 1454 case POP3_AUTH_FINAL: 1455 result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state); 1456 break; 1457 1458#ifndef CURL_DISABLE_CRYPTO_AUTH 1459 case POP3_APOP: 1460 result = pop3_state_apop_resp(conn, pop3code, pop3c->state); 1461 break; 1462#endif 1463 1464 case POP3_USER: 1465 result = pop3_state_user_resp(conn, pop3code, pop3c->state); 1466 break; 1467 1468 case POP3_PASS: 1469 result = pop3_state_pass_resp(conn, pop3code, pop3c->state); 1470 break; 1471 1472 case POP3_COMMAND: 1473 result = pop3_state_command_resp(conn, pop3code, pop3c->state); 1474 break; 1475 1476 case POP3_QUIT: 1477 /* fallthrough, just stop! */ 1478 default: 1479 /* internal error */ 1480 state(conn, POP3_STOP); 1481 break; 1482 } 1483 } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp)); 1484 1485 return result; 1486} 1487 1488/* Called repeatedly until done from multi.c */ 1489static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done) 1490{ 1491 CURLcode result = CURLE_OK; 1492 struct pop3_conn *pop3c = &conn->proto.pop3c; 1493 1494 if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) { 1495 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); 1496 if(result || !pop3c->ssldone) 1497 return result; 1498 } 1499 1500 result = Curl_pp_statemach(&pop3c->pp, FALSE); 1501 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE; 1502 1503 return result; 1504} 1505 1506static CURLcode pop3_block_statemach(struct connectdata *conn) 1507{ 1508 CURLcode result = CURLE_OK; 1509 struct pop3_conn *pop3c = &conn->proto.pop3c; 1510 1511 while(pop3c->state != POP3_STOP && !result) 1512 result = Curl_pp_statemach(&pop3c->pp, TRUE); 1513 1514 return result; 1515} 1516 1517/* Allocate and initialize the POP3 struct for the current SessionHandle if 1518 required */ 1519static CURLcode pop3_init(struct connectdata *conn) 1520{ 1521 CURLcode result = CURLE_OK; 1522 struct SessionHandle *data = conn->data; 1523 struct POP3 *pop3; 1524 1525 pop3 = data->req.protop = calloc(sizeof(struct POP3), 1); 1526 if(!pop3) 1527 result = CURLE_OUT_OF_MEMORY; 1528 1529 return result; 1530} 1531 1532/* For the POP3 "protocol connect" and "doing" phases only */ 1533static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks, 1534 int numsocks) 1535{ 1536 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks); 1537} 1538 1539/*********************************************************************** 1540 * 1541 * pop3_connect() 1542 * 1543 * This function should do everything that is to be considered a part of the 1544 * connection phase. 1545 * 1546 * The variable 'done' points to will be TRUE if the protocol-layer connect 1547 * phase is done when this function returns, or FALSE if not. 1548 */ 1549static CURLcode pop3_connect(struct connectdata *conn, bool *done) 1550{ 1551 CURLcode result = CURLE_OK; 1552 struct pop3_conn *pop3c = &conn->proto.pop3c; 1553 struct pingpong *pp = &pop3c->pp; 1554 1555 *done = FALSE; /* default to not done yet */ 1556 1557 /* We always support persistent connections in POP3 */ 1558 conn->bits.close = FALSE; 1559 1560 /* Set the default response time-out */ 1561 pp->response_time = RESP_TIMEOUT; 1562 pp->statemach_act = pop3_statemach_act; 1563 pp->endofresp = pop3_endofresp; 1564 pp->conn = conn; 1565 1566 /* Set the default preferred authentication type and mechanism */ 1567 pop3c->preftype = POP3_TYPE_ANY; 1568 pop3c->prefmech = SASL_AUTH_ANY; 1569 1570 /* Initialise the pingpong layer */ 1571 Curl_pp_init(pp); 1572 1573 /* Parse the URL options */ 1574 result = pop3_parse_url_options(conn); 1575 if(result) 1576 return result; 1577 1578 /* Start off waiting for the server greeting response */ 1579 state(conn, POP3_SERVERGREET); 1580 1581 result = pop3_multi_statemach(conn, done); 1582 1583 return result; 1584} 1585 1586/*********************************************************************** 1587 * 1588 * pop3_done() 1589 * 1590 * The DONE function. This does what needs to be done after a single DO has 1591 * performed. 1592 * 1593 * Input argument is already checked for validity. 1594 */ 1595static CURLcode pop3_done(struct connectdata *conn, CURLcode status, 1596 bool premature) 1597{ 1598 CURLcode result = CURLE_OK; 1599 struct SessionHandle *data = conn->data; 1600 struct POP3 *pop3 = data->req.protop; 1601 1602 (void)premature; 1603 1604 if(!pop3) 1605 /* When the easy handle is removed from the multi interface while libcurl 1606 is still trying to resolve the host name, the POP3 struct is not yet 1607 initialized. However, the removal action calls Curl_done() which in 1608 turn calls this function, so we simply return success. */ 1609 return CURLE_OK; 1610 1611 if(status) { 1612 conn->bits.close = TRUE; /* marked for closure */ 1613 result = status; /* use the already set error code */ 1614 } 1615 1616 /* Cleanup our per-request based variables */ 1617 Curl_safefree(pop3->id); 1618 Curl_safefree(pop3->custom); 1619 1620 /* Clear the transfer mode for the next request */ 1621 pop3->transfer = FTPTRANSFER_BODY; 1622 1623 return result; 1624} 1625 1626/*********************************************************************** 1627 * 1628 * pop3_perform() 1629 * 1630 * This is the actual DO function for POP3. Get a message/listing according to 1631 * the options previously setup. 1632 */ 1633static CURLcode pop3_perform(struct connectdata *conn, bool *connected, 1634 bool *dophase_done) 1635{ 1636 /* This is POP3 and no proxy */ 1637 CURLcode result = CURLE_OK; 1638 struct POP3 *pop3 = conn->data->req.protop; 1639 1640 DEBUGF(infof(conn->data, "DO phase starts\n")); 1641 1642 if(conn->data->set.opt_no_body) { 1643 /* Requested no body means no transfer */ 1644 pop3->transfer = FTPTRANSFER_INFO; 1645 } 1646 1647 *dophase_done = FALSE; /* not done yet */ 1648 1649 /* Start the first command in the DO phase */ 1650 result = pop3_perform_command(conn); 1651 if(result) 1652 return result; 1653 1654 /* Run the state-machine */ 1655 result = pop3_multi_statemach(conn, dophase_done); 1656 1657 *connected = conn->bits.tcpconnect[FIRSTSOCKET]; 1658 1659 if(*dophase_done) 1660 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1661 1662 return result; 1663} 1664 1665/*********************************************************************** 1666 * 1667 * pop3_do() 1668 * 1669 * This function is registered as 'curl_do' function. It decodes the path 1670 * parts etc as a wrapper to the actual DO function (pop3_perform). 1671 * 1672 * The input argument is already checked for validity. 1673 */ 1674static CURLcode pop3_do(struct connectdata *conn, bool *done) 1675{ 1676 CURLcode result = CURLE_OK; 1677 1678 *done = FALSE; /* default to false */ 1679 1680 /* Parse the URL path */ 1681 result = pop3_parse_url_path(conn); 1682 if(result) 1683 return result; 1684 1685 /* Parse the custom request */ 1686 result = pop3_parse_custom_request(conn); 1687 if(result) 1688 return result; 1689 1690 result = pop3_regular_transfer(conn, done); 1691 1692 return result; 1693} 1694 1695/*********************************************************************** 1696 * 1697 * pop3_disconnect() 1698 * 1699 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection 1700 * resources. BLOCKING. 1701 */ 1702static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection) 1703{ 1704 struct pop3_conn *pop3c = &conn->proto.pop3c; 1705 1706 /* We cannot send quit unconditionally. If this connection is stale or 1707 bad in any way, sending quit and waiting around here will make the 1708 disconnect wait in vain and cause more problems than we need to. */ 1709 1710 /* The POP3 session may or may not have been allocated/setup at this 1711 point! */ 1712 if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart) 1713 if(!pop3_perform_quit(conn)) 1714 (void)pop3_block_statemach(conn); /* ignore errors on QUIT */ 1715 1716 /* Disconnect from the server */ 1717 Curl_pp_disconnect(&pop3c->pp); 1718 1719 /* Cleanup the SASL module */ 1720 Curl_sasl_cleanup(conn, pop3c->authused); 1721 1722 /* Cleanup our connection based variables */ 1723 Curl_safefree(pop3c->apoptimestamp); 1724 1725 return CURLE_OK; 1726} 1727 1728/* Call this when the DO phase has completed */ 1729static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected) 1730{ 1731 (void)conn; 1732 (void)connected; 1733 1734 return CURLE_OK; 1735} 1736 1737/* Called from multi.c while DOing */ 1738static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done) 1739{ 1740 CURLcode result = pop3_multi_statemach(conn, dophase_done); 1741 1742 if(result) 1743 DEBUGF(infof(conn->data, "DO phase failed\n")); 1744 else if(*dophase_done) { 1745 result = pop3_dophase_done(conn, FALSE /* not connected */); 1746 1747 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1748 } 1749 1750 return result; 1751} 1752 1753/*********************************************************************** 1754 * 1755 * pop3_regular_transfer() 1756 * 1757 * The input argument is already checked for validity. 1758 * 1759 * Performs all commands done before a regular transfer between a local and a 1760 * remote host. 1761 */ 1762static CURLcode pop3_regular_transfer(struct connectdata *conn, 1763 bool *dophase_done) 1764{ 1765 CURLcode result = CURLE_OK; 1766 bool connected = FALSE; 1767 struct SessionHandle *data = conn->data; 1768 1769 /* Make sure size is unknown at this point */ 1770 data->req.size = -1; 1771 1772 /* Set the progress data */ 1773 Curl_pgrsSetUploadCounter(data, 0); 1774 Curl_pgrsSetDownloadCounter(data, 0); 1775 Curl_pgrsSetUploadSize(data, 0); 1776 Curl_pgrsSetDownloadSize(data, 0); 1777 1778 /* Carry out the perform */ 1779 result = pop3_perform(conn, &connected, dophase_done); 1780 1781 /* Perform post DO phase operations if necessary */ 1782 if(!result && *dophase_done) 1783 result = pop3_dophase_done(conn, connected); 1784 1785 return result; 1786} 1787 1788static CURLcode pop3_setup_connection(struct connectdata *conn) 1789{ 1790 struct SessionHandle *data = conn->data; 1791 1792 /* Initialise the POP3 layer */ 1793 CURLcode result = pop3_init(conn); 1794 if(result) 1795 return result; 1796 1797 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { 1798 /* Unless we have asked to tunnel POP3 operations through the proxy, we 1799 switch and use HTTP operations only */ 1800#ifndef CURL_DISABLE_HTTP 1801 if(conn->handler == &Curl_handler_pop3) 1802 conn->handler = &Curl_handler_pop3_proxy; 1803 else { 1804#ifdef USE_SSL 1805 conn->handler = &Curl_handler_pop3s_proxy; 1806#else 1807 failf(data, "POP3S not supported!"); 1808 return CURLE_UNSUPPORTED_PROTOCOL; 1809#endif 1810 } 1811 1812 /* set it up as an HTTP connection instead */ 1813 return conn->handler->setup_connection(conn); 1814#else 1815 failf(data, "POP3 over http proxy requires HTTP support built-in!"); 1816 return CURLE_UNSUPPORTED_PROTOCOL; 1817#endif 1818 } 1819 1820 data->state.path++; /* don't include the initial slash */ 1821 1822 return CURLE_OK; 1823} 1824 1825/*********************************************************************** 1826 * 1827 * pop3_parse_url_options() 1828 * 1829 * Parse the URL login options. 1830 */ 1831static CURLcode pop3_parse_url_options(struct connectdata *conn) 1832{ 1833 CURLcode result = CURLE_OK; 1834 struct pop3_conn *pop3c = &conn->proto.pop3c; 1835 const char *options = conn->options; 1836 const char *ptr = options; 1837 bool reset = TRUE; 1838 1839 while(ptr && *ptr) { 1840 const char *key = ptr; 1841 1842 while(*ptr && *ptr != '=') 1843 ptr++; 1844 1845 if(strnequal(key, "AUTH", 4)) { 1846 size_t len = 0; 1847 const char *value = ++ptr; 1848 1849 if(reset) { 1850 reset = FALSE; 1851 pop3c->preftype = POP3_TYPE_NONE; 1852 pop3c->prefmech = SASL_AUTH_NONE; 1853 } 1854 1855 while(*ptr && *ptr != ';') { 1856 ptr++; 1857 len++; 1858 } 1859 1860 if(strnequal(value, "*", len)) { 1861 pop3c->preftype = POP3_TYPE_ANY; 1862 pop3c->prefmech = SASL_AUTH_ANY; 1863 } 1864 else if(strnequal(value, "+APOP", len)) { 1865 pop3c->preftype = POP3_TYPE_APOP; 1866 pop3c->prefmech = SASL_AUTH_NONE; 1867 } 1868 else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) { 1869 pop3c->preftype = POP3_TYPE_SASL; 1870 pop3c->prefmech |= SASL_MECH_LOGIN; 1871 } 1872 else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) { 1873 pop3c->preftype = POP3_TYPE_SASL; 1874 pop3c->prefmech |= SASL_MECH_PLAIN; 1875 } 1876 else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) { 1877 pop3c->preftype = POP3_TYPE_SASL; 1878 pop3c->prefmech |= SASL_MECH_CRAM_MD5; 1879 } 1880 else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) { 1881 pop3c->preftype = POP3_TYPE_SASL; 1882 pop3c->prefmech |= SASL_MECH_DIGEST_MD5; 1883 } 1884 else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) { 1885 pop3c->preftype = POP3_TYPE_SASL; 1886 pop3c->prefmech |= SASL_MECH_GSSAPI; 1887 } 1888 else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) { 1889 pop3c->preftype = POP3_TYPE_SASL; 1890 pop3c->prefmech |= SASL_MECH_NTLM; 1891 } 1892 else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) { 1893 pop3c->preftype = POP3_TYPE_SASL; 1894 pop3c->prefmech |= SASL_MECH_XOAUTH2; 1895 } 1896 1897 if(*ptr == ';') 1898 ptr++; 1899 } 1900 else 1901 result = CURLE_URL_MALFORMAT; 1902 } 1903 1904 return result; 1905} 1906 1907/*********************************************************************** 1908 * 1909 * pop3_parse_url_path() 1910 * 1911 * Parse the URL path into separate path components. 1912 */ 1913static CURLcode pop3_parse_url_path(struct connectdata *conn) 1914{ 1915 /* The POP3 struct is already initialised in pop3_connect() */ 1916 struct SessionHandle *data = conn->data; 1917 struct POP3 *pop3 = data->req.protop; 1918 const char *path = data->state.path; 1919 1920 /* URL decode the path for the message ID */ 1921 return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE); 1922} 1923 1924/*********************************************************************** 1925 * 1926 * pop3_parse_custom_request() 1927 * 1928 * Parse the custom request. 1929 */ 1930static CURLcode pop3_parse_custom_request(struct connectdata *conn) 1931{ 1932 CURLcode result = CURLE_OK; 1933 struct SessionHandle *data = conn->data; 1934 struct POP3 *pop3 = data->req.protop; 1935 const char *custom = data->set.str[STRING_CUSTOMREQUEST]; 1936 1937 /* URL decode the custom request */ 1938 if(custom) 1939 result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE); 1940 1941 return result; 1942} 1943 1944/*********************************************************************** 1945 * 1946 * pop3_calc_sasl_details() 1947 * 1948 * Calculate the required login details for SASL authentication. 1949 */ 1950static CURLcode pop3_calc_sasl_details(struct connectdata *conn, 1951 const char **mech, 1952 char **initresp, size_t *len, 1953 pop3state *state1, pop3state *state2) 1954{ 1955 CURLcode result = CURLE_OK; 1956 struct SessionHandle *data = conn->data; 1957 struct pop3_conn *pop3c = &conn->proto.pop3c; 1958 1959 /* Calculate the supported authentication mechanism, by decreasing order of 1960 security, as well as the initial response where appropriate */ 1961#ifndef CURL_DISABLE_CRYPTO_AUTH 1962 if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) && 1963 (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) { 1964 *mech = SASL_MECH_STRING_DIGEST_MD5; 1965 *state1 = POP3_AUTH_DIGESTMD5; 1966 pop3c->authused = SASL_MECH_DIGEST_MD5; 1967 } 1968 else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) && 1969 (pop3c->prefmech & SASL_MECH_CRAM_MD5)) { 1970 *mech = SASL_MECH_STRING_CRAM_MD5; 1971 *state1 = POP3_AUTH_CRAMMD5; 1972 pop3c->authused = SASL_MECH_CRAM_MD5; 1973 } 1974 else 1975#endif 1976#ifdef USE_NTLM 1977 if((pop3c->authmechs & SASL_MECH_NTLM) && 1978 (pop3c->prefmech & SASL_MECH_NTLM)) { 1979 *mech = SASL_MECH_STRING_NTLM; 1980 *state1 = POP3_AUTH_NTLM; 1981 *state2 = POP3_AUTH_NTLM_TYPE2MSG; 1982 pop3c->authused = SASL_MECH_NTLM; 1983 1984 if(data->set.sasl_ir) 1985 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, 1986 &conn->ntlm, 1987 initresp, len); 1988 } 1989 else 1990#endif 1991 if(((pop3c->authmechs & SASL_MECH_XOAUTH2) && 1992 (pop3c->prefmech & SASL_MECH_XOAUTH2) && 1993 (pop3c->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { 1994 *mech = SASL_MECH_STRING_XOAUTH2; 1995 *state1 = POP3_AUTH_XOAUTH2; 1996 *state2 = POP3_AUTH_FINAL; 1997 pop3c->authused = SASL_MECH_XOAUTH2; 1998 1999 if(data->set.sasl_ir) 2000 result = Curl_sasl_create_xoauth2_message(data, conn->user, 2001 conn->xoauth2_bearer, 2002 initresp, len); 2003 } 2004 else if((pop3c->authmechs & SASL_MECH_LOGIN) && 2005 (pop3c->prefmech & SASL_MECH_LOGIN)) { 2006 *mech = SASL_MECH_STRING_LOGIN; 2007 *state1 = POP3_AUTH_LOGIN; 2008 *state2 = POP3_AUTH_LOGIN_PASSWD; 2009 pop3c->authused = SASL_MECH_LOGIN; 2010 2011 if(data->set.sasl_ir) 2012 result = Curl_sasl_create_login_message(data, conn->user, initresp, len); 2013 } 2014 else if((pop3c->authmechs & SASL_MECH_PLAIN) && 2015 (pop3c->prefmech & SASL_MECH_PLAIN)) { 2016 *mech = SASL_MECH_STRING_PLAIN; 2017 *state1 = POP3_AUTH_PLAIN; 2018 *state2 = POP3_AUTH_FINAL; 2019 pop3c->authused = SASL_MECH_PLAIN; 2020 2021 if(data->set.sasl_ir) 2022 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, 2023 initresp, len); 2024 } 2025 2026 return result; 2027} 2028 2029/*********************************************************************** 2030 * 2031 * Curl_pop3_write() 2032 * 2033 * This function scans the body after the end-of-body and writes everything 2034 * until the end is found. 2035 */ 2036CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread) 2037{ 2038 /* This code could be made into a special function in the handler struct */ 2039 CURLcode result = CURLE_OK; 2040 struct SessionHandle *data = conn->data; 2041 struct SingleRequest *k = &data->req; 2042 2043 struct pop3_conn *pop3c = &conn->proto.pop3c; 2044 bool strip_dot = FALSE; 2045 size_t last = 0; 2046 size_t i; 2047 2048 /* Search through the buffer looking for the end-of-body marker which is 2049 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches 2050 the eob so the server will have prefixed it with an extra dot which we 2051 need to strip out. Additionally the marker could of course be spread out 2052 over 5 different data chunks. */ 2053 for(i = 0; i < nread; i++) { 2054 size_t prev = pop3c->eob; 2055 2056 switch(str[i]) { 2057 case 0x0d: 2058 if(pop3c->eob == 0) { 2059 pop3c->eob++; 2060 2061 if(i) { 2062 /* Write out the body part that didn't match */ 2063 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last], 2064 i - last); 2065 2066 if(result) 2067 return result; 2068 2069 last = i; 2070 } 2071 } 2072 else if(pop3c->eob == 3) 2073 pop3c->eob++; 2074 else 2075 /* If the character match wasn't at position 0 or 3 then restart the 2076 pattern matching */ 2077 pop3c->eob = 1; 2078 break; 2079 2080 case 0x0a: 2081 if(pop3c->eob == 1 || pop3c->eob == 4) 2082 pop3c->eob++; 2083 else 2084 /* If the character match wasn't at position 1 or 4 then start the 2085 search again */ 2086 pop3c->eob = 0; 2087 break; 2088 2089 case 0x2e: 2090 if(pop3c->eob == 2) 2091 pop3c->eob++; 2092 else if(pop3c->eob == 3) { 2093 /* We have an extra dot after the CRLF which we need to strip off */ 2094 strip_dot = TRUE; 2095 pop3c->eob = 0; 2096 } 2097 else 2098 /* If the character match wasn't at position 2 then start the search 2099 again */ 2100 pop3c->eob = 0; 2101 break; 2102 2103 default: 2104 pop3c->eob = 0; 2105 break; 2106 } 2107 2108 /* Did we have a partial match which has subsequently failed? */ 2109 if(prev && prev >= pop3c->eob) { 2110 /* Strip can only be non-zero for the very first mismatch after CRLF 2111 and then both prev and strip are equal and nothing will be output 2112 below */ 2113 while(prev && pop3c->strip) { 2114 prev--; 2115 pop3c->strip--; 2116 } 2117 2118 if(prev) { 2119 /* If the partial match was the CRLF and dot then only write the CRLF 2120 as the server would have inserted the dot */ 2121 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB, 2122 strip_dot ? prev - 1 : prev); 2123 2124 if(result) 2125 return result; 2126 2127 last = i; 2128 strip_dot = FALSE; 2129 } 2130 } 2131 } 2132 2133 if(pop3c->eob == POP3_EOB_LEN) { 2134 /* We have a full match so the transfer is done, however we must transfer 2135 the CRLF at the start of the EOB as this is considered to be part of the 2136 message as per RFC-1939, sect. 3 */ 2137 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2); 2138 2139 k->keepon &= ~KEEP_RECV; 2140 pop3c->eob = 0; 2141 2142 return result; 2143 } 2144 2145 if(pop3c->eob) 2146 /* While EOB is matching nothing should be output */ 2147 return CURLE_OK; 2148 2149 if(nread - last) { 2150 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last], 2151 nread - last); 2152 } 2153 2154 return result; 2155} 2156 2157#endif /* CURL_DISABLE_POP3 */ 2158