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