1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2011, 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 * RFC1939 POP3 protocol 22 * RFC2384 POP URL Scheme 23 * RFC2595 Using TLS with IMAP, POP3 and ACAP 24 * 25 ***************************************************************************/ 26 27#include "setup.h" 28 29#ifndef CURL_DISABLE_POP3 30 31#ifdef HAVE_UNISTD_H 32#include <unistd.h> 33#endif 34 35#ifdef HAVE_SYS_SOCKET_H 36#include <sys/socket.h> 37#endif 38#ifdef HAVE_NETINET_IN_H 39#include <netinet/in.h> 40#endif 41#ifdef HAVE_ARPA_INET_H 42#include <arpa/inet.h> 43#endif 44#ifdef HAVE_UTSNAME_H 45#include <sys/utsname.h> 46#endif 47#ifdef HAVE_NETDB_H 48#include <netdb.h> 49#endif 50#ifdef __VMS 51#include <in.h> 52#include <inet.h> 53#endif 54 55#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) 56#undef in_addr_t 57#define in_addr_t unsigned long 58#endif 59 60#include <curl/curl.h> 61#include "urldata.h" 62#include "sendf.h" 63#include "if2ip.h" 64#include "hostip.h" 65#include "progress.h" 66#include "transfer.h" 67#include "escape.h" 68#include "http.h" /* for HTTP proxy tunnel stuff */ 69#include "socks.h" 70#include "pop3.h" 71 72#include "strtoofft.h" 73#include "strequal.h" 74#include "sslgen.h" 75#include "connect.h" 76#include "strerror.h" 77#include "select.h" 78#include "multiif.h" 79#include "url.h" 80#include "rawstr.h" 81#include "strtoofft.h" 82#include "http_proxy.h" 83 84#define _MPRINTF_REPLACE /* use our functions only */ 85#include <curl/mprintf.h> 86 87#include "curl_memory.h" 88/* The last #include file should be: */ 89#include "memdebug.h" 90 91/* Local API functions */ 92static CURLcode pop3_parse_url_path(struct connectdata *conn); 93static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done); 94static CURLcode pop3_do(struct connectdata *conn, bool *done); 95static CURLcode pop3_done(struct connectdata *conn, 96 CURLcode, bool premature); 97static CURLcode pop3_connect(struct connectdata *conn, bool *done); 98static CURLcode pop3_disconnect(struct connectdata *conn, bool dead); 99static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done); 100static int pop3_getsock(struct connectdata *conn, 101 curl_socket_t *socks, 102 int numsocks); 103static CURLcode pop3_doing(struct connectdata *conn, 104 bool *dophase_done); 105static CURLcode pop3_setup_connection(struct connectdata * conn); 106 107/* 108 * POP3 protocol handler. 109 */ 110 111const struct Curl_handler Curl_handler_pop3 = { 112 "POP3", /* scheme */ 113 pop3_setup_connection, /* setup_connection */ 114 pop3_do, /* do_it */ 115 pop3_done, /* done */ 116 ZERO_NULL, /* do_more */ 117 pop3_connect, /* connect_it */ 118 pop3_multi_statemach, /* connecting */ 119 pop3_doing, /* doing */ 120 pop3_getsock, /* proto_getsock */ 121 pop3_getsock, /* doing_getsock */ 122 ZERO_NULL, /* domore_getsock */ 123 ZERO_NULL, /* perform_getsock */ 124 pop3_disconnect, /* disconnect */ 125 ZERO_NULL, /* readwrite */ 126 PORT_POP3, /* defport */ 127 CURLPROTO_POP3, /* protocol */ 128 PROTOPT_CLOSEACTION /* flags */ 129}; 130 131 132#ifdef USE_SSL 133/* 134 * POP3S protocol handler. 135 */ 136 137const struct Curl_handler Curl_handler_pop3s = { 138 "POP3S", /* scheme */ 139 pop3_setup_connection, /* setup_connection */ 140 pop3_do, /* do_it */ 141 pop3_done, /* done */ 142 ZERO_NULL, /* do_more */ 143 pop3_connect, /* connect_it */ 144 pop3_multi_statemach, /* connecting */ 145 pop3_doing, /* doing */ 146 pop3_getsock, /* proto_getsock */ 147 pop3_getsock, /* doing_getsock */ 148 ZERO_NULL, /* domore_getsock */ 149 ZERO_NULL, /* perform_getsock */ 150 pop3_disconnect, /* disconnect */ 151 ZERO_NULL, /* readwrite */ 152 PORT_POP3S, /* defport */ 153 CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */ 154 PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */ 155}; 156#endif 157 158#ifndef CURL_DISABLE_HTTP 159/* 160 * HTTP-proxyed POP3 protocol handler. 161 */ 162 163static const struct Curl_handler Curl_handler_pop3_proxy = { 164 "POP3", /* scheme */ 165 ZERO_NULL, /* setup_connection */ 166 Curl_http, /* do_it */ 167 Curl_http_done, /* done */ 168 ZERO_NULL, /* do_more */ 169 ZERO_NULL, /* connect_it */ 170 ZERO_NULL, /* connecting */ 171 ZERO_NULL, /* doing */ 172 ZERO_NULL, /* proto_getsock */ 173 ZERO_NULL, /* doing_getsock */ 174 ZERO_NULL, /* domore_getsock */ 175 ZERO_NULL, /* perform_getsock */ 176 ZERO_NULL, /* disconnect */ 177 ZERO_NULL, /* readwrite */ 178 PORT_POP3, /* defport */ 179 CURLPROTO_HTTP, /* protocol */ 180 PROTOPT_NONE /* flags */ 181}; 182 183 184#ifdef USE_SSL 185/* 186 * HTTP-proxyed POP3S protocol handler. 187 */ 188 189static const struct Curl_handler Curl_handler_pop3s_proxy = { 190 "POP3S", /* scheme */ 191 ZERO_NULL, /* setup_connection */ 192 Curl_http, /* do_it */ 193 Curl_http_done, /* done */ 194 ZERO_NULL, /* do_more */ 195 ZERO_NULL, /* connect_it */ 196 ZERO_NULL, /* connecting */ 197 ZERO_NULL, /* doing */ 198 ZERO_NULL, /* proto_getsock */ 199 ZERO_NULL, /* doing_getsock */ 200 ZERO_NULL, /* domore_getsock */ 201 ZERO_NULL, /* perform_getsock */ 202 ZERO_NULL, /* disconnect */ 203 ZERO_NULL, /* readwrite */ 204 PORT_POP3S, /* defport */ 205 CURLPROTO_HTTP, /* protocol */ 206 PROTOPT_NONE /* flags */ 207}; 208#endif 209#endif 210 211 212/* function that checks for a pop3 status code at the start of the given 213 string */ 214static int pop3_endofresp(struct pingpong *pp, 215 int *resp) 216{ 217 char *line = pp->linestart_resp; 218 size_t len = pp->nread_resp; 219 220 if(((len >= 3) && !memcmp("+OK", line, 3)) || 221 ((len >= 4) && !memcmp("-ERR", line, 4))) { 222 *resp=line[1]; /* O or E */ 223 return TRUE; 224 } 225 226 return FALSE; /* nothing for us */ 227} 228 229/* This is the ONLY way to change POP3 state! */ 230static void state(struct connectdata *conn, 231 pop3state newstate) 232{ 233#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 234 /* for debug purposes */ 235 static const char * const names[]={ 236 "STOP", 237 "SERVERGREET", 238 "USER", 239 "PASS", 240 "STARTTLS", 241 "LIST", 242 "LIST_SINGLE", 243 "RETR", 244 "QUIT", 245 /* LAST */ 246 }; 247#endif 248 struct pop3_conn *pop3c = &conn->proto.pop3c; 249#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 250 if(pop3c->state != newstate) 251 infof(conn->data, "POP3 %p state change from %s to %s\n", 252 pop3c, names[pop3c->state], names[newstate]); 253#endif 254 pop3c->state = newstate; 255} 256 257static CURLcode pop3_state_user(struct connectdata *conn) 258{ 259 CURLcode result; 260 struct FTP *pop3 = conn->data->state.proto.pop3; 261 262 /* send USER */ 263 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s", 264 pop3->user?pop3->user:""); 265 if(result) 266 return result; 267 268 state(conn, POP3_USER); 269 270 return CURLE_OK; 271} 272 273/* For the POP3 "protocol connect" and "doing" phases only */ 274static int pop3_getsock(struct connectdata *conn, 275 curl_socket_t *socks, 276 int numsocks) 277{ 278 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks); 279} 280 281#ifdef USE_SSL 282static void pop3_to_pop3s(struct connectdata *conn) 283{ 284 conn->handler = &Curl_handler_pop3s; 285} 286#else 287#define pop3_to_pop3s(x) Curl_nop_stmt 288#endif 289 290/* for STARTTLS responses */ 291static CURLcode pop3_state_starttls_resp(struct connectdata *conn, 292 int pop3code, 293 pop3state instate) 294{ 295 CURLcode result = CURLE_OK; 296 struct SessionHandle *data = conn->data; 297 (void)instate; /* no use for this yet */ 298 299 if(pop3code != 'O') { 300 failf(data, "STARTTLS denied. %c", pop3code); 301 result = CURLE_LOGIN_DENIED; 302 state(conn, POP3_STOP); 303 } 304 else { 305 /* Curl_ssl_connect is BLOCKING */ 306 result = Curl_ssl_connect(conn, FIRSTSOCKET); 307 if(CURLE_OK == result) { 308 pop3_to_pop3s(conn); 309 result = pop3_state_user(conn); 310 } 311 else { 312 state(conn, POP3_STOP); 313 } 314 } 315 return result; 316} 317 318/* for USER responses */ 319static CURLcode pop3_state_user_resp(struct connectdata *conn, 320 int pop3code, 321 pop3state instate) 322{ 323 CURLcode result = CURLE_OK; 324 struct SessionHandle *data = conn->data; 325 struct FTP *pop3 = data->state.proto.pop3; 326 327 (void)instate; /* no use for this yet */ 328 329 if(pop3code != 'O') { 330 failf(data, "Access denied. %c", pop3code); 331 result = CURLE_LOGIN_DENIED; 332 } 333 else 334 /* send PASS */ 335 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s", 336 pop3->passwd?pop3->passwd:""); 337 if(result) 338 return result; 339 340 state(conn, POP3_PASS); 341 return result; 342} 343 344/* for PASS responses */ 345static CURLcode pop3_state_pass_resp(struct connectdata *conn, 346 int pop3code, 347 pop3state instate) 348{ 349 CURLcode result = CURLE_OK; 350 struct SessionHandle *data = conn->data; 351 (void)instate; /* no use for this yet */ 352 353 if(pop3code != 'O') { 354 failf(data, "Access denied. %c", pop3code); 355 result = CURLE_LOGIN_DENIED; 356 } 357 358 state(conn, POP3_STOP); 359 return result; 360} 361 362/* for the retr response */ 363static CURLcode pop3_state_retr_resp(struct connectdata *conn, 364 int pop3code, 365 pop3state instate) 366{ 367 CURLcode result = CURLE_OK; 368 struct SessionHandle *data = conn->data; 369 struct FTP *pop3 = data->state.proto.pop3; 370 struct pop3_conn *pop3c = &conn->proto.pop3c; 371 struct pingpong *pp = &pop3c->pp; 372 373 (void)instate; /* no use for this yet */ 374 375 if('O' != pop3code) { 376 state(conn, POP3_STOP); 377 return CURLE_RECV_ERROR; 378 } 379 380 /* POP3 download */ 381 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, 382 pop3->bytecountp, -1, NULL); /* no upload here */ 383 384 if(pp->cache) { 385 /* At this point there is a bunch of data in the header "cache" that is 386 actually body content, send it as body and then skip it. Do note 387 that there may even be additional "headers" after the body. */ 388 389 /* we may get the EOB already here! */ 390 result = Curl_pop3_write(conn, pp->cache, pp->cache_size); 391 if(result) 392 return result; 393 394 /* cache is drained */ 395 free(pp->cache); 396 pp->cache = NULL; 397 pp->cache_size = 0; 398 } 399 400 state(conn, POP3_STOP); 401 return result; 402} 403 404 405/* for the list response */ 406static CURLcode pop3_state_list_resp(struct connectdata *conn, 407 int pop3code, 408 pop3state instate) 409{ 410 CURLcode result = CURLE_OK; 411 struct SessionHandle *data = conn->data; 412 struct FTP *pop3 = data->state.proto.pop3; 413 struct pop3_conn *pop3c = &conn->proto.pop3c; 414 struct pingpong *pp = &pop3c->pp; 415 416 (void)instate; /* no use for this yet */ 417 418 if('O' != pop3code) { 419 state(conn, POP3_STOP); 420 return CURLE_RECV_ERROR; 421 } 422 423 /* POP3 download */ 424 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp, 425 -1, NULL); /* no upload here */ 426 427 if(pp->cache) { 428 /* cache holds the email ID listing */ 429 430 /* we may get the EOB already here! */ 431 result = Curl_pop3_write(conn, pp->cache, pp->cache_size); 432 if(result) 433 return result; 434 435 /* cache is drained */ 436 free(pp->cache); 437 pp->cache = NULL; 438 pp->cache_size = 0; 439 } 440 441 state(conn, POP3_STOP); 442 return result; 443} 444 445/* for LIST response with a given message */ 446static CURLcode pop3_state_list_single_resp(struct connectdata *conn, 447 int pop3code, 448 pop3state instate) 449{ 450 CURLcode result = CURLE_OK; 451 struct SessionHandle *data = conn->data; 452 (void)instate; /* no use for this yet */ 453 454 if(pop3code != 'O') { 455 failf(data, "Invalid message. %c", pop3code); 456 result = CURLE_REMOTE_FILE_NOT_FOUND; 457 } 458 459 state(conn, POP3_STOP); 460 return result; 461} 462 463/* start the DO phase for RETR */ 464static CURLcode pop3_retr(struct connectdata *conn) 465{ 466 CURLcode result = CURLE_OK; 467 struct pop3_conn *pop3c = &conn->proto.pop3c; 468 469 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "RETR %s", pop3c->mailbox); 470 if(result) 471 return result; 472 473 state(conn, POP3_RETR); 474 return result; 475} 476 477/* start the DO phase for LIST */ 478static CURLcode pop3_list(struct connectdata *conn) 479{ 480 CURLcode result = CURLE_OK; 481 struct pop3_conn *pop3c = &conn->proto.pop3c; 482 483 if(pop3c->mailbox[0] != '\0') 484 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "LIST %s", pop3c->mailbox); 485 else 486 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "LIST"); 487 if(result) 488 return result; 489 490 if(pop3c->mailbox[0] != '\0') 491 state(conn, POP3_LIST_SINGLE); 492 else 493 state(conn, POP3_LIST); 494 return result; 495} 496 497static CURLcode pop3_statemach_act(struct connectdata *conn) 498{ 499 CURLcode result; 500 curl_socket_t sock = conn->sock[FIRSTSOCKET]; 501 struct SessionHandle *data=conn->data; 502 int pop3code; 503 struct pop3_conn *pop3c = &conn->proto.pop3c; 504 struct pingpong *pp = &pop3c->pp; 505 size_t nread = 0; 506 507 if(pp->sendleft) 508 return Curl_pp_flushsend(pp); 509 510 /* we read a piece of response */ 511 result = Curl_pp_readresp(sock, pp, &pop3code, &nread); 512 if(result) 513 return result; 514 515 if(pop3code) { 516 /* we have now received a full POP3 server response */ 517 switch(pop3c->state) { 518 case POP3_SERVERGREET: 519 if(pop3code != 'O') { 520 failf(data, "Got unexpected pop3-server response"); 521 return CURLE_FTP_WEIRD_SERVER_REPLY; 522 } 523 524 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { 525 /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch 526 to TLS connection now */ 527 result = Curl_pp_sendf(&pop3c->pp, "STLS"); 528 state(conn, POP3_STARTTLS); 529 } 530 else 531 result = pop3_state_user(conn); 532 if(result) 533 return result; 534 break; 535 536 case POP3_USER: 537 result = pop3_state_user_resp(conn, pop3code, pop3c->state); 538 break; 539 540 case POP3_PASS: 541 result = pop3_state_pass_resp(conn, pop3code, pop3c->state); 542 break; 543 544 case POP3_STARTTLS: 545 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state); 546 break; 547 548 case POP3_RETR: 549 result = pop3_state_retr_resp(conn, pop3code, pop3c->state); 550 break; 551 552 case POP3_LIST: 553 result = pop3_state_list_resp(conn, pop3code, pop3c->state); 554 break; 555 556 case POP3_LIST_SINGLE: 557 result = pop3_state_list_single_resp(conn, pop3code, pop3c->state); 558 break; 559 560 case POP3_QUIT: 561 /* fallthrough, just stop! */ 562 default: 563 /* internal error */ 564 state(conn, POP3_STOP); 565 break; 566 } 567 } 568 return result; 569} 570 571/* called repeatedly until done from multi.c */ 572static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done) 573{ 574 struct pop3_conn *pop3c = &conn->proto.pop3c; 575 CURLcode result = Curl_pp_multi_statemach(&pop3c->pp); 576 577 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE; 578 579 return result; 580} 581 582static CURLcode pop3_easy_statemach(struct connectdata *conn) 583{ 584 struct pop3_conn *pop3c = &conn->proto.pop3c; 585 struct pingpong *pp = &pop3c->pp; 586 CURLcode result = CURLE_OK; 587 588 while(pop3c->state != POP3_STOP) { 589 result = Curl_pp_easy_statemach(pp); 590 if(result) 591 break; 592 } 593 594 return result; 595} 596 597/* 598 * Allocate and initialize the struct POP3 for the current SessionHandle. If 599 * need be. 600 */ 601static CURLcode pop3_init(struct connectdata *conn) 602{ 603 struct SessionHandle *data = conn->data; 604 struct FTP *pop3 = data->state.proto.pop3; 605 if(!pop3) { 606 pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1); 607 if(!pop3) 608 return CURLE_OUT_OF_MEMORY; 609 } 610 611 /* get some initial data into the pop3 struct */ 612 pop3->bytecountp = &data->req.bytecount; 613 614 /* No need to duplicate user+password, the connectdata struct won't change 615 during a session, but we re-init them here since on subsequent inits 616 since the conn struct may have changed or been replaced. 617 */ 618 pop3->user = conn->user; 619 pop3->passwd = conn->passwd; 620 621 return CURLE_OK; 622} 623 624/* 625 * pop3_connect() should do everything that is to be considered a part of 626 * the connection phase. 627 * 628 * The variable 'done' points to will be TRUE if the protocol-layer connect 629 * phase is done when this function returns, or FALSE is not. When called as 630 * a part of the easy interface, it will always be TRUE. 631 */ 632static CURLcode pop3_connect(struct connectdata *conn, 633 bool *done) /* see description above */ 634{ 635 CURLcode result; 636 struct pop3_conn *pop3c = &conn->proto.pop3c; 637 struct SessionHandle *data=conn->data; 638 struct pingpong *pp = &pop3c->pp; 639 640 *done = FALSE; /* default to not done yet */ 641 642 /* If there already is a protocol-specific struct allocated for this 643 sessionhandle, deal with it */ 644 Curl_reset_reqproto(conn); 645 646 result = pop3_init(conn); 647 if(CURLE_OK != result) 648 return result; 649 650 /* We always support persistent connections on pop3 */ 651 conn->bits.close = FALSE; 652 653 pp->response_time = RESP_TIMEOUT; /* set default response time-out */ 654 pp->statemach_act = pop3_statemach_act; 655 pp->endofresp = pop3_endofresp; 656 pp->conn = conn; 657 658 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { 659 /* for POP3 over HTTP proxy */ 660 struct HTTP http_proxy; 661 struct FTP *pop3_save; 662 663 /* BLOCKING */ 664 /* We want "seamless" POP3 operations through HTTP proxy tunnel */ 665 666 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member 667 * conn->proto.http; we want POP3 through HTTP and we have to change the 668 * member temporarily for connecting to the HTTP proxy. After 669 * Curl_proxyCONNECT we have to set back the member to the original struct 670 * POP3 pointer 671 */ 672 pop3_save = data->state.proto.pop3; 673 memset(&http_proxy, 0, sizeof(http_proxy)); 674 data->state.proto.http = &http_proxy; 675 676 result = Curl_proxyCONNECT(conn, FIRSTSOCKET, 677 conn->host.name, conn->remote_port); 678 679 data->state.proto.pop3 = pop3_save; 680 681 if(CURLE_OK != result) 682 return result; 683 } 684 685 if(conn->handler->flags & PROTOPT_SSL) { 686 /* BLOCKING */ 687 result = Curl_ssl_connect(conn, FIRSTSOCKET); 688 if(result) 689 return result; 690 } 691 692 Curl_pp_init(pp); /* init the response reader stuff */ 693 694 /* When we connect, we start in the state where we await the server greet 695 response */ 696 state(conn, POP3_SERVERGREET); 697 698 if(data->state.used_interface == Curl_if_multi) 699 result = pop3_multi_statemach(conn, done); 700 else { 701 result = pop3_easy_statemach(conn); 702 if(!result) 703 *done = TRUE; 704 } 705 706 return result; 707} 708 709/*********************************************************************** 710 * 711 * pop3_done() 712 * 713 * The DONE function. This does what needs to be done after a single DO has 714 * performed. 715 * 716 * Input argument is already checked for validity. 717 */ 718static CURLcode pop3_done(struct connectdata *conn, CURLcode status, 719 bool premature) 720{ 721 struct SessionHandle *data = conn->data; 722 struct FTP *pop3 = data->state.proto.pop3; 723 struct pop3_conn *pop3c = &conn->proto.pop3c; 724 CURLcode result=CURLE_OK; 725 (void)premature; 726 727 if(!pop3) 728 /* When the easy handle is removed from the multi while libcurl is still 729 * trying to resolve the host name, it seems that the pop3 struct is not 730 * yet initialized, but the removal action calls Curl_done() which calls 731 * this function. So we simply return success if no pop3 pointer is set. 732 */ 733 return CURLE_OK; 734 735 if(status) { 736 conn->bits.close = TRUE; /* marked for closure */ 737 result = status; /* use the already set error code */ 738 } 739 740 Curl_safefree(pop3c->mailbox); 741 pop3c->mailbox = NULL; 742 743 /* clear these for next connection */ 744 pop3->transfer = FTPTRANSFER_BODY; 745 746 return result; 747} 748 749/*********************************************************************** 750 * 751 * pop3_perform() 752 * 753 * This is the actual DO function for POP3. Get a file/directory according to 754 * the options previously setup. 755 */ 756 757static 758CURLcode pop3_perform(struct connectdata *conn, 759 bool *connected, /* connect status after PASV / PORT */ 760 bool *dophase_done) 761{ 762 /* this is POP3 and no proxy */ 763 CURLcode result=CURLE_OK; 764 struct pop3_conn *pop3c = &conn->proto.pop3c; 765 766 DEBUGF(infof(conn->data, "DO phase starts\n")); 767 768 if(conn->data->set.opt_no_body) { 769 /* requested no body means no transfer... */ 770 struct FTP *pop3 = conn->data->state.proto.pop3; 771 pop3->transfer = FTPTRANSFER_INFO; 772 } 773 774 *dophase_done = FALSE; /* not done yet */ 775 776 /* start the first command in the DO phase */ 777 /* If mailbox is empty, then assume user wants listing for mail IDs, 778 * otherwise, attempt to retrieve the mail-id stored in mailbox 779 */ 780 if(strlen(pop3c->mailbox) && !conn->data->set.ftp_list_only) 781 result = pop3_retr(conn); 782 else 783 result = pop3_list(conn); 784 if(result) 785 return result; 786 787 /* run the state-machine */ 788 if(conn->data->state.used_interface == Curl_if_multi) 789 result = pop3_multi_statemach(conn, dophase_done); 790 else { 791 result = pop3_easy_statemach(conn); 792 *dophase_done = TRUE; /* with the easy interface we are done here */ 793 } 794 *connected = conn->bits.tcpconnect[FIRSTSOCKET]; 795 796 if(*dophase_done) 797 DEBUGF(infof(conn->data, "DO phase is complete\n")); 798 799 return result; 800} 801 802/*********************************************************************** 803 * 804 * pop3_do() 805 * 806 * This function is registered as 'curl_do' function. It decodes the path 807 * parts etc as a wrapper to the actual DO function (pop3_perform). 808 * 809 * The input argument is already checked for validity. 810 */ 811static CURLcode pop3_do(struct connectdata *conn, bool *done) 812{ 813 CURLcode retcode = CURLE_OK; 814 815 *done = FALSE; /* default to false */ 816 817 /* 818 Since connections can be re-used between SessionHandles, this might be a 819 connection already existing but on a fresh SessionHandle struct so we must 820 make sure we have a good 'struct POP3' to play with. For new connections, 821 the struct POP3 is allocated and setup in the pop3_connect() function. 822 */ 823 Curl_reset_reqproto(conn); 824 retcode = pop3_init(conn); 825 if(retcode) 826 return retcode; 827 828 retcode = pop3_parse_url_path(conn); 829 if(retcode) 830 return retcode; 831 832 retcode = pop3_regular_transfer(conn, done); 833 834 return retcode; 835} 836 837/*********************************************************************** 838 * 839 * pop3_quit() 840 * 841 * This should be called before calling sclose(). We should then wait for the 842 * response from the server before returning. The calling code should then try 843 * to close the connection. 844 * 845 */ 846static CURLcode pop3_quit(struct connectdata *conn) 847{ 848 CURLcode result = CURLE_OK; 849 850 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL); 851 if(result) 852 return result; 853 state(conn, POP3_QUIT); 854 855 result = pop3_easy_statemach(conn); 856 857 return result; 858} 859 860/*********************************************************************** 861 * 862 * pop3_disconnect() 863 * 864 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection 865 * resources. BLOCKING. 866 */ 867static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection) 868{ 869 struct pop3_conn *pop3c= &conn->proto.pop3c; 870 871 /* We cannot send quit unconditionally. If this connection is stale or 872 bad in any way, sending quit and waiting around here will make the 873 disconnect wait in vain and cause more problems than we need to. 874 */ 875 876 /* The POP3 session may or may not have been allocated/setup at this 877 point! */ 878 if(!dead_connection && pop3c->pp.conn) 879 (void)pop3_quit(conn); /* ignore errors on the LOGOUT */ 880 881 882 Curl_pp_disconnect(&pop3c->pp); 883 884 return CURLE_OK; 885} 886 887/*********************************************************************** 888 * 889 * pop3_parse_url_path() 890 * 891 * Parse the URL path into separate path components. 892 * 893 */ 894static CURLcode pop3_parse_url_path(struct connectdata *conn) 895{ 896 /* the pop3 struct is already inited in pop3_connect() */ 897 struct pop3_conn *pop3c = &conn->proto.pop3c; 898 struct SessionHandle *data = conn->data; 899 const char *path = data->state.path; 900 901 /* url decode the path and use this mailbox */ 902 pop3c->mailbox = curl_easy_unescape(data, path, 0, NULL); 903 if(!pop3c->mailbox) 904 return CURLE_OUT_OF_MEMORY; 905 906 return CURLE_OK; 907} 908 909/* call this when the DO phase has completed */ 910static CURLcode pop3_dophase_done(struct connectdata *conn, 911 bool connected) 912{ 913 struct FTP *pop3 = conn->data->state.proto.pop3; 914 (void)connected; 915 916 if(pop3->transfer != FTPTRANSFER_BODY) 917 /* no data to transfer */ 918 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); 919 920 return CURLE_OK; 921} 922 923/* called from multi.c while DOing */ 924static CURLcode pop3_doing(struct connectdata *conn, 925 bool *dophase_done) 926{ 927 CURLcode result; 928 result = pop3_multi_statemach(conn, dophase_done); 929 930 if(*dophase_done) { 931 result = pop3_dophase_done(conn, FALSE /* not connected */); 932 933 DEBUGF(infof(conn->data, "DO phase is complete\n")); 934 } 935 return result; 936} 937 938/*********************************************************************** 939 * 940 * pop3_regular_transfer() 941 * 942 * The input argument is already checked for validity. 943 * 944 * Performs all commands done before a regular transfer between a local and a 945 * remote host. 946 * 947 */ 948static 949CURLcode pop3_regular_transfer(struct connectdata *conn, 950 bool *dophase_done) 951{ 952 CURLcode result=CURLE_OK; 953 bool connected=FALSE; 954 struct SessionHandle *data = conn->data; 955 data->req.size = -1; /* make sure this is unknown at this point */ 956 957 Curl_pgrsSetUploadCounter(data, 0); 958 Curl_pgrsSetDownloadCounter(data, 0); 959 Curl_pgrsSetUploadSize(data, 0); 960 Curl_pgrsSetDownloadSize(data, 0); 961 962 result = pop3_perform(conn, 963 &connected, /* have we connected after PASV/PORT */ 964 dophase_done); /* all commands in the DO-phase done? */ 965 966 if(CURLE_OK == result) { 967 968 if(!*dophase_done) 969 /* the DO phase has not completed yet */ 970 return CURLE_OK; 971 972 result = pop3_dophase_done(conn, connected); 973 if(result) 974 return result; 975 } 976 977 return result; 978} 979 980static CURLcode pop3_setup_connection(struct connectdata * conn) 981{ 982 struct SessionHandle *data = conn->data; 983 984 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { 985 /* Unless we have asked to tunnel pop3 operations through the proxy, we 986 switch and use HTTP operations only */ 987#ifndef CURL_DISABLE_HTTP 988 if(conn->handler == &Curl_handler_pop3) 989 conn->handler = &Curl_handler_pop3_proxy; 990 else { 991#ifdef USE_SSL 992 conn->handler = &Curl_handler_pop3s_proxy; 993#else 994 failf(data, "POP3S not supported!"); 995 return CURLE_UNSUPPORTED_PROTOCOL; 996#endif 997 } 998 /* 999 * We explicitly mark this connection as persistent here as we're doing 1000 * POP3 over HTTP and thus we accidentally avoid setting this value 1001 * otherwise. 1002 */ 1003 conn->bits.close = FALSE; 1004#else 1005 failf(data, "POP3 over http proxy requires HTTP support built-in!"); 1006 return CURLE_UNSUPPORTED_PROTOCOL; 1007#endif 1008 } 1009 1010 data->state.path++; /* don't include the initial slash */ 1011 1012 return CURLE_OK; 1013} 1014 1015/* this is the 5-bytes End-Of-Body marker for POP3 */ 1016#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" 1017#define POP3_EOB_LEN 5 1018 1019/* 1020 * This function scans the body after the end-of-body and writes everything 1021 * until the end is found. 1022 */ 1023CURLcode Curl_pop3_write(struct connectdata *conn, 1024 char *str, 1025 size_t nread) 1026{ 1027 /* This code could be made into a special function in the handler struct. */ 1028 CURLcode result; 1029 struct SessionHandle *data = conn->data; 1030 struct SingleRequest *k = &data->req; 1031 1032 /* Detect the end-of-body marker, which is 5 bytes: 1033 0d 0a 2e 0d 0a. This marker can of course be spread out 1034 over up to 5 different data chunks. Deal with it! */ 1035 struct pop3_conn *pop3c = &conn->proto.pop3c; 1036 size_t checkmax = (nread >= POP3_EOB_LEN?POP3_EOB_LEN:nread); 1037 size_t checkleft = POP3_EOB_LEN-pop3c->eob; 1038 size_t check = (checkmax >= checkleft?checkleft:checkmax); 1039 1040 if(!memcmp(POP3_EOB, &str[nread - check], check)) { 1041 /* substring match */ 1042 pop3c->eob += check; 1043 if(pop3c->eob == POP3_EOB_LEN) { 1044 /* full match, the transfer is done! */ 1045 str[nread - check] = '\0'; 1046 nread -= check; 1047 k->keepon &= ~KEEP_RECV; 1048 pop3c->eob = 0; 1049 } 1050 } 1051 else if(pop3c->eob) { 1052 /* not a match, but we matched a piece before so we must now 1053 send that part as body first, before we move on and send 1054 this buffer */ 1055 result = Curl_client_write(conn, CLIENTWRITE_BODY, 1056 (char *)POP3_EOB, pop3c->eob); 1057 if(result) 1058 return result; 1059 pop3c->eob = 0; 1060 } 1061 1062 result = Curl_client_write(conn, CLIENTWRITE_BODY, str, nread); 1063 1064 return result; 1065} 1066 1067#endif /* CURL_DISABLE_POP3 */ 1068