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