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