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