1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2014, 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_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 (void)instate; /* no use for this yet */ 982 983 if(pop3code != '+') { 984 failf(data, "Access denied: %d", pop3code); 985 return CURLE_LOGIN_DENIED; 986 } 987 988 /* Get the challenge message */ 989 pop3_get_message(data->state.buffer, &chlg64); 990 991 /* Create the response message */ 992 result = Curl_sasl_create_digest_md5_message(data, chlg64, 993 conn->user, conn->passwd, 994 "pop", &rplyb64, &len); 995 if(result) { 996 if(result == CURLE_BAD_CONTENT_ENCODING) { 997 /* Send the cancellation */ 998 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*"); 999 1000 if(!result) 1001 state(conn, POP3_AUTH_CANCEL); 1002 } 1003 } 1004 else { 1005 /* Send the response */ 1006 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64); 1007 1008 if(!result) 1009 state(conn, POP3_AUTH_DIGESTMD5_RESP); 1010 } 1011 1012 Curl_safefree(rplyb64); 1013 1014 return result; 1015} 1016 1017/* For AUTH DIGEST-MD5 challenge-response responses */ 1018static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn, 1019 int pop3code, 1020 pop3state instate) 1021{ 1022 CURLcode result = CURLE_OK; 1023 struct SessionHandle *data = conn->data; 1024 1025 (void)instate; /* no use for this yet */ 1026 1027 if(pop3code != '+') { 1028 failf(data, "Authentication failed: %d", pop3code); 1029 result = CURLE_LOGIN_DENIED; 1030 } 1031 else { 1032 /* Send an empty response */ 1033 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", ""); 1034 1035 if(!result) 1036 state(conn, POP3_AUTH_FINAL); 1037 } 1038 1039 return result; 1040} 1041#endif 1042 1043#ifdef USE_NTLM 1044/* For AUTH NTLM (without initial response) responses */ 1045static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn, 1046 int pop3code, 1047 pop3state instate) 1048{ 1049 CURLcode result = CURLE_OK; 1050 struct SessionHandle *data = conn->data; 1051 size_t len = 0; 1052 char *type1msg = NULL; 1053 1054 (void)instate; /* no use for this yet */ 1055 1056 if(pop3code != '+') { 1057 failf(data, "Access denied: %d", pop3code); 1058 result = CURLE_LOGIN_DENIED; 1059 } 1060 else { 1061 /* Create the type-1 message */ 1062 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, 1063 &conn->ntlm, 1064 &type1msg, &len); 1065 if(!result && type1msg) { 1066 /* Send the message */ 1067 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg); 1068 1069 if(!result) 1070 state(conn, POP3_AUTH_NTLM_TYPE2MSG); 1071 } 1072 } 1073 1074 Curl_safefree(type1msg); 1075 1076 return result; 1077} 1078 1079/* For NTLM type-2 responses (sent in reponse to our type-1 message) */ 1080static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn, 1081 int pop3code, 1082 pop3state instate) 1083{ 1084 CURLcode result = CURLE_OK; 1085 struct SessionHandle *data = conn->data; 1086 char *type2msg = NULL; 1087 char *type3msg = NULL; 1088 size_t len = 0; 1089 1090 (void)instate; /* no use for this yet */ 1091 1092 if(pop3code != '+') { 1093 failf(data, "Access denied: %d", pop3code); 1094 result = CURLE_LOGIN_DENIED; 1095 } 1096 else { 1097 /* Get the type-2 message */ 1098 pop3_get_message(data->state.buffer, &type2msg); 1099 1100 /* Decode the type-2 message */ 1101 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm); 1102 if(result) { 1103 /* Send the cancellation */ 1104 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*"); 1105 1106 if(!result) 1107 state(conn, POP3_AUTH_CANCEL); 1108 } 1109 else { 1110 /* Create the type-3 message */ 1111 result = Curl_sasl_create_ntlm_type3_message(data, conn->user, 1112 conn->passwd, &conn->ntlm, 1113 &type3msg, &len); 1114 if(!result && type3msg) { 1115 /* Send the message */ 1116 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg); 1117 1118 if(!result) 1119 state(conn, POP3_AUTH_FINAL); 1120 } 1121 } 1122 } 1123 1124 Curl_safefree(type3msg); 1125 1126 return result; 1127} 1128#endif 1129 1130/* For AUTH XOAUTH2 (without initial response) responses */ 1131static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn, 1132 int pop3code, pop3state instate) 1133{ 1134 CURLcode result = CURLE_OK; 1135 struct SessionHandle *data = conn->data; 1136 size_t len = 0; 1137 char *xoauth = NULL; 1138 1139 (void)instate; /* no use for this yet */ 1140 1141 if(pop3code != '+') { 1142 failf(data, "Access denied: %d", pop3code); 1143 result = CURLE_LOGIN_DENIED; 1144 } 1145 else { 1146 /* Create the authorisation message */ 1147 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, 1148 conn->xoauth2_bearer, 1149 &xoauth, &len); 1150 if(!result && xoauth) { 1151 /* Send the message */ 1152 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth); 1153 1154 if(!result) 1155 state(conn, POP3_AUTH_FINAL); 1156 } 1157 } 1158 1159 Curl_safefree(xoauth); 1160 1161 return result; 1162} 1163 1164/* For AUTH cancellation responses */ 1165static CURLcode pop3_state_auth_cancel_resp(struct connectdata *conn, 1166 int pop3code, 1167 pop3state instate) 1168{ 1169 CURLcode result = CURLE_OK; 1170 struct SessionHandle *data = conn->data; 1171 struct pop3_conn *pop3c = &conn->proto.pop3c; 1172 const char *mech = NULL; 1173 char *initresp = NULL; 1174 size_t len = 0; 1175 pop3state state1 = POP3_STOP; 1176 pop3state state2 = POP3_STOP; 1177 1178 (void)pop3code; 1179 (void)instate; /* no use for this yet */ 1180 1181 /* Remove the offending mechanism from the supported list */ 1182 pop3c->authmechs ^= pop3c->authused; 1183 1184 /* Calculate alternative SASL login details */ 1185 result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1, 1186 &state2); 1187 1188 if(!result) { 1189 /* Do we have any mechanisms left or can we fallback to another 1190 authentication type? */ 1191 if(mech) { 1192 /* Retry SASL based authentication */ 1193 result = pop3_perform_auth(conn, mech, initresp, len, state1, state2); 1194 1195 Curl_safefree(initresp); 1196 } 1197#ifndef CURL_DISABLE_CRYPTO_AUTH 1198 else if((pop3c->authtypes & POP3_TYPE_APOP) && 1199 (pop3c->preftype & POP3_TYPE_APOP)) 1200 /* Perform APOP authentication */ 1201 result = pop3_perform_apop(conn); 1202#endif 1203 else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) && 1204 (pop3c->preftype & POP3_TYPE_CLEARTEXT)) 1205 /* Perform clear text authentication */ 1206 result = pop3_perform_user(conn); 1207 else { 1208 failf(data, "Authentication cancelled"); 1209 1210 result = CURLE_LOGIN_DENIED; 1211 } 1212 } 1213 1214 return result; 1215} 1216 1217/* For final responses in the AUTH sequence */ 1218static CURLcode pop3_state_auth_final_resp(struct connectdata *conn, 1219 int pop3code, 1220 pop3state instate) 1221{ 1222 CURLcode result = CURLE_OK; 1223 struct SessionHandle *data = conn->data; 1224 1225 (void)instate; /* no use for this yet */ 1226 1227 if(pop3code != '+') { 1228 failf(data, "Authentication failed: %d", pop3code); 1229 result = CURLE_LOGIN_DENIED; 1230 } 1231 else 1232 /* End of connect phase */ 1233 state(conn, POP3_STOP); 1234 1235 return result; 1236} 1237 1238#ifndef CURL_DISABLE_CRYPTO_AUTH 1239/* For APOP responses */ 1240static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code, 1241 pop3state instate) 1242{ 1243 CURLcode result = CURLE_OK; 1244 struct SessionHandle *data = conn->data; 1245 1246 (void)instate; /* no use for this yet */ 1247 1248 if(pop3code != '+') { 1249 failf(data, "Authentication failed: %d", pop3code); 1250 result = CURLE_LOGIN_DENIED; 1251 } 1252 else 1253 /* End of connect phase */ 1254 state(conn, POP3_STOP); 1255 1256 return result; 1257} 1258#endif 1259 1260/* For USER responses */ 1261static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code, 1262 pop3state instate) 1263{ 1264 CURLcode result = CURLE_OK; 1265 struct SessionHandle *data = conn->data; 1266 1267 (void)instate; /* no use for this yet */ 1268 1269 if(pop3code != '+') { 1270 failf(data, "Access denied. %c", pop3code); 1271 result = CURLE_LOGIN_DENIED; 1272 } 1273 else 1274 /* Send the PASS command */ 1275 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s", 1276 conn->passwd ? conn->passwd : ""); 1277 if(!result) 1278 state(conn, POP3_PASS); 1279 1280 return result; 1281} 1282 1283/* For PASS responses */ 1284static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code, 1285 pop3state instate) 1286{ 1287 CURLcode result = CURLE_OK; 1288 struct SessionHandle *data = conn->data; 1289 1290 (void)instate; /* no use for this yet */ 1291 1292 if(pop3code != '+') { 1293 failf(data, "Access denied. %c", pop3code); 1294 result = CURLE_LOGIN_DENIED; 1295 } 1296 else 1297 /* End of connect phase */ 1298 state(conn, POP3_STOP); 1299 1300 return result; 1301} 1302 1303/* For command responses */ 1304static CURLcode pop3_state_command_resp(struct connectdata *conn, 1305 int pop3code, 1306 pop3state instate) 1307{ 1308 CURLcode result = CURLE_OK; 1309 struct SessionHandle *data = conn->data; 1310 struct POP3 *pop3 = data->req.protop; 1311 struct pop3_conn *pop3c = &conn->proto.pop3c; 1312 struct pingpong *pp = &pop3c->pp; 1313 1314 (void)instate; /* no use for this yet */ 1315 1316 if(pop3code != '+') { 1317 state(conn, POP3_STOP); 1318 return CURLE_RECV_ERROR; 1319 } 1320 1321 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the 1322 EOB string so count this is two matching bytes. This is necessary to make 1323 the code detect the EOB if the only data than comes now is %2e CR LF like 1324 when there is no body to return. */ 1325 pop3c->eob = 2; 1326 1327 /* But since this initial CR LF pair is not part of the actual body, we set 1328 the strip counter here so that these bytes won't be delivered. */ 1329 pop3c->strip = 2; 1330 1331 if(pop3->transfer == FTPTRANSFER_BODY) { 1332 /* POP3 download */ 1333 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL); 1334 1335 if(pp->cache) { 1336 /* The header "cache" contains a bunch of data that is actually body 1337 content so send it as such. Note that there may even be additional 1338 "headers" after the body */ 1339 1340 if(!data->set.opt_no_body) { 1341 result = Curl_pop3_write(conn, pp->cache, pp->cache_size); 1342 if(result) 1343 return result; 1344 } 1345 1346 /* Free the cache */ 1347 Curl_safefree(pp->cache); 1348 1349 /* Reset the cache size */ 1350 pp->cache_size = 0; 1351 } 1352 } 1353 1354 /* End of DO phase */ 1355 state(conn, POP3_STOP); 1356 1357 return result; 1358} 1359 1360static CURLcode pop3_statemach_act(struct connectdata *conn) 1361{ 1362 CURLcode result = CURLE_OK; 1363 curl_socket_t sock = conn->sock[FIRSTSOCKET]; 1364 int pop3code; 1365 struct pop3_conn *pop3c = &conn->proto.pop3c; 1366 struct pingpong *pp = &pop3c->pp; 1367 size_t nread = 0; 1368 1369 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */ 1370 if(pop3c->state == POP3_UPGRADETLS) 1371 return pop3_perform_upgrade_tls(conn); 1372 1373 /* Flush any data that needs to be sent */ 1374 if(pp->sendleft) 1375 return Curl_pp_flushsend(pp); 1376 1377 do { 1378 /* Read the response from the server */ 1379 result = Curl_pp_readresp(sock, pp, &pop3code, &nread); 1380 if(result) 1381 return result; 1382 1383 if(!pop3code) 1384 break; 1385 1386 /* We have now received a full POP3 server response */ 1387 switch(pop3c->state) { 1388 case POP3_SERVERGREET: 1389 result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state); 1390 break; 1391 1392 case POP3_CAPA: 1393 result = pop3_state_capa_resp(conn, pop3code, pop3c->state); 1394 break; 1395 1396 case POP3_STARTTLS: 1397 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state); 1398 break; 1399 1400 case POP3_AUTH_PLAIN: 1401 result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state); 1402 break; 1403 1404 case POP3_AUTH_LOGIN: 1405 result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state); 1406 break; 1407 1408 case POP3_AUTH_LOGIN_PASSWD: 1409 result = pop3_state_auth_login_password_resp(conn, pop3code, 1410 pop3c->state); 1411 break; 1412 1413#ifndef CURL_DISABLE_CRYPTO_AUTH 1414 case POP3_AUTH_CRAMMD5: 1415 result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state); 1416 break; 1417 1418 case POP3_AUTH_DIGESTMD5: 1419 result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state); 1420 break; 1421 1422 case POP3_AUTH_DIGESTMD5_RESP: 1423 result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state); 1424 break; 1425#endif 1426 1427#ifdef USE_NTLM 1428 case POP3_AUTH_NTLM: 1429 result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state); 1430 break; 1431 1432 case POP3_AUTH_NTLM_TYPE2MSG: 1433 result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code, 1434 pop3c->state); 1435 break; 1436#endif 1437 1438 case POP3_AUTH_XOAUTH2: 1439 result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state); 1440 break; 1441 1442 case POP3_AUTH_CANCEL: 1443 result = pop3_state_auth_cancel_resp(conn, pop3code, pop3c->state); 1444 break; 1445 1446 case POP3_AUTH_FINAL: 1447 result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state); 1448 break; 1449 1450#ifndef CURL_DISABLE_CRYPTO_AUTH 1451 case POP3_APOP: 1452 result = pop3_state_apop_resp(conn, pop3code, pop3c->state); 1453 break; 1454#endif 1455 1456 case POP3_USER: 1457 result = pop3_state_user_resp(conn, pop3code, pop3c->state); 1458 break; 1459 1460 case POP3_PASS: 1461 result = pop3_state_pass_resp(conn, pop3code, pop3c->state); 1462 break; 1463 1464 case POP3_COMMAND: 1465 result = pop3_state_command_resp(conn, pop3code, pop3c->state); 1466 break; 1467 1468 case POP3_QUIT: 1469 /* fallthrough, just stop! */ 1470 default: 1471 /* internal error */ 1472 state(conn, POP3_STOP); 1473 break; 1474 } 1475 } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp)); 1476 1477 return result; 1478} 1479 1480/* Called repeatedly until done from multi.c */ 1481static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done) 1482{ 1483 CURLcode result = CURLE_OK; 1484 struct pop3_conn *pop3c = &conn->proto.pop3c; 1485 1486 if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) { 1487 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone); 1488 if(result || !pop3c->ssldone) 1489 return result; 1490 } 1491 1492 result = Curl_pp_statemach(&pop3c->pp, FALSE); 1493 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE; 1494 1495 return result; 1496} 1497 1498static CURLcode pop3_block_statemach(struct connectdata *conn) 1499{ 1500 CURLcode result = CURLE_OK; 1501 struct pop3_conn *pop3c = &conn->proto.pop3c; 1502 1503 while(pop3c->state != POP3_STOP && !result) 1504 result = Curl_pp_statemach(&pop3c->pp, TRUE); 1505 1506 return result; 1507} 1508 1509/* Allocate and initialize the POP3 struct for the current SessionHandle if 1510 required */ 1511static CURLcode pop3_init(struct connectdata *conn) 1512{ 1513 CURLcode result = CURLE_OK; 1514 struct SessionHandle *data = conn->data; 1515 struct POP3 *pop3; 1516 1517 pop3 = data->req.protop = calloc(sizeof(struct POP3), 1); 1518 if(!pop3) 1519 result = CURLE_OUT_OF_MEMORY; 1520 1521 return result; 1522} 1523 1524/* For the POP3 "protocol connect" and "doing" phases only */ 1525static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks, 1526 int numsocks) 1527{ 1528 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks); 1529} 1530 1531/*********************************************************************** 1532 * 1533 * pop3_connect() 1534 * 1535 * This function should do everything that is to be considered a part of the 1536 * connection phase. 1537 * 1538 * The variable 'done' points to will be TRUE if the protocol-layer connect 1539 * phase is done when this function returns, or FALSE if not. 1540 */ 1541static CURLcode pop3_connect(struct connectdata *conn, bool *done) 1542{ 1543 CURLcode result = CURLE_OK; 1544 struct pop3_conn *pop3c = &conn->proto.pop3c; 1545 struct pingpong *pp = &pop3c->pp; 1546 1547 *done = FALSE; /* default to not done yet */ 1548 1549 /* We always support persistent connections in POP3 */ 1550 connkeep(conn, "POP3 default"); 1551 1552 /* Set the default response time-out */ 1553 pp->response_time = RESP_TIMEOUT; 1554 pp->statemach_act = pop3_statemach_act; 1555 pp->endofresp = pop3_endofresp; 1556 pp->conn = conn; 1557 1558 /* Set the default preferred authentication type and mechanism */ 1559 pop3c->preftype = POP3_TYPE_ANY; 1560 pop3c->prefmech = SASL_AUTH_ANY; 1561 1562 /* Initialise the pingpong layer */ 1563 Curl_pp_init(pp); 1564 1565 /* Parse the URL options */ 1566 result = pop3_parse_url_options(conn); 1567 if(result) 1568 return result; 1569 1570 /* Start off waiting for the server greeting response */ 1571 state(conn, POP3_SERVERGREET); 1572 1573 result = pop3_multi_statemach(conn, done); 1574 1575 return result; 1576} 1577 1578/*********************************************************************** 1579 * 1580 * pop3_done() 1581 * 1582 * The DONE function. This does what needs to be done after a single DO has 1583 * performed. 1584 * 1585 * Input argument is already checked for validity. 1586 */ 1587static CURLcode pop3_done(struct connectdata *conn, CURLcode status, 1588 bool premature) 1589{ 1590 CURLcode result = CURLE_OK; 1591 struct SessionHandle *data = conn->data; 1592 struct POP3 *pop3 = data->req.protop; 1593 1594 (void)premature; 1595 1596 if(!pop3) 1597 /* When the easy handle is removed from the multi interface while libcurl 1598 is still trying to resolve the host name, the POP3 struct is not yet 1599 initialized. However, the removal action calls Curl_done() which in 1600 turn calls this function, so we simply return success. */ 1601 return CURLE_OK; 1602 1603 if(status) { 1604 connclose(conn, "POP3 done with bad status"); 1605 result = status; /* use the already set error code */ 1606 } 1607 1608 /* Cleanup our per-request based variables */ 1609 Curl_safefree(pop3->id); 1610 Curl_safefree(pop3->custom); 1611 1612 /* Clear the transfer mode for the next request */ 1613 pop3->transfer = FTPTRANSFER_BODY; 1614 1615 return result; 1616} 1617 1618/*********************************************************************** 1619 * 1620 * pop3_perform() 1621 * 1622 * This is the actual DO function for POP3. Get a message/listing according to 1623 * the options previously setup. 1624 */ 1625static CURLcode pop3_perform(struct connectdata *conn, bool *connected, 1626 bool *dophase_done) 1627{ 1628 /* This is POP3 and no proxy */ 1629 CURLcode result = CURLE_OK; 1630 struct POP3 *pop3 = conn->data->req.protop; 1631 1632 DEBUGF(infof(conn->data, "DO phase starts\n")); 1633 1634 if(conn->data->set.opt_no_body) { 1635 /* Requested no body means no transfer */ 1636 pop3->transfer = FTPTRANSFER_INFO; 1637 } 1638 1639 *dophase_done = FALSE; /* not done yet */ 1640 1641 /* Start the first command in the DO phase */ 1642 result = pop3_perform_command(conn); 1643 if(result) 1644 return result; 1645 1646 /* Run the state-machine */ 1647 result = pop3_multi_statemach(conn, dophase_done); 1648 1649 *connected = conn->bits.tcpconnect[FIRSTSOCKET]; 1650 1651 if(*dophase_done) 1652 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1653 1654 return result; 1655} 1656 1657/*********************************************************************** 1658 * 1659 * pop3_do() 1660 * 1661 * This function is registered as 'curl_do' function. It decodes the path 1662 * parts etc as a wrapper to the actual DO function (pop3_perform). 1663 * 1664 * The input argument is already checked for validity. 1665 */ 1666static CURLcode pop3_do(struct connectdata *conn, bool *done) 1667{ 1668 CURLcode result = CURLE_OK; 1669 1670 *done = FALSE; /* default to false */ 1671 1672 /* Parse the URL path */ 1673 result = pop3_parse_url_path(conn); 1674 if(result) 1675 return result; 1676 1677 /* Parse the custom request */ 1678 result = pop3_parse_custom_request(conn); 1679 if(result) 1680 return result; 1681 1682 result = pop3_regular_transfer(conn, done); 1683 1684 return result; 1685} 1686 1687/*********************************************************************** 1688 * 1689 * pop3_disconnect() 1690 * 1691 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection 1692 * resources. BLOCKING. 1693 */ 1694static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection) 1695{ 1696 struct pop3_conn *pop3c = &conn->proto.pop3c; 1697 1698 /* We cannot send quit unconditionally. If this connection is stale or 1699 bad in any way, sending quit and waiting around here will make the 1700 disconnect wait in vain and cause more problems than we need to. */ 1701 1702 /* The POP3 session may or may not have been allocated/setup at this 1703 point! */ 1704 if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart) 1705 if(!pop3_perform_quit(conn)) 1706 (void)pop3_block_statemach(conn); /* ignore errors on QUIT */ 1707 1708 /* Disconnect from the server */ 1709 Curl_pp_disconnect(&pop3c->pp); 1710 1711 /* Cleanup the SASL module */ 1712 Curl_sasl_cleanup(conn, pop3c->authused); 1713 1714 /* Cleanup our connection based variables */ 1715 Curl_safefree(pop3c->apoptimestamp); 1716 1717 return CURLE_OK; 1718} 1719 1720/* Call this when the DO phase has completed */ 1721static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected) 1722{ 1723 (void)conn; 1724 (void)connected; 1725 1726 return CURLE_OK; 1727} 1728 1729/* Called from multi.c while DOing */ 1730static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done) 1731{ 1732 CURLcode result = pop3_multi_statemach(conn, dophase_done); 1733 1734 if(result) 1735 DEBUGF(infof(conn->data, "DO phase failed\n")); 1736 else if(*dophase_done) { 1737 result = pop3_dophase_done(conn, FALSE /* not connected */); 1738 1739 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1740 } 1741 1742 return result; 1743} 1744 1745/*********************************************************************** 1746 * 1747 * pop3_regular_transfer() 1748 * 1749 * The input argument is already checked for validity. 1750 * 1751 * Performs all commands done before a regular transfer between a local and a 1752 * remote host. 1753 */ 1754static CURLcode pop3_regular_transfer(struct connectdata *conn, 1755 bool *dophase_done) 1756{ 1757 CURLcode result = CURLE_OK; 1758 bool connected = FALSE; 1759 struct SessionHandle *data = conn->data; 1760 1761 /* Make sure size is unknown at this point */ 1762 data->req.size = -1; 1763 1764 /* Set the progress data */ 1765 Curl_pgrsSetUploadCounter(data, 0); 1766 Curl_pgrsSetDownloadCounter(data, 0); 1767 Curl_pgrsSetUploadSize(data, 0); 1768 Curl_pgrsSetDownloadSize(data, 0); 1769 1770 /* Carry out the perform */ 1771 result = pop3_perform(conn, &connected, dophase_done); 1772 1773 /* Perform post DO phase operations if necessary */ 1774 if(!result && *dophase_done) 1775 result = pop3_dophase_done(conn, connected); 1776 1777 return result; 1778} 1779 1780static CURLcode pop3_setup_connection(struct connectdata *conn) 1781{ 1782 struct SessionHandle *data = conn->data; 1783 1784 /* Initialise the POP3 layer */ 1785 CURLcode result = pop3_init(conn); 1786 if(result) 1787 return result; 1788 1789 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { 1790 /* Unless we have asked to tunnel POP3 operations through the proxy, we 1791 switch and use HTTP operations only */ 1792#ifndef CURL_DISABLE_HTTP 1793 if(conn->handler == &Curl_handler_pop3) 1794 conn->handler = &Curl_handler_pop3_proxy; 1795 else { 1796#ifdef USE_SSL 1797 conn->handler = &Curl_handler_pop3s_proxy; 1798#else 1799 failf(data, "POP3S not supported!"); 1800 return CURLE_UNSUPPORTED_PROTOCOL; 1801#endif 1802 } 1803 1804 /* set it up as an HTTP connection instead */ 1805 return conn->handler->setup_connection(conn); 1806#else 1807 failf(data, "POP3 over http proxy requires HTTP support built-in!"); 1808 return CURLE_UNSUPPORTED_PROTOCOL; 1809#endif 1810 } 1811 1812 data->state.path++; /* don't include the initial slash */ 1813 1814 return CURLE_OK; 1815} 1816 1817/*********************************************************************** 1818 * 1819 * pop3_parse_url_options() 1820 * 1821 * Parse the URL login options. 1822 */ 1823static CURLcode pop3_parse_url_options(struct connectdata *conn) 1824{ 1825 CURLcode result = CURLE_OK; 1826 struct pop3_conn *pop3c = &conn->proto.pop3c; 1827 const char *options = conn->options; 1828 const char *ptr = options; 1829 bool reset = TRUE; 1830 1831 while(ptr && *ptr) { 1832 const char *key = ptr; 1833 1834 while(*ptr && *ptr != '=') 1835 ptr++; 1836 1837 if(strnequal(key, "AUTH", 4)) { 1838 size_t len = 0; 1839 const char *value = ++ptr; 1840 1841 if(reset) { 1842 reset = FALSE; 1843 pop3c->preftype = POP3_TYPE_NONE; 1844 pop3c->prefmech = SASL_AUTH_NONE; 1845 } 1846 1847 while(*ptr && *ptr != ';') { 1848 ptr++; 1849 len++; 1850 } 1851 1852 if(strnequal(value, "*", len)) { 1853 pop3c->preftype = POP3_TYPE_ANY; 1854 pop3c->prefmech = SASL_AUTH_ANY; 1855 } 1856 else if(strnequal(value, "+APOP", len)) { 1857 pop3c->preftype = POP3_TYPE_APOP; 1858 pop3c->prefmech = SASL_AUTH_NONE; 1859 } 1860 else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) { 1861 pop3c->preftype = POP3_TYPE_SASL; 1862 pop3c->prefmech |= SASL_MECH_LOGIN; 1863 } 1864 else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) { 1865 pop3c->preftype = POP3_TYPE_SASL; 1866 pop3c->prefmech |= SASL_MECH_PLAIN; 1867 } 1868 else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) { 1869 pop3c->preftype = POP3_TYPE_SASL; 1870 pop3c->prefmech |= SASL_MECH_CRAM_MD5; 1871 } 1872 else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) { 1873 pop3c->preftype = POP3_TYPE_SASL; 1874 pop3c->prefmech |= SASL_MECH_DIGEST_MD5; 1875 } 1876 else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) { 1877 pop3c->preftype = POP3_TYPE_SASL; 1878 pop3c->prefmech |= SASL_MECH_GSSAPI; 1879 } 1880 else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) { 1881 pop3c->preftype = POP3_TYPE_SASL; 1882 pop3c->prefmech |= SASL_MECH_NTLM; 1883 } 1884 else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) { 1885 pop3c->preftype = POP3_TYPE_SASL; 1886 pop3c->prefmech |= SASL_MECH_XOAUTH2; 1887 } 1888 1889 if(*ptr == ';') 1890 ptr++; 1891 } 1892 else 1893 result = CURLE_URL_MALFORMAT; 1894 } 1895 1896 return result; 1897} 1898 1899/*********************************************************************** 1900 * 1901 * pop3_parse_url_path() 1902 * 1903 * Parse the URL path into separate path components. 1904 */ 1905static CURLcode pop3_parse_url_path(struct connectdata *conn) 1906{ 1907 /* The POP3 struct is already initialised in pop3_connect() */ 1908 struct SessionHandle *data = conn->data; 1909 struct POP3 *pop3 = data->req.protop; 1910 const char *path = data->state.path; 1911 1912 /* URL decode the path for the message ID */ 1913 return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE); 1914} 1915 1916/*********************************************************************** 1917 * 1918 * pop3_parse_custom_request() 1919 * 1920 * Parse the custom request. 1921 */ 1922static CURLcode pop3_parse_custom_request(struct connectdata *conn) 1923{ 1924 CURLcode result = CURLE_OK; 1925 struct SessionHandle *data = conn->data; 1926 struct POP3 *pop3 = data->req.protop; 1927 const char *custom = data->set.str[STRING_CUSTOMREQUEST]; 1928 1929 /* URL decode the custom request */ 1930 if(custom) 1931 result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE); 1932 1933 return result; 1934} 1935 1936/*********************************************************************** 1937 * 1938 * pop3_calc_sasl_details() 1939 * 1940 * Calculate the required login details for SASL authentication. 1941 */ 1942static CURLcode pop3_calc_sasl_details(struct connectdata *conn, 1943 const char **mech, 1944 char **initresp, size_t *len, 1945 pop3state *state1, pop3state *state2) 1946{ 1947 CURLcode result = CURLE_OK; 1948 struct SessionHandle *data = conn->data; 1949 struct pop3_conn *pop3c = &conn->proto.pop3c; 1950 1951 /* Calculate the supported authentication mechanism, by decreasing order of 1952 security, as well as the initial response where appropriate */ 1953#ifndef CURL_DISABLE_CRYPTO_AUTH 1954 if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) && 1955 (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) { 1956 *mech = SASL_MECH_STRING_DIGEST_MD5; 1957 *state1 = POP3_AUTH_DIGESTMD5; 1958 pop3c->authused = SASL_MECH_DIGEST_MD5; 1959 } 1960 else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) && 1961 (pop3c->prefmech & SASL_MECH_CRAM_MD5)) { 1962 *mech = SASL_MECH_STRING_CRAM_MD5; 1963 *state1 = POP3_AUTH_CRAMMD5; 1964 pop3c->authused = SASL_MECH_CRAM_MD5; 1965 } 1966 else 1967#endif 1968#ifdef USE_NTLM 1969 if((pop3c->authmechs & SASL_MECH_NTLM) && 1970 (pop3c->prefmech & SASL_MECH_NTLM)) { 1971 *mech = SASL_MECH_STRING_NTLM; 1972 *state1 = POP3_AUTH_NTLM; 1973 *state2 = POP3_AUTH_NTLM_TYPE2MSG; 1974 pop3c->authused = SASL_MECH_NTLM; 1975 1976 if(data->set.sasl_ir) 1977 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, 1978 &conn->ntlm, 1979 initresp, len); 1980 } 1981 else 1982#endif 1983 if(((pop3c->authmechs & SASL_MECH_XOAUTH2) && 1984 (pop3c->prefmech & SASL_MECH_XOAUTH2) && 1985 (pop3c->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { 1986 *mech = SASL_MECH_STRING_XOAUTH2; 1987 *state1 = POP3_AUTH_XOAUTH2; 1988 *state2 = POP3_AUTH_FINAL; 1989 pop3c->authused = SASL_MECH_XOAUTH2; 1990 1991 if(data->set.sasl_ir) 1992 result = Curl_sasl_create_xoauth2_message(data, conn->user, 1993 conn->xoauth2_bearer, 1994 initresp, len); 1995 } 1996 else if((pop3c->authmechs & SASL_MECH_LOGIN) && 1997 (pop3c->prefmech & SASL_MECH_LOGIN)) { 1998 *mech = SASL_MECH_STRING_LOGIN; 1999 *state1 = POP3_AUTH_LOGIN; 2000 *state2 = POP3_AUTH_LOGIN_PASSWD; 2001 pop3c->authused = SASL_MECH_LOGIN; 2002 2003 if(data->set.sasl_ir) 2004 result = Curl_sasl_create_login_message(data, conn->user, initresp, len); 2005 } 2006 else if((pop3c->authmechs & SASL_MECH_PLAIN) && 2007 (pop3c->prefmech & SASL_MECH_PLAIN)) { 2008 *mech = SASL_MECH_STRING_PLAIN; 2009 *state1 = POP3_AUTH_PLAIN; 2010 *state2 = POP3_AUTH_FINAL; 2011 pop3c->authused = SASL_MECH_PLAIN; 2012 2013 if(data->set.sasl_ir) 2014 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, 2015 initresp, len); 2016 } 2017 2018 return result; 2019} 2020 2021/*********************************************************************** 2022 * 2023 * Curl_pop3_write() 2024 * 2025 * This function scans the body after the end-of-body and writes everything 2026 * until the end is found. 2027 */ 2028CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread) 2029{ 2030 /* This code could be made into a special function in the handler struct */ 2031 CURLcode result = CURLE_OK; 2032 struct SessionHandle *data = conn->data; 2033 struct SingleRequest *k = &data->req; 2034 2035 struct pop3_conn *pop3c = &conn->proto.pop3c; 2036 bool strip_dot = FALSE; 2037 size_t last = 0; 2038 size_t i; 2039 2040 /* Search through the buffer looking for the end-of-body marker which is 2041 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches 2042 the eob so the server will have prefixed it with an extra dot which we 2043 need to strip out. Additionally the marker could of course be spread out 2044 over 5 different data chunks. */ 2045 for(i = 0; i < nread; i++) { 2046 size_t prev = pop3c->eob; 2047 2048 switch(str[i]) { 2049 case 0x0d: 2050 if(pop3c->eob == 0) { 2051 pop3c->eob++; 2052 2053 if(i) { 2054 /* Write out the body part that didn't match */ 2055 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last], 2056 i - last); 2057 2058 if(result) 2059 return result; 2060 2061 last = i; 2062 } 2063 } 2064 else if(pop3c->eob == 3) 2065 pop3c->eob++; 2066 else 2067 /* If the character match wasn't at position 0 or 3 then restart the 2068 pattern matching */ 2069 pop3c->eob = 1; 2070 break; 2071 2072 case 0x0a: 2073 if(pop3c->eob == 1 || pop3c->eob == 4) 2074 pop3c->eob++; 2075 else 2076 /* If the character match wasn't at position 1 or 4 then start the 2077 search again */ 2078 pop3c->eob = 0; 2079 break; 2080 2081 case 0x2e: 2082 if(pop3c->eob == 2) 2083 pop3c->eob++; 2084 else if(pop3c->eob == 3) { 2085 /* We have an extra dot after the CRLF which we need to strip off */ 2086 strip_dot = TRUE; 2087 pop3c->eob = 0; 2088 } 2089 else 2090 /* If the character match wasn't at position 2 then start the search 2091 again */ 2092 pop3c->eob = 0; 2093 break; 2094 2095 default: 2096 pop3c->eob = 0; 2097 break; 2098 } 2099 2100 /* Did we have a partial match which has subsequently failed? */ 2101 if(prev && prev >= pop3c->eob) { 2102 /* Strip can only be non-zero for the very first mismatch after CRLF 2103 and then both prev and strip are equal and nothing will be output 2104 below */ 2105 while(prev && pop3c->strip) { 2106 prev--; 2107 pop3c->strip--; 2108 } 2109 2110 if(prev) { 2111 /* If the partial match was the CRLF and dot then only write the CRLF 2112 as the server would have inserted the dot */ 2113 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB, 2114 strip_dot ? prev - 1 : prev); 2115 2116 if(result) 2117 return result; 2118 2119 last = i; 2120 strip_dot = FALSE; 2121 } 2122 } 2123 } 2124 2125 if(pop3c->eob == POP3_EOB_LEN) { 2126 /* We have a full match so the transfer is done, however we must transfer 2127 the CRLF at the start of the EOB as this is considered to be part of the 2128 message as per RFC-1939, sect. 3 */ 2129 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2); 2130 2131 k->keepon &= ~KEEP_RECV; 2132 pop3c->eob = 0; 2133 2134 return result; 2135 } 2136 2137 if(pop3c->eob) 2138 /* While EOB is matching nothing should be output */ 2139 return CURLE_OK; 2140 2141 if(nread - last) { 2142 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last], 2143 nread - last); 2144 } 2145 2146 return result; 2147} 2148 2149#endif /* CURL_DISABLE_POP3 */ 2150