1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel (at) 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 * RFC2195 CRAM-MD5 authentication 22 * RFC2595 Using TLS with IMAP, POP3 and ACAP 23 * RFC2831 DIGEST-MD5 authentication 24 * RFC3501 IMAPv4 protocol 25 * RFC4422 Simple Authentication and Security Layer (SASL) 26 * RFC4616 PLAIN authentication 27 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism 28 * RFC4959 IMAP Extension for SASL Initial Client Response 29 * RFC5092 IMAP URL Scheme 30 * RFC6749 OAuth 2.0 Authorization Framework 31 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> 32 * 33 ***************************************************************************/ 34 35 #include "curl_setup.h" 36 37 #ifndef CURL_DISABLE_IMAP 38 39 #ifdef HAVE_NETINET_IN_H 40 #include <netinet/in.h> 41 #endif 42 #ifdef HAVE_ARPA_INET_H 43 #include <arpa/inet.h> 44 #endif 45 #ifdef HAVE_UTSNAME_H 46 #include <sys/utsname.h> 47 #endif 48 #ifdef HAVE_NETDB_H 49 #include <netdb.h> 50 #endif 51 #ifdef __VMS 52 #include <in.h> 53 #include <inet.h> 54 #endif 55 56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__)) 57 #undef in_addr_t 58 #define in_addr_t unsigned long 59 #endif 60 61 #include <curl/curl.h> 62 #include "urldata.h" 63 #include "sendf.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 "imap.h" 71 72 #include "strtoofft.h" 73 #include "strequal.h" 74 #include "vtls/vtls.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 "curl_sasl.h" 82 #include "warnless.h" 83 #include "curl_printf.h" 84 85 #include "curl_memory.h" 86 /* The last #include file should be: */ 87 #include "memdebug.h" 88 89 /* Local API functions */ 90 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done); 91 static CURLcode imap_do(struct connectdata *conn, bool *done); 92 static CURLcode imap_done(struct connectdata *conn, CURLcode status, 93 bool premature); 94 static CURLcode imap_connect(struct connectdata *conn, bool *done); 95 static CURLcode imap_disconnect(struct connectdata *conn, bool dead); 96 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done); 97 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks, 98 int numsocks); 99 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done); 100 static CURLcode imap_setup_connection(struct connectdata *conn); 101 static char *imap_atom(const char *str); 102 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...); 103 static CURLcode imap_parse_url_options(struct connectdata *conn); 104 static CURLcode imap_parse_url_path(struct connectdata *conn); 105 static CURLcode imap_parse_custom_request(struct connectdata *conn); 106 static CURLcode imap_perform_authenticate(struct connectdata *conn, 107 const char *mech, 108 const char *initresp); 109 static CURLcode imap_continue_authenticate(struct connectdata *conn, 110 const char *resp); 111 static void imap_get_message(char *buffer, char** outptr); 112 113 /* 114 * IMAP protocol handler. 115 */ 116 117 const struct Curl_handler Curl_handler_imap = { 118 "IMAP", /* scheme */ 119 imap_setup_connection, /* setup_connection */ 120 imap_do, /* do_it */ 121 imap_done, /* done */ 122 ZERO_NULL, /* do_more */ 123 imap_connect, /* connect_it */ 124 imap_multi_statemach, /* connecting */ 125 imap_doing, /* doing */ 126 imap_getsock, /* proto_getsock */ 127 imap_getsock, /* doing_getsock */ 128 ZERO_NULL, /* domore_getsock */ 129 ZERO_NULL, /* perform_getsock */ 130 imap_disconnect, /* disconnect */ 131 ZERO_NULL, /* readwrite */ 132 PORT_IMAP, /* defport */ 133 CURLPROTO_IMAP, /* protocol */ 134 PROTOPT_CLOSEACTION /* flags */ 135 }; 136 137 #ifdef USE_SSL 138 /* 139 * IMAPS protocol handler. 140 */ 141 142 const struct Curl_handler Curl_handler_imaps = { 143 "IMAPS", /* scheme */ 144 imap_setup_connection, /* setup_connection */ 145 imap_do, /* do_it */ 146 imap_done, /* done */ 147 ZERO_NULL, /* do_more */ 148 imap_connect, /* connect_it */ 149 imap_multi_statemach, /* connecting */ 150 imap_doing, /* doing */ 151 imap_getsock, /* proto_getsock */ 152 imap_getsock, /* doing_getsock */ 153 ZERO_NULL, /* domore_getsock */ 154 ZERO_NULL, /* perform_getsock */ 155 imap_disconnect, /* disconnect */ 156 ZERO_NULL, /* readwrite */ 157 PORT_IMAPS, /* defport */ 158 CURLPROTO_IMAPS, /* protocol */ 159 PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */ 160 }; 161 #endif 162 163 #ifndef CURL_DISABLE_HTTP 164 /* 165 * HTTP-proxyed IMAP protocol handler. 166 */ 167 168 static const struct Curl_handler Curl_handler_imap_proxy = { 169 "IMAP", /* scheme */ 170 Curl_http_setup_conn, /* setup_connection */ 171 Curl_http, /* do_it */ 172 Curl_http_done, /* done */ 173 ZERO_NULL, /* do_more */ 174 ZERO_NULL, /* connect_it */ 175 ZERO_NULL, /* connecting */ 176 ZERO_NULL, /* doing */ 177 ZERO_NULL, /* proto_getsock */ 178 ZERO_NULL, /* doing_getsock */ 179 ZERO_NULL, /* domore_getsock */ 180 ZERO_NULL, /* perform_getsock */ 181 ZERO_NULL, /* disconnect */ 182 ZERO_NULL, /* readwrite */ 183 PORT_IMAP, /* defport */ 184 CURLPROTO_HTTP, /* protocol */ 185 PROTOPT_NONE /* flags */ 186 }; 187 188 #ifdef USE_SSL 189 /* 190 * HTTP-proxyed IMAPS protocol handler. 191 */ 192 193 static const struct Curl_handler Curl_handler_imaps_proxy = { 194 "IMAPS", /* scheme */ 195 Curl_http_setup_conn, /* setup_connection */ 196 Curl_http, /* do_it */ 197 Curl_http_done, /* done */ 198 ZERO_NULL, /* do_more */ 199 ZERO_NULL, /* connect_it */ 200 ZERO_NULL, /* connecting */ 201 ZERO_NULL, /* doing */ 202 ZERO_NULL, /* proto_getsock */ 203 ZERO_NULL, /* doing_getsock */ 204 ZERO_NULL, /* domore_getsock */ 205 ZERO_NULL, /* perform_getsock */ 206 ZERO_NULL, /* disconnect */ 207 ZERO_NULL, /* readwrite */ 208 PORT_IMAPS, /* defport */ 209 CURLPROTO_HTTP, /* protocol */ 210 PROTOPT_NONE /* flags */ 211 }; 212 #endif 213 #endif 214 215 /* SASL parameters for the imap protocol */ 216 static const struct SASLproto saslimap = { 217 "imap", /* The service name */ 218 '+', /* Code received when continuation is expected */ 219 'O', /* Code to receive upon authentication success */ 220 0, /* Maximum initial response length (no max) */ 221 imap_perform_authenticate, /* Send authentication command */ 222 imap_continue_authenticate, /* Send authentication continuation */ 223 imap_get_message /* Get SASL response message */ 224 }; 225 226 227 #ifdef USE_SSL 228 static void imap_to_imaps(struct connectdata *conn) 229 { 230 conn->handler = &Curl_handler_imaps; 231 } 232 #else 233 #define imap_to_imaps(x) Curl_nop_stmt 234 #endif 235 236 /*********************************************************************** 237 * 238 * imap_matchresp() 239 * 240 * Determines whether the untagged response is related to the specified 241 * command by checking if it is in format "* <command-name> ..." or 242 * "* <number> <command-name> ...". 243 * 244 * The "* " marker is assumed to have already been checked by the caller. 245 */ 246 static bool imap_matchresp(const char *line, size_t len, const char *cmd) 247 { 248 const char *end = line + len; 249 size_t cmd_len = strlen(cmd); 250 251 /* Skip the untagged response marker */ 252 line += 2; 253 254 /* Do we have a number after the marker? */ 255 if(line < end && ISDIGIT(*line)) { 256 /* Skip the number */ 257 do 258 line++; 259 while(line < end && ISDIGIT(*line)); 260 261 /* Do we have the space character? */ 262 if(line == end || *line != ' ') 263 return FALSE; 264 265 line++; 266 } 267 268 /* Does the command name match and is it followed by a space character or at 269 the end of line? */ 270 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) && 271 (line[cmd_len] == ' ' || line + cmd_len + 2 == end)) 272 return TRUE; 273 274 return FALSE; 275 } 276 277 /*********************************************************************** 278 * 279 * imap_endofresp() 280 * 281 * Checks whether the given string is a valid tagged, untagged or continuation 282 * response which can be processed by the response handler. 283 */ 284 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len, 285 int *resp) 286 { 287 struct IMAP *imap = conn->data->req.protop; 288 struct imap_conn *imapc = &conn->proto.imapc; 289 const char *id = imapc->resptag; 290 size_t id_len = strlen(id); 291 292 /* Do we have a tagged command response? */ 293 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') { 294 line += id_len + 1; 295 len -= id_len + 1; 296 297 if(len >= 2 && !memcmp(line, "OK", 2)) 298 *resp = 'O'; 299 else if(len >= 2 && !memcmp(line, "NO", 2)) 300 *resp = 'N'; 301 else if(len >= 3 && !memcmp(line, "BAD", 3)) 302 *resp = 'B'; 303 else { 304 failf(conn->data, "Bad tagged response"); 305 *resp = -1; 306 } 307 308 return TRUE; 309 } 310 311 /* Do we have an untagged command response? */ 312 if(len >= 2 && !memcmp("* ", line, 2)) { 313 switch(imapc->state) { 314 /* States which are interested in untagged responses */ 315 case IMAP_CAPABILITY: 316 if(!imap_matchresp(line, len, "CAPABILITY")) 317 return FALSE; 318 break; 319 320 case IMAP_LIST: 321 if((!imap->custom && !imap_matchresp(line, len, "LIST")) || 322 (imap->custom && !imap_matchresp(line, len, imap->custom) && 323 (strcmp(imap->custom, "STORE") || 324 !imap_matchresp(line, len, "FETCH")) && 325 strcmp(imap->custom, "SELECT") && 326 strcmp(imap->custom, "EXAMINE") && 327 strcmp(imap->custom, "SEARCH") && 328 strcmp(imap->custom, "EXPUNGE") && 329 strcmp(imap->custom, "LSUB") && 330 strcmp(imap->custom, "UID") && 331 strcmp(imap->custom, "NOOP"))) 332 return FALSE; 333 break; 334 335 case IMAP_SELECT: 336 /* SELECT is special in that its untagged responses do not have a 337 common prefix so accept anything! */ 338 break; 339 340 case IMAP_FETCH: 341 if(!imap_matchresp(line, len, "FETCH")) 342 return FALSE; 343 break; 344 345 case IMAP_SEARCH: 346 if(!imap_matchresp(line, len, "SEARCH")) 347 return FALSE; 348 break; 349 350 /* Ignore other untagged responses */ 351 default: 352 return FALSE; 353 } 354 355 *resp = '*'; 356 return TRUE; 357 } 358 359 /* Do we have a continuation response? This should be a + symbol followed by 360 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and 361 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but 362 some e-mail servers ignore this and only send a single + instead. */ 363 if((len == 3 && !memcmp("+", line, 1)) || 364 (len >= 2 && !memcmp("+ ", line, 2))) { 365 switch(imapc->state) { 366 /* States which are interested in continuation responses */ 367 case IMAP_AUTHENTICATE: 368 case IMAP_APPEND: 369 *resp = '+'; 370 break; 371 372 default: 373 failf(conn->data, "Unexpected continuation response"); 374 *resp = -1; 375 break; 376 } 377 378 return TRUE; 379 } 380 381 return FALSE; /* Nothing for us */ 382 } 383 384 /*********************************************************************** 385 * 386 * imap_get_message() 387 * 388 * Gets the authentication message from the response buffer. 389 */ 390 static void imap_get_message(char *buffer, char** outptr) 391 { 392 size_t len = 0; 393 char* message = NULL; 394 395 /* Find the start of the message */ 396 for(message = buffer + 2; *message == ' ' || *message == '\t'; message++) 397 ; 398 399 /* Find the end of the message */ 400 for(len = strlen(message); len--;) 401 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && 402 message[len] != '\t') 403 break; 404 405 /* Terminate the message */ 406 if(++len) { 407 message[len] = '\0'; 408 } 409 410 *outptr = message; 411 } 412 413 /*********************************************************************** 414 * 415 * state() 416 * 417 * This is the ONLY way to change IMAP state! 418 */ 419 static void state(struct connectdata *conn, imapstate newstate) 420 { 421 struct imap_conn *imapc = &conn->proto.imapc; 422 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 423 /* for debug purposes */ 424 static const char * const names[]={ 425 "STOP", 426 "SERVERGREET", 427 "CAPABILITY", 428 "STARTTLS", 429 "UPGRADETLS", 430 "AUTHENTICATE", 431 "LOGIN", 432 "LIST", 433 "SELECT", 434 "FETCH", 435 "FETCH_FINAL", 436 "APPEND", 437 "APPEND_FINAL", 438 "SEARCH", 439 "LOGOUT", 440 /* LAST */ 441 }; 442 443 if(imapc->state != newstate) 444 infof(conn->data, "IMAP %p state change from %s to %s\n", 445 (void *)imapc, names[imapc->state], names[newstate]); 446 #endif 447 448 imapc->state = newstate; 449 } 450 451 /*********************************************************************** 452 * 453 * imap_perform_capability() 454 * 455 * Sends the CAPABILITY command in order to obtain a list of server side 456 * supported capabilities. 457 */ 458 static CURLcode imap_perform_capability(struct connectdata *conn) 459 { 460 CURLcode result = CURLE_OK; 461 struct imap_conn *imapc = &conn->proto.imapc; 462 463 imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */ 464 imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */ 465 imapc->tls_supported = FALSE; /* Clear the TLS capability */ 466 467 /* Send the CAPABILITY command */ 468 result = imap_sendf(conn, "CAPABILITY"); 469 470 if(!result) 471 state(conn, IMAP_CAPABILITY); 472 473 return result; 474 } 475 476 /*********************************************************************** 477 * 478 * imap_perform_starttls() 479 * 480 * Sends the STARTTLS command to start the upgrade to TLS. 481 */ 482 static CURLcode imap_perform_starttls(struct connectdata *conn) 483 { 484 CURLcode result = CURLE_OK; 485 486 /* Send the STARTTLS command */ 487 result = imap_sendf(conn, "STARTTLS"); 488 489 if(!result) 490 state(conn, IMAP_STARTTLS); 491 492 return result; 493 } 494 495 /*********************************************************************** 496 * 497 * imap_perform_upgrade_tls() 498 * 499 * Performs the upgrade to TLS. 500 */ 501 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn) 502 { 503 CURLcode result = CURLE_OK; 504 struct imap_conn *imapc = &conn->proto.imapc; 505 506 /* Start the SSL connection */ 507 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone); 508 509 if(!result) { 510 if(imapc->state != IMAP_UPGRADETLS) 511 state(conn, IMAP_UPGRADETLS); 512 513 if(imapc->ssldone) { 514 imap_to_imaps(conn); 515 result = imap_perform_capability(conn); 516 } 517 } 518 519 return result; 520 } 521 522 /*********************************************************************** 523 * 524 * imap_perform_login() 525 * 526 * Sends a clear text LOGIN command to authenticate with. 527 */ 528 static CURLcode imap_perform_login(struct connectdata *conn) 529 { 530 CURLcode result = CURLE_OK; 531 char *user; 532 char *passwd; 533 534 /* Check we have a username and password to authenticate with and end the 535 connect phase if we don't */ 536 if(!conn->bits.user_passwd) { 537 state(conn, IMAP_STOP); 538 539 return result; 540 } 541 542 /* Make sure the username and password are in the correct atom format */ 543 user = imap_atom(conn->user); 544 passwd = imap_atom(conn->passwd); 545 546 /* Send the LOGIN command */ 547 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "", 548 passwd ? passwd : ""); 549 550 free(user); 551 free(passwd); 552 553 if(!result) 554 state(conn, IMAP_LOGIN); 555 556 return result; 557 } 558 559 /*********************************************************************** 560 * 561 * imap_perform_authenticate() 562 * 563 * Sends an AUTHENTICATE command allowing the client to login with the given 564 * SASL authentication mechanism. 565 */ 566 static CURLcode imap_perform_authenticate(struct connectdata *conn, 567 const char *mech, 568 const char *initresp) 569 { 570 CURLcode result = CURLE_OK; 571 572 if(initresp) { 573 /* Send the AUTHENTICATE command with the initial response */ 574 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp); 575 } 576 else { 577 /* Send the AUTHENTICATE command */ 578 result = imap_sendf(conn, "AUTHENTICATE %s", mech); 579 } 580 581 return result; 582 } 583 584 /*********************************************************************** 585 * 586 * imap_continue_authenticate() 587 * 588 * Sends SASL continuation data or cancellation. 589 */ 590 static CURLcode imap_continue_authenticate(struct connectdata *conn, 591 const char *resp) 592 { 593 struct imap_conn *imapc = &conn->proto.imapc; 594 595 return Curl_pp_sendf(&imapc->pp, "%s", resp); 596 } 597 598 /*********************************************************************** 599 * 600 * imap_perform_authentication() 601 * 602 * Initiates the authentication sequence, with the appropriate SASL 603 * authentication mechanism, falling back to clear text should a common 604 * mechanism not be available between the client and server. 605 */ 606 static CURLcode imap_perform_authentication(struct connectdata *conn) 607 { 608 CURLcode result = CURLE_OK; 609 struct imap_conn *imapc = &conn->proto.imapc; 610 saslprogress progress; 611 612 /* Check we have enough data to authenticate with and end the 613 connect phase if we don't */ 614 if(!Curl_sasl_can_authenticate(&imapc->sasl, conn)) { 615 state(conn, IMAP_STOP); 616 return result; 617 } 618 619 /* Calculate the SASL login details */ 620 result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress); 621 622 if(!result) { 623 if(progress == SASL_INPROGRESS) 624 state(conn, IMAP_AUTHENTICATE); 625 else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) 626 /* Perform clear text authentication */ 627 result = imap_perform_login(conn); 628 else { 629 /* Other mechanisms not supported */ 630 infof(conn->data, "No known authentication mechanisms supported!\n"); 631 result = CURLE_LOGIN_DENIED; 632 } 633 } 634 635 return result; 636 } 637 638 /*********************************************************************** 639 * 640 * imap_perform_list() 641 * 642 * Sends a LIST command or an alternative custom request. 643 */ 644 static CURLcode imap_perform_list(struct connectdata *conn) 645 { 646 CURLcode result = CURLE_OK; 647 struct SessionHandle *data = conn->data; 648 struct IMAP *imap = data->req.protop; 649 char *mailbox; 650 651 if(imap->custom) 652 /* Send the custom request */ 653 result = imap_sendf(conn, "%s%s", imap->custom, 654 imap->custom_params ? imap->custom_params : ""); 655 else { 656 /* Make sure the mailbox is in the correct atom format */ 657 mailbox = imap_atom(imap->mailbox ? imap->mailbox : ""); 658 if(!mailbox) 659 return CURLE_OUT_OF_MEMORY; 660 661 /* Send the LIST command */ 662 result = imap_sendf(conn, "LIST \"%s\" *", mailbox); 663 664 free(mailbox); 665 } 666 667 if(!result) 668 state(conn, IMAP_LIST); 669 670 return result; 671 } 672 673 /*********************************************************************** 674 * 675 * imap_perform_select() 676 * 677 * Sends a SELECT command to ask the server to change the selected mailbox. 678 */ 679 static CURLcode imap_perform_select(struct connectdata *conn) 680 { 681 CURLcode result = CURLE_OK; 682 struct SessionHandle *data = conn->data; 683 struct IMAP *imap = data->req.protop; 684 struct imap_conn *imapc = &conn->proto.imapc; 685 char *mailbox; 686 687 /* Invalidate old information as we are switching mailboxes */ 688 Curl_safefree(imapc->mailbox); 689 Curl_safefree(imapc->mailbox_uidvalidity); 690 691 /* Check we have a mailbox */ 692 if(!imap->mailbox) { 693 failf(conn->data, "Cannot SELECT without a mailbox."); 694 return CURLE_URL_MALFORMAT; 695 } 696 697 /* Make sure the mailbox is in the correct atom format */ 698 mailbox = imap_atom(imap->mailbox); 699 if(!mailbox) 700 return CURLE_OUT_OF_MEMORY; 701 702 /* Send the SELECT command */ 703 result = imap_sendf(conn, "SELECT %s", mailbox); 704 705 free(mailbox); 706 707 if(!result) 708 state(conn, IMAP_SELECT); 709 710 return result; 711 } 712 713 /*********************************************************************** 714 * 715 * imap_perform_fetch() 716 * 717 * Sends a FETCH command to initiate the download of a message. 718 */ 719 static CURLcode imap_perform_fetch(struct connectdata *conn) 720 { 721 CURLcode result = CURLE_OK; 722 struct IMAP *imap = conn->data->req.protop; 723 724 /* Check we have a UID */ 725 if(!imap->uid) { 726 failf(conn->data, "Cannot FETCH without a UID."); 727 return CURLE_URL_MALFORMAT; 728 } 729 730 /* Send the FETCH command */ 731 if(imap->partial) 732 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>", 733 imap->uid, 734 imap->section ? imap->section : "", 735 imap->partial); 736 else 737 result = imap_sendf(conn, "FETCH %s BODY[%s]", 738 imap->uid, 739 imap->section ? imap->section : ""); 740 741 if(!result) 742 state(conn, IMAP_FETCH); 743 744 return result; 745 } 746 747 /*********************************************************************** 748 * 749 * imap_perform_append() 750 * 751 * Sends an APPEND command to initiate the upload of a message. 752 */ 753 static CURLcode imap_perform_append(struct connectdata *conn) 754 { 755 CURLcode result = CURLE_OK; 756 struct IMAP *imap = conn->data->req.protop; 757 char *mailbox; 758 759 /* Check we have a mailbox */ 760 if(!imap->mailbox) { 761 failf(conn->data, "Cannot APPEND without a mailbox."); 762 return CURLE_URL_MALFORMAT; 763 } 764 765 /* Check we know the size of the upload */ 766 if(conn->data->state.infilesize < 0) { 767 failf(conn->data, "Cannot APPEND with unknown input file size\n"); 768 return CURLE_UPLOAD_FAILED; 769 } 770 771 /* Make sure the mailbox is in the correct atom format */ 772 mailbox = imap_atom(imap->mailbox); 773 if(!mailbox) 774 return CURLE_OUT_OF_MEMORY; 775 776 /* Send the APPEND command */ 777 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}", 778 mailbox, conn->data->state.infilesize); 779 780 free(mailbox); 781 782 if(!result) 783 state(conn, IMAP_APPEND); 784 785 return result; 786 } 787 788 /*********************************************************************** 789 * 790 * imap_perform_search() 791 * 792 * Sends a SEARCH command. 793 */ 794 static CURLcode imap_perform_search(struct connectdata *conn) 795 { 796 CURLcode result = CURLE_OK; 797 struct IMAP *imap = conn->data->req.protop; 798 799 /* Check we have a query string */ 800 if(!imap->query) { 801 failf(conn->data, "Cannot SEARCH without a query string."); 802 return CURLE_URL_MALFORMAT; 803 } 804 805 /* Send the SEARCH command */ 806 result = imap_sendf(conn, "SEARCH %s", imap->query); 807 808 if(!result) 809 state(conn, IMAP_SEARCH); 810 811 return result; 812 } 813 814 /*********************************************************************** 815 * 816 * imap_perform_logout() 817 * 818 * Performs the logout action prior to sclose() being called. 819 */ 820 static CURLcode imap_perform_logout(struct connectdata *conn) 821 { 822 CURLcode result = CURLE_OK; 823 824 /* Send the LOGOUT command */ 825 result = imap_sendf(conn, "LOGOUT"); 826 827 if(!result) 828 state(conn, IMAP_LOGOUT); 829 830 return result; 831 } 832 833 /* For the initial server greeting */ 834 static CURLcode imap_state_servergreet_resp(struct connectdata *conn, 835 int imapcode, 836 imapstate instate) 837 { 838 CURLcode result = CURLE_OK; 839 struct SessionHandle *data = conn->data; 840 841 (void)instate; /* no use for this yet */ 842 843 if(imapcode != 'O') { 844 failf(data, "Got unexpected imap-server response"); 845 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */ 846 } 847 else 848 result = imap_perform_capability(conn); 849 850 return result; 851 } 852 853 /* For CAPABILITY responses */ 854 static CURLcode imap_state_capability_resp(struct connectdata *conn, 855 int imapcode, 856 imapstate instate) 857 { 858 CURLcode result = CURLE_OK; 859 struct SessionHandle *data = conn->data; 860 struct imap_conn *imapc = &conn->proto.imapc; 861 const char *line = data->state.buffer; 862 size_t wordlen; 863 864 (void)instate; /* no use for this yet */ 865 866 /* Do we have a untagged response? */ 867 if(imapcode == '*') { 868 line += 2; 869 870 /* Loop through the data line */ 871 for(;;) { 872 while(*line && 873 (*line == ' ' || *line == '\t' || 874 *line == '\r' || *line == '\n')) { 875 876 line++; 877 } 878 879 if(!*line) 880 break; 881 882 /* Extract the word */ 883 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' && 884 line[wordlen] != '\t' && line[wordlen] != '\r' && 885 line[wordlen] != '\n';) 886 wordlen++; 887 888 /* Does the server support the STARTTLS capability? */ 889 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8)) 890 imapc->tls_supported = TRUE; 891 892 /* Has the server explicitly disabled clear text authentication? */ 893 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13)) 894 imapc->login_disabled = TRUE; 895 896 /* Does the server support the SASL-IR capability? */ 897 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7)) 898 imapc->ir_supported = TRUE; 899 900 /* Do we have a SASL based authentication mechanism? */ 901 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) { 902 size_t llen; 903 unsigned int mechbit; 904 905 line += 5; 906 wordlen -= 5; 907 908 /* Test the word for a matching authentication mechanism */ 909 if((mechbit = Curl_sasl_decode_mech(line, wordlen, &llen)) && 910 llen == wordlen) 911 imapc->sasl.authmechs |= mechbit; 912 } 913 914 line += wordlen; 915 } 916 } 917 else if(imapcode == 'O') { 918 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { 919 /* We don't have a SSL/TLS connection yet, but SSL is requested */ 920 if(imapc->tls_supported) 921 /* Switch to TLS connection now */ 922 result = imap_perform_starttls(conn); 923 else if(data->set.use_ssl == CURLUSESSL_TRY) 924 /* Fallback and carry on with authentication */ 925 result = imap_perform_authentication(conn); 926 else { 927 failf(data, "STARTTLS not supported."); 928 result = CURLE_USE_SSL_FAILED; 929 } 930 } 931 else 932 result = imap_perform_authentication(conn); 933 } 934 else 935 result = imap_perform_authentication(conn); 936 937 return result; 938 } 939 940 /* For STARTTLS responses */ 941 static CURLcode imap_state_starttls_resp(struct connectdata *conn, 942 int imapcode, 943 imapstate instate) 944 { 945 CURLcode result = CURLE_OK; 946 struct SessionHandle *data = conn->data; 947 948 (void)instate; /* no use for this yet */ 949 950 if(imapcode != 'O') { 951 if(data->set.use_ssl != CURLUSESSL_TRY) { 952 failf(data, "STARTTLS denied. %c", imapcode); 953 result = CURLE_USE_SSL_FAILED; 954 } 955 else 956 result = imap_perform_authentication(conn); 957 } 958 else 959 result = imap_perform_upgrade_tls(conn); 960 961 return result; 962 } 963 964 /* For SASL authentication responses */ 965 static CURLcode imap_state_auth_resp(struct connectdata *conn, 966 int imapcode, 967 imapstate instate) 968 { 969 CURLcode result = CURLE_OK; 970 struct SessionHandle *data = conn->data; 971 struct imap_conn *imapc = &conn->proto.imapc; 972 saslprogress progress; 973 974 (void)instate; /* no use for this yet */ 975 976 result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress); 977 if(!result) 978 switch(progress) { 979 case SASL_DONE: 980 state(conn, IMAP_STOP); /* Authenticated */ 981 break; 982 case SASL_IDLE: /* No mechanism left after cancellation */ 983 if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT)) 984 /* Perform clear text authentication */ 985 result = imap_perform_login(conn); 986 else { 987 failf(data, "Authentication cancelled"); 988 result = CURLE_LOGIN_DENIED; 989 } 990 break; 991 default: 992 break; 993 } 994 995 return result; 996 } 997 998 /* For LOGIN responses */ 999 static CURLcode imap_state_login_resp(struct connectdata *conn, 1000 int imapcode, 1001 imapstate instate) 1002 { 1003 CURLcode result = CURLE_OK; 1004 struct SessionHandle *data = conn->data; 1005 1006 (void)instate; /* no use for this yet */ 1007 1008 if(imapcode != 'O') { 1009 failf(data, "Access denied. %c", imapcode); 1010 result = CURLE_LOGIN_DENIED; 1011 } 1012 else 1013 /* End of connect phase */ 1014 state(conn, IMAP_STOP); 1015 1016 return result; 1017 } 1018 1019 /* For LIST responses */ 1020 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode, 1021 imapstate instate) 1022 { 1023 CURLcode result = CURLE_OK; 1024 char *line = conn->data->state.buffer; 1025 size_t len = strlen(line); 1026 1027 (void)instate; /* No use for this yet */ 1028 1029 if(imapcode == '*') { 1030 /* Temporarily add the LF character back and send as body to the client */ 1031 line[len] = '\n'; 1032 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1); 1033 line[len] = '\0'; 1034 } 1035 else if(imapcode != 'O') 1036 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */ 1037 else 1038 /* End of DO phase */ 1039 state(conn, IMAP_STOP); 1040 1041 return result; 1042 } 1043 1044 /* For SELECT responses */ 1045 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode, 1046 imapstate instate) 1047 { 1048 CURLcode result = CURLE_OK; 1049 struct SessionHandle *data = conn->data; 1050 struct IMAP *imap = conn->data->req.protop; 1051 struct imap_conn *imapc = &conn->proto.imapc; 1052 const char *line = data->state.buffer; 1053 char tmp[20]; 1054 1055 (void)instate; /* no use for this yet */ 1056 1057 if(imapcode == '*') { 1058 /* See if this is an UIDVALIDITY response */ 1059 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) { 1060 Curl_safefree(imapc->mailbox_uidvalidity); 1061 imapc->mailbox_uidvalidity = strdup(tmp); 1062 } 1063 } 1064 else if(imapcode == 'O') { 1065 /* Check if the UIDVALIDITY has been specified and matches */ 1066 if(imap->uidvalidity && imapc->mailbox_uidvalidity && 1067 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) { 1068 failf(conn->data, "Mailbox UIDVALIDITY has changed"); 1069 result = CURLE_REMOTE_FILE_NOT_FOUND; 1070 } 1071 else { 1072 /* Note the currently opened mailbox on this connection */ 1073 imapc->mailbox = strdup(imap->mailbox); 1074 1075 if(imap->custom) 1076 result = imap_perform_list(conn); 1077 else if(imap->query) 1078 result = imap_perform_search(conn); 1079 else 1080 result = imap_perform_fetch(conn); 1081 } 1082 } 1083 else { 1084 failf(data, "Select failed"); 1085 result = CURLE_LOGIN_DENIED; 1086 } 1087 1088 return result; 1089 } 1090 1091 /* For the (first line of the) FETCH responses */ 1092 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode, 1093 imapstate instate) 1094 { 1095 CURLcode result = CURLE_OK; 1096 struct SessionHandle *data = conn->data; 1097 struct imap_conn *imapc = &conn->proto.imapc; 1098 struct pingpong *pp = &imapc->pp; 1099 const char *ptr = data->state.buffer; 1100 bool parsed = FALSE; 1101 curl_off_t size; 1102 1103 (void)instate; /* no use for this yet */ 1104 1105 if(imapcode != '*') { 1106 Curl_pgrsSetDownloadSize(data, -1); 1107 state(conn, IMAP_STOP); 1108 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */ 1109 } 1110 1111 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse 1112 the continuation data contained within the curly brackets */ 1113 while(*ptr && (*ptr != '{')) 1114 ptr++; 1115 1116 if(*ptr == '{') { 1117 char *endptr; 1118 size = curlx_strtoofft(ptr + 1, &endptr, 10); 1119 if(endptr - ptr > 1 && endptr[0] == '}' && 1120 endptr[1] == '\r' && endptr[2] == '\0') 1121 parsed = TRUE; 1122 } 1123 1124 if(parsed) { 1125 infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n", 1126 size); 1127 Curl_pgrsSetDownloadSize(data, size); 1128 1129 if(pp->cache) { 1130 /* At this point there is a bunch of data in the header "cache" that is 1131 actually body content, send it as body and then skip it. Do note 1132 that there may even be additional "headers" after the body. */ 1133 size_t chunk = pp->cache_size; 1134 1135 if(chunk > (size_t)size) 1136 /* The conversion from curl_off_t to size_t is always fine here */ 1137 chunk = (size_t)size; 1138 1139 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk); 1140 if(result) 1141 return result; 1142 1143 data->req.bytecount += chunk; 1144 1145 infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU 1146 " bytes, %" CURL_FORMAT_CURL_OFF_TU 1147 " bytes are left for transfer\n", (curl_off_t)chunk, 1148 size - chunk); 1149 1150 /* Have we used the entire cache or just part of it?*/ 1151 if(pp->cache_size > chunk) { 1152 /* Only part of it so shrink the cache to fit the trailing data */ 1153 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk); 1154 pp->cache_size -= chunk; 1155 } 1156 else { 1157 /* Free the cache */ 1158 Curl_safefree(pp->cache); 1159 1160 /* Reset the cache size */ 1161 pp->cache_size = 0; 1162 } 1163 } 1164 1165 if(data->req.bytecount == size) 1166 /* The entire data is already transferred! */ 1167 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); 1168 else { 1169 /* IMAP download */ 1170 data->req.maxdownload = size; 1171 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL); 1172 } 1173 } 1174 else { 1175 /* We don't know how to parse this line */ 1176 failf(pp->conn->data, "Failed to parse FETCH response."); 1177 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */ 1178 } 1179 1180 /* End of DO phase */ 1181 state(conn, IMAP_STOP); 1182 1183 return result; 1184 } 1185 1186 /* For final FETCH responses performed after the download */ 1187 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn, 1188 int imapcode, 1189 imapstate instate) 1190 { 1191 CURLcode result = CURLE_OK; 1192 1193 (void)instate; /* No use for this yet */ 1194 1195 if(imapcode != 'O') 1196 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */ 1197 else 1198 /* End of DONE phase */ 1199 state(conn, IMAP_STOP); 1200 1201 return result; 1202 } 1203 1204 /* For APPEND responses */ 1205 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode, 1206 imapstate instate) 1207 { 1208 CURLcode result = CURLE_OK; 1209 struct SessionHandle *data = conn->data; 1210 1211 (void)instate; /* No use for this yet */ 1212 1213 if(imapcode != '+') { 1214 result = CURLE_UPLOAD_FAILED; 1215 } 1216 else { 1217 /* Set the progress upload size */ 1218 Curl_pgrsSetUploadSize(data, data->state.infilesize); 1219 1220 /* IMAP upload */ 1221 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); 1222 1223 /* End of DO phase */ 1224 state(conn, IMAP_STOP); 1225 } 1226 1227 return result; 1228 } 1229 1230 /* For final APPEND responses performed after the upload */ 1231 static CURLcode imap_state_append_final_resp(struct connectdata *conn, 1232 int imapcode, 1233 imapstate instate) 1234 { 1235 CURLcode result = CURLE_OK; 1236 1237 (void)instate; /* No use for this yet */ 1238 1239 if(imapcode != 'O') 1240 result = CURLE_UPLOAD_FAILED; 1241 else 1242 /* End of DONE phase */ 1243 state(conn, IMAP_STOP); 1244 1245 return result; 1246 } 1247 1248 /* For SEARCH responses */ 1249 static CURLcode imap_state_search_resp(struct connectdata *conn, int imapcode, 1250 imapstate instate) 1251 { 1252 CURLcode result = CURLE_OK; 1253 char *line = conn->data->state.buffer; 1254 size_t len = strlen(line); 1255 1256 (void)instate; /* No use for this yet */ 1257 1258 if(imapcode == '*') { 1259 /* Temporarily add the LF character back and send as body to the client */ 1260 line[len] = '\n'; 1261 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1); 1262 line[len] = '\0'; 1263 } 1264 else if(imapcode != 'O') 1265 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */ 1266 else 1267 /* End of DO phase */ 1268 state(conn, IMAP_STOP); 1269 1270 return result; 1271 } 1272 1273 static CURLcode imap_statemach_act(struct connectdata *conn) 1274 { 1275 CURLcode result = CURLE_OK; 1276 curl_socket_t sock = conn->sock[FIRSTSOCKET]; 1277 int imapcode; 1278 struct imap_conn *imapc = &conn->proto.imapc; 1279 struct pingpong *pp = &imapc->pp; 1280 size_t nread = 0; 1281 1282 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */ 1283 if(imapc->state == IMAP_UPGRADETLS) 1284 return imap_perform_upgrade_tls(conn); 1285 1286 /* Flush any data that needs to be sent */ 1287 if(pp->sendleft) 1288 return Curl_pp_flushsend(pp); 1289 1290 do { 1291 /* Read the response from the server */ 1292 result = Curl_pp_readresp(sock, pp, &imapcode, &nread); 1293 if(result) 1294 return result; 1295 1296 /* Was there an error parsing the response line? */ 1297 if(imapcode == -1) 1298 return CURLE_FTP_WEIRD_SERVER_REPLY; 1299 1300 if(!imapcode) 1301 break; 1302 1303 /* We have now received a full IMAP server response */ 1304 switch(imapc->state) { 1305 case IMAP_SERVERGREET: 1306 result = imap_state_servergreet_resp(conn, imapcode, imapc->state); 1307 break; 1308 1309 case IMAP_CAPABILITY: 1310 result = imap_state_capability_resp(conn, imapcode, imapc->state); 1311 break; 1312 1313 case IMAP_STARTTLS: 1314 result = imap_state_starttls_resp(conn, imapcode, imapc->state); 1315 break; 1316 1317 case IMAP_AUTHENTICATE: 1318 result = imap_state_auth_resp(conn, imapcode, imapc->state); 1319 break; 1320 1321 case IMAP_LOGIN: 1322 result = imap_state_login_resp(conn, imapcode, imapc->state); 1323 break; 1324 1325 case IMAP_LIST: 1326 result = imap_state_list_resp(conn, imapcode, imapc->state); 1327 break; 1328 1329 case IMAP_SELECT: 1330 result = imap_state_select_resp(conn, imapcode, imapc->state); 1331 break; 1332 1333 case IMAP_FETCH: 1334 result = imap_state_fetch_resp(conn, imapcode, imapc->state); 1335 break; 1336 1337 case IMAP_FETCH_FINAL: 1338 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state); 1339 break; 1340 1341 case IMAP_APPEND: 1342 result = imap_state_append_resp(conn, imapcode, imapc->state); 1343 break; 1344 1345 case IMAP_APPEND_FINAL: 1346 result = imap_state_append_final_resp(conn, imapcode, imapc->state); 1347 break; 1348 1349 case IMAP_SEARCH: 1350 result = imap_state_search_resp(conn, imapcode, imapc->state); 1351 break; 1352 1353 case IMAP_LOGOUT: 1354 /* fallthrough, just stop! */ 1355 default: 1356 /* internal error */ 1357 state(conn, IMAP_STOP); 1358 break; 1359 } 1360 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp)); 1361 1362 return result; 1363 } 1364 1365 /* Called repeatedly until done from multi.c */ 1366 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done) 1367 { 1368 CURLcode result = CURLE_OK; 1369 struct imap_conn *imapc = &conn->proto.imapc; 1370 1371 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) { 1372 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone); 1373 if(result || !imapc->ssldone) 1374 return result; 1375 } 1376 1377 result = Curl_pp_statemach(&imapc->pp, FALSE); 1378 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE; 1379 1380 return result; 1381 } 1382 1383 static CURLcode imap_block_statemach(struct connectdata *conn) 1384 { 1385 CURLcode result = CURLE_OK; 1386 struct imap_conn *imapc = &conn->proto.imapc; 1387 1388 while(imapc->state != IMAP_STOP && !result) 1389 result = Curl_pp_statemach(&imapc->pp, TRUE); 1390 1391 return result; 1392 } 1393 1394 /* Allocate and initialize the struct IMAP for the current SessionHandle if 1395 required */ 1396 static CURLcode imap_init(struct connectdata *conn) 1397 { 1398 CURLcode result = CURLE_OK; 1399 struct SessionHandle *data = conn->data; 1400 struct IMAP *imap; 1401 1402 imap = data->req.protop = calloc(sizeof(struct IMAP), 1); 1403 if(!imap) 1404 result = CURLE_OUT_OF_MEMORY; 1405 1406 return result; 1407 } 1408 1409 /* For the IMAP "protocol connect" and "doing" phases only */ 1410 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks, 1411 int numsocks) 1412 { 1413 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks); 1414 } 1415 1416 /*********************************************************************** 1417 * 1418 * imap_connect() 1419 * 1420 * This function should do everything that is to be considered a part of the 1421 * connection phase. 1422 * 1423 * The variable 'done' points to will be TRUE if the protocol-layer connect 1424 * phase is done when this function returns, or FALSE if not. 1425 */ 1426 static CURLcode imap_connect(struct connectdata *conn, bool *done) 1427 { 1428 CURLcode result = CURLE_OK; 1429 struct imap_conn *imapc = &conn->proto.imapc; 1430 struct pingpong *pp = &imapc->pp; 1431 1432 *done = FALSE; /* default to not done yet */ 1433 1434 /* We always support persistent connections in IMAP */ 1435 connkeep(conn, "IMAP default"); 1436 1437 /* Set the default response time-out */ 1438 pp->response_time = RESP_TIMEOUT; 1439 pp->statemach_act = imap_statemach_act; 1440 pp->endofresp = imap_endofresp; 1441 pp->conn = conn; 1442 1443 /* Set the default preferred authentication type and mechanism */ 1444 imapc->preftype = IMAP_TYPE_ANY; 1445 Curl_sasl_init(&imapc->sasl, &saslimap); 1446 1447 /* Initialise the pingpong layer */ 1448 Curl_pp_init(pp); 1449 1450 /* Parse the URL options */ 1451 result = imap_parse_url_options(conn); 1452 if(result) 1453 return result; 1454 1455 /* Start off waiting for the server greeting response */ 1456 state(conn, IMAP_SERVERGREET); 1457 1458 /* Start off with an response id of '*' */ 1459 strcpy(imapc->resptag, "*"); 1460 1461 result = imap_multi_statemach(conn, done); 1462 1463 return result; 1464 } 1465 1466 /*********************************************************************** 1467 * 1468 * imap_done() 1469 * 1470 * The DONE function. This does what needs to be done after a single DO has 1471 * performed. 1472 * 1473 * Input argument is already checked for validity. 1474 */ 1475 static CURLcode imap_done(struct connectdata *conn, CURLcode status, 1476 bool premature) 1477 { 1478 CURLcode result = CURLE_OK; 1479 struct SessionHandle *data = conn->data; 1480 struct IMAP *imap = data->req.protop; 1481 1482 (void)premature; 1483 1484 if(!imap) 1485 /* When the easy handle is removed from the multi interface while libcurl 1486 is still trying to resolve the host name, the IMAP struct is not yet 1487 initialized. However, the removal action calls Curl_done() which in 1488 turn calls this function, so we simply return success. */ 1489 return CURLE_OK; 1490 1491 if(status) { 1492 connclose(conn, "IMAP done with bad status"); /* marked for closure */ 1493 result = status; /* use the already set error code */ 1494 } 1495 else if(!data->set.connect_only && !imap->custom && 1496 (imap->uid || data->set.upload)) { 1497 /* Handle responses after FETCH or APPEND transfer has finished */ 1498 if(!data->set.upload) 1499 state(conn, IMAP_FETCH_FINAL); 1500 else { 1501 /* End the APPEND command first by sending an empty line */ 1502 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", ""); 1503 if(!result) 1504 state(conn, IMAP_APPEND_FINAL); 1505 } 1506 1507 /* Run the state-machine 1508 1509 TODO: when the multi interface is used, this _really_ should be using 1510 the imap_multi_statemach function but we have no general support for 1511 non-blocking DONE operations, not in the multi state machine and with 1512 Curl_done() invokes on several places in the code! 1513 */ 1514 if(!result) 1515 result = imap_block_statemach(conn); 1516 } 1517 1518 /* Cleanup our per-request based variables */ 1519 Curl_safefree(imap->mailbox); 1520 Curl_safefree(imap->uidvalidity); 1521 Curl_safefree(imap->uid); 1522 Curl_safefree(imap->section); 1523 Curl_safefree(imap->partial); 1524 Curl_safefree(imap->query); 1525 Curl_safefree(imap->custom); 1526 Curl_safefree(imap->custom_params); 1527 1528 /* Clear the transfer mode for the next request */ 1529 imap->transfer = FTPTRANSFER_BODY; 1530 1531 return result; 1532 } 1533 1534 /*********************************************************************** 1535 * 1536 * imap_perform() 1537 * 1538 * This is the actual DO function for IMAP. Fetch or append a message, or do 1539 * other things according to the options previously setup. 1540 */ 1541 static CURLcode imap_perform(struct connectdata *conn, bool *connected, 1542 bool *dophase_done) 1543 { 1544 /* This is IMAP and no proxy */ 1545 CURLcode result = CURLE_OK; 1546 struct SessionHandle *data = conn->data; 1547 struct IMAP *imap = data->req.protop; 1548 struct imap_conn *imapc = &conn->proto.imapc; 1549 bool selected = FALSE; 1550 1551 DEBUGF(infof(conn->data, "DO phase starts\n")); 1552 1553 if(conn->data->set.opt_no_body) { 1554 /* Requested no body means no transfer */ 1555 imap->transfer = FTPTRANSFER_INFO; 1556 } 1557 1558 *dophase_done = FALSE; /* not done yet */ 1559 1560 /* Determine if the requested mailbox (with the same UIDVALIDITY if set) 1561 has already been selected on this connection */ 1562 if(imap->mailbox && imapc->mailbox && 1563 !strcmp(imap->mailbox, imapc->mailbox) && 1564 (!imap->uidvalidity || !imapc->mailbox_uidvalidity || 1565 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity))) 1566 selected = TRUE; 1567 1568 /* Start the first command in the DO phase */ 1569 if(conn->data->set.upload) 1570 /* APPEND can be executed directly */ 1571 result = imap_perform_append(conn); 1572 else if(imap->custom && (selected || !imap->mailbox)) 1573 /* Custom command using the same mailbox or no mailbox */ 1574 result = imap_perform_list(conn); 1575 else if(!imap->custom && selected && imap->uid) 1576 /* FETCH from the same mailbox */ 1577 result = imap_perform_fetch(conn); 1578 else if(!imap->custom && selected && imap->query) 1579 /* SEARCH the current mailbox */ 1580 result = imap_perform_search(conn); 1581 else if(imap->mailbox && !selected && 1582 (imap->custom || imap->uid || imap->query)) 1583 /* SELECT the mailbox */ 1584 result = imap_perform_select(conn); 1585 else 1586 /* LIST */ 1587 result = imap_perform_list(conn); 1588 1589 if(result) 1590 return result; 1591 1592 /* Run the state-machine */ 1593 result = imap_multi_statemach(conn, dophase_done); 1594 1595 *connected = conn->bits.tcpconnect[FIRSTSOCKET]; 1596 1597 if(*dophase_done) 1598 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1599 1600 return result; 1601 } 1602 1603 /*********************************************************************** 1604 * 1605 * imap_do() 1606 * 1607 * This function is registered as 'curl_do' function. It decodes the path 1608 * parts etc as a wrapper to the actual DO function (imap_perform). 1609 * 1610 * The input argument is already checked for validity. 1611 */ 1612 static CURLcode imap_do(struct connectdata *conn, bool *done) 1613 { 1614 CURLcode result = CURLE_OK; 1615 1616 *done = FALSE; /* default to false */ 1617 1618 /* Parse the URL path */ 1619 result = imap_parse_url_path(conn); 1620 if(result) 1621 return result; 1622 1623 /* Parse the custom request */ 1624 result = imap_parse_custom_request(conn); 1625 if(result) 1626 return result; 1627 1628 result = imap_regular_transfer(conn, done); 1629 1630 return result; 1631 } 1632 1633 /*********************************************************************** 1634 * 1635 * imap_disconnect() 1636 * 1637 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection 1638 * resources. BLOCKING. 1639 */ 1640 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection) 1641 { 1642 struct imap_conn *imapc = &conn->proto.imapc; 1643 1644 /* We cannot send quit unconditionally. If this connection is stale or 1645 bad in any way, sending quit and waiting around here will make the 1646 disconnect wait in vain and cause more problems than we need to. */ 1647 1648 /* The IMAP session may or may not have been allocated/setup at this 1649 point! */ 1650 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart) 1651 if(!imap_perform_logout(conn)) 1652 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */ 1653 1654 /* Disconnect from the server */ 1655 Curl_pp_disconnect(&imapc->pp); 1656 1657 /* Cleanup the SASL module */ 1658 Curl_sasl_cleanup(conn, imapc->sasl.authused); 1659 1660 /* Cleanup our connection based variables */ 1661 Curl_safefree(imapc->mailbox); 1662 Curl_safefree(imapc->mailbox_uidvalidity); 1663 1664 return CURLE_OK; 1665 } 1666 1667 /* Call this when the DO phase has completed */ 1668 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected) 1669 { 1670 struct IMAP *imap = conn->data->req.protop; 1671 1672 (void)connected; 1673 1674 if(imap->transfer != FTPTRANSFER_BODY) 1675 /* no data to transfer */ 1676 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); 1677 1678 return CURLE_OK; 1679 } 1680 1681 /* Called from multi.c while DOing */ 1682 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done) 1683 { 1684 CURLcode result = imap_multi_statemach(conn, dophase_done); 1685 1686 if(result) 1687 DEBUGF(infof(conn->data, "DO phase failed\n")); 1688 else if(*dophase_done) { 1689 result = imap_dophase_done(conn, FALSE /* not connected */); 1690 1691 DEBUGF(infof(conn->data, "DO phase is complete\n")); 1692 } 1693 1694 return result; 1695 } 1696 1697 /*********************************************************************** 1698 * 1699 * imap_regular_transfer() 1700 * 1701 * The input argument is already checked for validity. 1702 * 1703 * Performs all commands done before a regular transfer between a local and a 1704 * remote host. 1705 */ 1706 static CURLcode imap_regular_transfer(struct connectdata *conn, 1707 bool *dophase_done) 1708 { 1709 CURLcode result = CURLE_OK; 1710 bool connected = FALSE; 1711 struct SessionHandle *data = conn->data; 1712 1713 /* Make sure size is unknown at this point */ 1714 data->req.size = -1; 1715 1716 /* Set the progress data */ 1717 Curl_pgrsSetUploadCounter(data, 0); 1718 Curl_pgrsSetDownloadCounter(data, 0); 1719 Curl_pgrsSetUploadSize(data, -1); 1720 Curl_pgrsSetDownloadSize(data, -1); 1721 1722 /* Carry out the perform */ 1723 result = imap_perform(conn, &connected, dophase_done); 1724 1725 /* Perform post DO phase operations if necessary */ 1726 if(!result && *dophase_done) 1727 result = imap_dophase_done(conn, connected); 1728 1729 return result; 1730 } 1731 1732 static CURLcode imap_setup_connection(struct connectdata *conn) 1733 { 1734 struct SessionHandle *data = conn->data; 1735 1736 /* Initialise the IMAP layer */ 1737 CURLcode result = imap_init(conn); 1738 if(result) 1739 return result; 1740 1741 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { 1742 /* Unless we have asked to tunnel IMAP operations through the proxy, we 1743 switch and use HTTP operations only */ 1744 #ifndef CURL_DISABLE_HTTP 1745 if(conn->handler == &Curl_handler_imap) 1746 conn->handler = &Curl_handler_imap_proxy; 1747 else { 1748 #ifdef USE_SSL 1749 conn->handler = &Curl_handler_imaps_proxy; 1750 #else 1751 failf(data, "IMAPS not supported!"); 1752 return CURLE_UNSUPPORTED_PROTOCOL; 1753 #endif 1754 } 1755 1756 /* set it up as an HTTP connection instead */ 1757 return conn->handler->setup_connection(conn); 1758 #else 1759 failf(data, "IMAP over http proxy requires HTTP support built-in!"); 1760 return CURLE_UNSUPPORTED_PROTOCOL; 1761 #endif 1762 } 1763 1764 data->state.path++; /* don't include the initial slash */ 1765 1766 return CURLE_OK; 1767 } 1768 1769 /*********************************************************************** 1770 * 1771 * imap_sendf() 1772 * 1773 * Sends the formated string as an IMAP command to the server. 1774 * 1775 * Designed to never block. 1776 */ 1777 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...) 1778 { 1779 CURLcode result = CURLE_OK; 1780 struct imap_conn *imapc = &conn->proto.imapc; 1781 char *taggedfmt; 1782 va_list ap; 1783 1784 DEBUGASSERT(fmt); 1785 1786 /* Calculate the next command ID wrapping at 3 digits */ 1787 imapc->cmdid = (imapc->cmdid + 1) % 1000; 1788 1789 /* Calculate the tag based on the connection ID and command ID */ 1790 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", 1791 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid); 1792 1793 /* Prefix the format with the tag */ 1794 taggedfmt = aprintf("%s %s", imapc->resptag, fmt); 1795 if(!taggedfmt) 1796 return CURLE_OUT_OF_MEMORY; 1797 1798 /* Send the data with the tag */ 1799 va_start(ap, fmt); 1800 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap); 1801 va_end(ap); 1802 1803 free(taggedfmt); 1804 1805 return result; 1806 } 1807 1808 /*********************************************************************** 1809 * 1810 * imap_atom() 1811 * 1812 * Checks the input string for characters that need escaping and returns an 1813 * atom ready for sending to the server. 1814 * 1815 * The returned string needs to be freed. 1816 * 1817 */ 1818 static char *imap_atom(const char *str) 1819 { 1820 const char *p1; 1821 char *p2; 1822 size_t backsp_count = 0; 1823 size_t quote_count = 0; 1824 bool space_exists = FALSE; 1825 size_t newlen = 0; 1826 char *newstr = NULL; 1827 1828 if(!str) 1829 return NULL; 1830 1831 /* Count any unescaped characters */ 1832 p1 = str; 1833 while(*p1) { 1834 if(*p1 == '\\') 1835 backsp_count++; 1836 else if(*p1 == '"') 1837 quote_count++; 1838 else if(*p1 == ' ') 1839 space_exists = TRUE; 1840 1841 p1++; 1842 } 1843 1844 /* Does the input contain any unescaped characters? */ 1845 if(!backsp_count && !quote_count && !space_exists) 1846 return strdup(str); 1847 1848 /* Calculate the new string length */ 1849 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0); 1850 1851 /* Allocate the new string */ 1852 newstr = (char *) malloc((newlen + 1) * sizeof(char)); 1853 if(!newstr) 1854 return NULL; 1855 1856 /* Surround the string in quotes if necessary */ 1857 p2 = newstr; 1858 if(space_exists) { 1859 newstr[0] = '"'; 1860 newstr[newlen - 1] = '"'; 1861 p2++; 1862 } 1863 1864 /* Copy the string, escaping backslash and quote characters along the way */ 1865 p1 = str; 1866 while(*p1) { 1867 if(*p1 == '\\' || *p1 == '"') { 1868 *p2 = '\\'; 1869 p2++; 1870 } 1871 1872 *p2 = *p1; 1873 1874 p1++; 1875 p2++; 1876 } 1877 1878 /* Terminate the string */ 1879 newstr[newlen] = '\0'; 1880 1881 return newstr; 1882 } 1883 1884 /*********************************************************************** 1885 * 1886 * imap_is_bchar() 1887 * 1888 * Portable test of whether the specified char is a "bchar" as defined in the 1889 * grammar of RFC-5092. 1890 */ 1891 static bool imap_is_bchar(char ch) 1892 { 1893 switch(ch) { 1894 /* bchar */ 1895 case ':': case '@': case '/': 1896 /* bchar -> achar */ 1897 case '&': case '=': 1898 /* bchar -> achar -> uchar -> unreserved */ 1899 case '0': case '1': case '2': case '3': case '4': case '5': case '6': 1900 case '7': case '8': case '9': 1901 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 1902 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 1903 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 1904 case 'V': case 'W': case 'X': case 'Y': case 'Z': 1905 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': 1906 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': 1907 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': 1908 case 'v': case 'w': case 'x': case 'y': case 'z': 1909 case '-': case '.': case '_': case '~': 1910 /* bchar -> achar -> uchar -> sub-delims-sh */ 1911 case '!': case '$': case '\'': case '(': case ')': case '*': 1912 case '+': case ',': 1913 /* bchar -> achar -> uchar -> pct-encoded */ 1914 case '%': /* HEXDIG chars are already included above */ 1915 return true; 1916 1917 default: 1918 return false; 1919 } 1920 } 1921 1922 /*********************************************************************** 1923 * 1924 * imap_parse_url_options() 1925 * 1926 * Parse the URL login options. 1927 */ 1928 static CURLcode imap_parse_url_options(struct connectdata *conn) 1929 { 1930 CURLcode result = CURLE_OK; 1931 struct imap_conn *imapc = &conn->proto.imapc; 1932 const char *ptr = conn->options; 1933 1934 imapc->sasl.resetprefs = TRUE; 1935 1936 while(!result && ptr && *ptr) { 1937 const char *key = ptr; 1938 const char *value; 1939 1940 while(*ptr && *ptr != '=') 1941 ptr++; 1942 1943 value = ptr + 1; 1944 1945 while(*ptr && *ptr != ';') 1946 ptr++; 1947 1948 if(strnequal(key, "AUTH=", 5)) 1949 result = Curl_sasl_parse_url_auth_option(&imapc->sasl, 1950 value, ptr - value); 1951 else 1952 result = CURLE_URL_MALFORMAT; 1953 1954 if(*ptr == ';') 1955 ptr++; 1956 } 1957 1958 switch(imapc->sasl.prefmech) { 1959 case SASL_AUTH_NONE: 1960 imapc->preftype = IMAP_TYPE_NONE; 1961 break; 1962 case SASL_AUTH_DEFAULT: 1963 imapc->preftype = IMAP_TYPE_ANY; 1964 break; 1965 default: 1966 imapc->preftype = IMAP_TYPE_SASL; 1967 break; 1968 } 1969 1970 return result; 1971 } 1972 1973 /*********************************************************************** 1974 * 1975 * imap_parse_url_path() 1976 * 1977 * Parse the URL path into separate path components. 1978 * 1979 */ 1980 static CURLcode imap_parse_url_path(struct connectdata *conn) 1981 { 1982 /* The imap struct is already initialised in imap_connect() */ 1983 CURLcode result = CURLE_OK; 1984 struct SessionHandle *data = conn->data; 1985 struct IMAP *imap = data->req.protop; 1986 const char *begin = data->state.path; 1987 const char *ptr = begin; 1988 1989 /* See how much of the URL is a valid path and decode it */ 1990 while(imap_is_bchar(*ptr)) 1991 ptr++; 1992 1993 if(ptr != begin) { 1994 /* Remove the trailing slash if present */ 1995 const char *end = ptr; 1996 if(end > begin && end[-1] == '/') 1997 end--; 1998 1999 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL, 2000 TRUE); 2001 if(result) 2002 return result; 2003 } 2004 else 2005 imap->mailbox = NULL; 2006 2007 /* There can be any number of parameters in the form ";NAME=VALUE" */ 2008 while(*ptr == ';') { 2009 char *name; 2010 char *value; 2011 size_t valuelen; 2012 2013 /* Find the length of the name parameter */ 2014 begin = ++ptr; 2015 while(*ptr && *ptr != '=') 2016 ptr++; 2017 2018 if(!*ptr) 2019 return CURLE_URL_MALFORMAT; 2020 2021 /* Decode the name parameter */ 2022 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE); 2023 if(result) 2024 return result; 2025 2026 /* Find the length of the value parameter */ 2027 begin = ++ptr; 2028 while(imap_is_bchar(*ptr)) 2029 ptr++; 2030 2031 /* Decode the value parameter */ 2032 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE); 2033 if(result) { 2034 free(name); 2035 return result; 2036 } 2037 2038 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value)); 2039 2040 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and 2041 PARTIAL) stripping of the trailing slash character if it is present. 2042 2043 Note: Unknown parameters trigger a URL_MALFORMAT error. */ 2044 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) { 2045 if(valuelen > 0 && value[valuelen - 1] == '/') 2046 value[valuelen - 1] = '\0'; 2047 2048 imap->uidvalidity = value; 2049 value = NULL; 2050 } 2051 else if(Curl_raw_equal(name, "UID") && !imap->uid) { 2052 if(valuelen > 0 && value[valuelen - 1] == '/') 2053 value[valuelen - 1] = '\0'; 2054 2055 imap->uid = value; 2056 value = NULL; 2057 } 2058 else if(Curl_raw_equal(name, "SECTION") && !imap->section) { 2059 if(valuelen > 0 && value[valuelen - 1] == '/') 2060 value[valuelen - 1] = '\0'; 2061 2062 imap->section = value; 2063 value = NULL; 2064 } 2065 else if(Curl_raw_equal(name, "PARTIAL") && !imap->partial) { 2066 if(valuelen > 0 && value[valuelen - 1] == '/') 2067 value[valuelen - 1] = '\0'; 2068 2069 imap->partial = value; 2070 value = NULL; 2071 } 2072 else { 2073 free(name); 2074 free(value); 2075 2076 return CURLE_URL_MALFORMAT; 2077 } 2078 2079 free(name); 2080 free(value); 2081 } 2082 2083 /* Does the URL contain a query parameter? Only valid when we have a mailbox 2084 and no UID as per RFC-5092 */ 2085 if(imap->mailbox && !imap->uid && *ptr == '?') { 2086 /* Find the length of the query parameter */ 2087 begin = ++ptr; 2088 while(imap_is_bchar(*ptr)) 2089 ptr++; 2090 2091 /* Decode the query parameter */ 2092 result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL, 2093 TRUE); 2094 if(result) 2095 return result; 2096 } 2097 2098 /* Any extra stuff at the end of the URL is an error */ 2099 if(*ptr) 2100 return CURLE_URL_MALFORMAT; 2101 2102 return CURLE_OK; 2103 } 2104 2105 /*********************************************************************** 2106 * 2107 * imap_parse_custom_request() 2108 * 2109 * Parse the custom request. 2110 */ 2111 static CURLcode imap_parse_custom_request(struct connectdata *conn) 2112 { 2113 CURLcode result = CURLE_OK; 2114 struct SessionHandle *data = conn->data; 2115 struct IMAP *imap = data->req.protop; 2116 const char *custom = data->set.str[STRING_CUSTOMREQUEST]; 2117 2118 if(custom) { 2119 /* URL decode the custom request */ 2120 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE); 2121 2122 /* Extract the parameters if specified */ 2123 if(!result) { 2124 const char *params = imap->custom; 2125 2126 while(*params && *params != ' ') 2127 params++; 2128 2129 if(*params) { 2130 imap->custom_params = strdup(params); 2131 imap->custom[params - imap->custom] = '\0'; 2132 2133 if(!imap->custom_params) 2134 result = CURLE_OUT_OF_MEMORY; 2135 } 2136 } 2137 } 2138 2139 return result; 2140 } 2141 2142 #endif /* CURL_DISABLE_IMAP */ 2143