1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at http://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * 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 * RFC4959 IMAP Extension for SASL Initial Client Response 28 * RFC5092 IMAP URL Scheme 29 * RFC6749 OAuth 2.0 Authorization Framework 30 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt> 31 * 32 ***************************************************************************/ 33 34#include "curl_setup.h" 35 36#ifndef CURL_DISABLE_IMAP 37 38#ifdef HAVE_NETINET_IN_H 39#include <netinet/in.h> 40#endif 41#ifdef HAVE_ARPA_INET_H 42#include <arpa/inet.h> 43#endif 44#ifdef HAVE_UTSNAME_H 45#include <sys/utsname.h> 46#endif 47#ifdef HAVE_NETDB_H 48#include <netdb.h> 49#endif 50#ifdef __VMS 51#include <in.h> 52#include <inet.h> 53#endif 54 55#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) 56#undef in_addr_t 57#define in_addr_t unsigned long 58#endif 59 60#include <curl/curl.h> 61#include "urldata.h" 62#include "sendf.h" 63#include "if2ip.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 84#define _MPRINTF_REPLACE /* use our functions only */ 85#include <curl/mprintf.h> 86 87#include "curl_memory.h" 88/* The last #include file should be: */ 89#include "memdebug.h" 90 91/* Local API functions */ 92static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done); 93static CURLcode imap_do(struct connectdata *conn, bool *done); 94static CURLcode imap_done(struct connectdata *conn, CURLcode status, 95 bool premature); 96static CURLcode imap_connect(struct connectdata *conn, bool *done); 97static CURLcode imap_disconnect(struct connectdata *conn, bool dead); 98static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done); 99static int imap_getsock(struct connectdata *conn, curl_socket_t *socks, 100 int numsocks); 101static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done); 102static CURLcode imap_setup_connection(struct connectdata *conn); 103static char *imap_atom(const char *str); 104static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...); 105static CURLcode imap_parse_url_options(struct connectdata *conn); 106static CURLcode imap_parse_url_path(struct connectdata *conn); 107static CURLcode imap_parse_custom_request(struct connectdata *conn); 108static CURLcode imap_calc_sasl_details(struct connectdata *conn, 109 const char **mech, 110 char **initresp, size_t *len, 111 imapstate *state1, imapstate *state2); 112 113/* 114 * IMAP protocol handler. 115 */ 116 117const 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 | PROTOPT_NEEDSPWD /* flags */ 135}; 136 137#ifdef USE_SSL 138/* 139 * IMAPS protocol handler. 140 */ 141 142const 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 | 160 PROTOPT_NEEDSPWD /* flags */ 161}; 162#endif 163 164#ifndef CURL_DISABLE_HTTP 165/* 166 * HTTP-proxyed IMAP protocol handler. 167 */ 168 169static const struct Curl_handler Curl_handler_imap_proxy = { 170 "IMAP", /* scheme */ 171 Curl_http_setup_conn, /* setup_connection */ 172 Curl_http, /* do_it */ 173 Curl_http_done, /* done */ 174 ZERO_NULL, /* do_more */ 175 ZERO_NULL, /* connect_it */ 176 ZERO_NULL, /* connecting */ 177 ZERO_NULL, /* doing */ 178 ZERO_NULL, /* proto_getsock */ 179 ZERO_NULL, /* doing_getsock */ 180 ZERO_NULL, /* domore_getsock */ 181 ZERO_NULL, /* perform_getsock */ 182 ZERO_NULL, /* disconnect */ 183 ZERO_NULL, /* readwrite */ 184 PORT_IMAP, /* defport */ 185 CURLPROTO_HTTP, /* protocol */ 186 PROTOPT_NONE /* flags */ 187}; 188 189#ifdef USE_SSL 190/* 191 * HTTP-proxyed IMAPS protocol handler. 192 */ 193 194static const struct Curl_handler Curl_handler_imaps_proxy = { 195 "IMAPS", /* scheme */ 196 Curl_http_setup_conn, /* setup_connection */ 197 Curl_http, /* do_it */ 198 Curl_http_done, /* done */ 199 ZERO_NULL, /* do_more */ 200 ZERO_NULL, /* connect_it */ 201 ZERO_NULL, /* connecting */ 202 ZERO_NULL, /* doing */ 203 ZERO_NULL, /* proto_getsock */ 204 ZERO_NULL, /* doing_getsock */ 205 ZERO_NULL, /* domore_getsock */ 206 ZERO_NULL, /* perform_getsock */ 207 ZERO_NULL, /* disconnect */ 208 ZERO_NULL, /* readwrite */ 209 PORT_IMAPS, /* defport */ 210 CURLPROTO_HTTP, /* protocol */ 211 PROTOPT_NONE /* flags */ 212}; 213#endif 214#endif 215 216#ifdef USE_SSL 217static void imap_to_imaps(struct connectdata *conn) 218{ 219 conn->handler = &Curl_handler_imaps; 220} 221#else 222#define imap_to_imaps(x) Curl_nop_stmt 223#endif 224 225/*********************************************************************** 226 * 227 * imap_matchresp() 228 * 229 * Determines whether the untagged response is related to the specified 230 * command by checking if it is in format "* <command-name> ..." or 231 * "* <number> <command-name> ...". 232 * 233 * The "* " marker is assumed to have already been checked by the caller. 234 */ 235static bool imap_matchresp(const char *line, size_t len, const char *cmd) 236{ 237 const char *end = line + len; 238 size_t cmd_len = strlen(cmd); 239 240 /* Skip the untagged response marker */ 241 line += 2; 242 243 /* Do we have a number after the marker? */ 244 if(line < end && ISDIGIT(*line)) { 245 /* Skip the number */ 246 do 247 line++; 248 while(line < end && ISDIGIT(*line)); 249 250 /* Do we have the space character? */ 251 if(line == end || *line != ' ') 252 return FALSE; 253 254 line++; 255 } 256 257 /* Does the command name match and is it followed by a space character or at 258 the end of line? */ 259 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) && 260 (line[cmd_len] == ' ' || line + cmd_len + 2 == end)) 261 return TRUE; 262 263 return FALSE; 264} 265 266/*********************************************************************** 267 * 268 * imap_endofresp() 269 * 270 * Checks whether the given string is a valid tagged, untagged or continuation 271 * response which can be processed by the response handler. 272 */ 273static bool imap_endofresp(struct connectdata *conn, char *line, size_t len, 274 int *resp) 275{ 276 struct IMAP *imap = conn->data->req.protop; 277 struct imap_conn *imapc = &conn->proto.imapc; 278 const char *id = imapc->resptag; 279 size_t id_len = strlen(id); 280 281 /* Do we have a tagged command response? */ 282 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') { 283 line += id_len + 1; 284 len -= id_len + 1; 285 286 if(len >= 2 && !memcmp(line, "OK", 2)) 287 *resp = 'O'; 288 else if(len >= 2 && !memcmp(line, "NO", 2)) 289 *resp = 'N'; 290 else if(len >= 3 && !memcmp(line, "BAD", 3)) 291 *resp = 'B'; 292 else { 293 failf(conn->data, "Bad tagged response"); 294 *resp = -1; 295 } 296 297 return TRUE; 298 } 299 300 /* Do we have an untagged command response? */ 301 if(len >= 2 && !memcmp("* ", line, 2)) { 302 switch(imapc->state) { 303 /* States which are interested in untagged responses */ 304 case IMAP_CAPABILITY: 305 if(!imap_matchresp(line, len, "CAPABILITY")) 306 return FALSE; 307 break; 308 309 case IMAP_LIST: 310 if((!imap->custom && !imap_matchresp(line, len, "LIST")) || 311 (imap->custom && !imap_matchresp(line, len, imap->custom) && 312 (strcmp(imap->custom, "STORE") || 313 !imap_matchresp(line, len, "FETCH")) && 314 strcmp(imap->custom, "SELECT") && 315 strcmp(imap->custom, "EXAMINE") && 316 strcmp(imap->custom, "SEARCH") && 317 strcmp(imap->custom, "EXPUNGE") && 318 strcmp(imap->custom, "LSUB") && 319 strcmp(imap->custom, "UID") && 320 strcmp(imap->custom, "NOOP"))) 321 return FALSE; 322 break; 323 324 case IMAP_SELECT: 325 /* SELECT is special in that its untagged responses do not have a 326 common prefix so accept anything! */ 327 break; 328 329 case IMAP_FETCH: 330 if(!imap_matchresp(line, len, "FETCH")) 331 return FALSE; 332 break; 333 334 case IMAP_SEARCH: 335 if(!imap_matchresp(line, len, "SEARCH")) 336 return FALSE; 337 break; 338 339 /* Ignore other untagged responses */ 340 default: 341 return FALSE; 342 } 343 344 *resp = '*'; 345 return TRUE; 346 } 347 348 /* Do we have a continuation response? This should be a + symbol followed by 349 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and 350 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but 351 some e-mail servers ignore this and only send a single + instead. */ 352 if((len == 3 && !memcmp("+", line, 1)) || 353 (len >= 2 && !memcmp("+ ", line, 2))) { 354 switch(imapc->state) { 355 /* States which are interested in continuation responses */ 356 case IMAP_AUTHENTICATE_PLAIN: 357 case IMAP_AUTHENTICATE_LOGIN: 358 case IMAP_AUTHENTICATE_LOGIN_PASSWD: 359 case IMAP_AUTHENTICATE_CRAMMD5: 360 case IMAP_AUTHENTICATE_DIGESTMD5: 361 case IMAP_AUTHENTICATE_DIGESTMD5_RESP: 362 case IMAP_AUTHENTICATE_NTLM: 363 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG: 364 case IMAP_AUTHENTICATE_XOAUTH2: 365 case IMAP_AUTHENTICATE_FINAL: 366 case IMAP_APPEND: 367 *resp = '+'; 368 break; 369 370 default: 371 failf(conn->data, "Unexpected continuation response"); 372 *resp = -1; 373 break; 374 } 375 376 return TRUE; 377 } 378 379 return FALSE; /* Nothing for us */ 380} 381 382/*********************************************************************** 383 * 384 * imap_get_message() 385 * 386 * Gets the authentication message from the response buffer. 387 */ 388static void imap_get_message(char *buffer, char** outptr) 389{ 390 size_t len = 0; 391 char* message = NULL; 392 393 /* Find the start of the message */ 394 for(message = buffer + 2; *message == ' ' || *message == '\t'; message++) 395 ; 396 397 /* Find the end of the message */ 398 for(len = strlen(message); len--;) 399 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' && 400 message[len] != '\t') 401 break; 402 403 /* Terminate the message */ 404 if(++len) { 405 message[len] = '\0'; 406 } 407 408 *outptr = message; 409} 410 411/*********************************************************************** 412 * 413 * state() 414 * 415 * This is the ONLY way to change IMAP state! 416 */ 417static void state(struct connectdata *conn, imapstate newstate) 418{ 419 struct imap_conn *imapc = &conn->proto.imapc; 420#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 421 /* for debug purposes */ 422 static const char * const names[]={ 423 "STOP", 424 "SERVERGREET", 425 "CAPABILITY", 426 "STARTTLS", 427 "UPGRADETLS", 428 "AUTHENTICATE_PLAIN", 429 "AUTHENTICATE_LOGIN", 430 "AUTHENTICATE_LOGIN_PASSWD", 431 "AUTHENTICATE_CRAMMD5", 432 "AUTHENTICATE_DIGESTMD5", 433 "AUTHENTICATE_DIGESTMD5_RESP", 434 "AUTHENTICATE_NTLM", 435 "AUTHENTICATE_NTLM_TYPE2MSG", 436 "AUTHENTICATE_XOAUTH2", 437 "AUTHENTICATE_CANCEL", 438 "AUTHENTICATE_FINAL", 439 "LOGIN", 440 "LIST", 441 "SELECT", 442 "FETCH", 443 "FETCH_FINAL", 444 "APPEND", 445 "APPEND_FINAL", 446 "SEARCH", 447 "LOGOUT", 448 /* LAST */ 449 }; 450 451 if(imapc->state != newstate) 452 infof(conn->data, "IMAP %p state change from %s to %s\n", 453 (void *)imapc, names[imapc->state], names[newstate]); 454#endif 455 456 imapc->state = newstate; 457} 458 459/*********************************************************************** 460 * 461 * imap_perform_capability() 462 * 463 * Sends the CAPABILITY command in order to obtain a list of server side 464 * supported capabilities. 465 */ 466static CURLcode imap_perform_capability(struct connectdata *conn) 467{ 468 CURLcode result = CURLE_OK; 469 struct imap_conn *imapc = &conn->proto.imapc; 470 471 imapc->authmechs = 0; /* No known authentication mechanisms yet */ 472 imapc->authused = 0; /* Clear the authentication mechanism used */ 473 imapc->tls_supported = FALSE; /* Clear the TLS capability */ 474 475 /* Send the CAPABILITY command */ 476 result = imap_sendf(conn, "CAPABILITY"); 477 478 if(!result) 479 state(conn, IMAP_CAPABILITY); 480 481 return result; 482} 483 484/*********************************************************************** 485 * 486 * imap_perform_starttls() 487 * 488 * Sends the STARTTLS command to start the upgrade to TLS. 489 */ 490static CURLcode imap_perform_starttls(struct connectdata *conn) 491{ 492 CURLcode result = CURLE_OK; 493 494 /* Send the STARTTLS command */ 495 result = imap_sendf(conn, "STARTTLS"); 496 497 if(!result) 498 state(conn, IMAP_STARTTLS); 499 500 return result; 501} 502 503/*********************************************************************** 504 * 505 * imap_perform_upgrade_tls() 506 * 507 * Performs the upgrade to TLS. 508 */ 509static CURLcode imap_perform_upgrade_tls(struct connectdata *conn) 510{ 511 CURLcode result = CURLE_OK; 512 struct imap_conn *imapc = &conn->proto.imapc; 513 514 /* Start the SSL connection */ 515 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone); 516 517 if(!result) { 518 if(imapc->state != IMAP_UPGRADETLS) 519 state(conn, IMAP_UPGRADETLS); 520 521 if(imapc->ssldone) { 522 imap_to_imaps(conn); 523 result = imap_perform_capability(conn); 524 } 525 } 526 527 return result; 528} 529 530/*********************************************************************** 531 * 532 * imap_perform_login() 533 * 534 * Sends a clear text LOGIN command to authenticate with. 535 */ 536static CURLcode imap_perform_login(struct connectdata *conn) 537{ 538 CURLcode result = CURLE_OK; 539 char *user; 540 char *passwd; 541 542 /* Check we have a username and password to authenticate with and end the 543 connect phase if we don't */ 544 if(!conn->bits.user_passwd) { 545 state(conn, IMAP_STOP); 546 547 return result; 548 } 549 550 /* Make sure the username and password are in the correct atom format */ 551 user = imap_atom(conn->user); 552 passwd = imap_atom(conn->passwd); 553 554 /* Send the LOGIN command */ 555 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "", 556 passwd ? passwd : ""); 557 558 Curl_safefree(user); 559 Curl_safefree(passwd); 560 561 if(!result) 562 state(conn, IMAP_LOGIN); 563 564 return result; 565} 566 567/*********************************************************************** 568 * 569 * imap_perform_authenticate() 570 * 571 * Sends an AUTHENTICATE command allowing the client to login with the given 572 * SASL authentication mechanism. 573 */ 574static CURLcode imap_perform_authenticate(struct connectdata *conn, 575 const char *mech, 576 const char *initresp, 577 imapstate state1, imapstate state2) 578{ 579 CURLcode result = CURLE_OK; 580 581 if(initresp) { 582 /* Send the AUTHENTICATE command with the initial response */ 583 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp); 584 585 if(!result) 586 state(conn, state2); 587 } 588 else { 589 /* Send the AUTHENTICATE command */ 590 result = imap_sendf(conn, "AUTHENTICATE %s", mech); 591 592 if(!result) 593 state(conn, state1); 594 } 595 596 return result; 597} 598 599/*********************************************************************** 600 * 601 * imap_perform_authentication() 602 * 603 * Initiates the authentication sequence, with the appropriate SASL 604 * authentication mechanism, falling back to clear text should a common 605 * mechanism not be available between the client and server. 606 */ 607static CURLcode imap_perform_authentication(struct connectdata *conn) 608{ 609 CURLcode result = CURLE_OK; 610 struct imap_conn *imapc = &conn->proto.imapc; 611 const char *mech = NULL; 612 char *initresp = NULL; 613 size_t len = 0; 614 imapstate state1 = IMAP_STOP; 615 imapstate state2 = IMAP_STOP; 616 617 /* Check we have a username and password to authenticate with and end the 618 connect phase if we don't */ 619 if(!conn->bits.user_passwd) { 620 state(conn, IMAP_STOP); 621 622 return result; 623 } 624 625 /* Calculate the SASL login details */ 626 result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1, 627 &state2); 628 629 if(!result) { 630 if(mech && (imapc->preftype & IMAP_TYPE_SASL)) { 631 /* Perform SASL based authentication */ 632 result = imap_perform_authenticate(conn, mech, initresp, state1, state2); 633 634 Curl_safefree(initresp); 635 } 636 else if((!imapc->login_disabled) && 637 (imapc->preftype & IMAP_TYPE_CLEARTEXT)) 638 /* Perform clear text authentication */ 639 result = imap_perform_login(conn); 640 else { 641 /* Other mechanisms not supported */ 642 infof(conn->data, "No known authentication mechanisms supported!\n"); 643 result = CURLE_LOGIN_DENIED; 644 } 645 } 646 647 return result; 648} 649 650/*********************************************************************** 651 * 652 * imap_perform_list() 653 * 654 * Sends a LIST command or an alternative custom request. 655 */ 656static CURLcode imap_perform_list(struct connectdata *conn) 657{ 658 CURLcode result = CURLE_OK; 659 struct SessionHandle *data = conn->data; 660 struct IMAP *imap = data->req.protop; 661 char *mailbox; 662 663 if(imap->custom) 664 /* Send the custom request */ 665 result = imap_sendf(conn, "%s%s", imap->custom, 666 imap->custom_params ? imap->custom_params : ""); 667 else { 668 /* Make sure the mailbox is in the correct atom format */ 669 mailbox = imap_atom(imap->mailbox ? imap->mailbox : ""); 670 if(!mailbox) 671 return CURLE_OUT_OF_MEMORY; 672 673 /* Send the LIST command */ 674 result = imap_sendf(conn, "LIST \"%s\" *", mailbox); 675 676 Curl_safefree(mailbox); 677 } 678 679 if(!result) 680 state(conn, IMAP_LIST); 681 682 return result; 683} 684 685/*********************************************************************** 686 * 687 * imap_perform_select() 688 * 689 * Sends a SELECT command to ask the server to change the selected mailbox. 690 */ 691static CURLcode imap_perform_select(struct connectdata *conn) 692{ 693 CURLcode result = CURLE_OK; 694 struct SessionHandle *data = conn->data; 695 struct IMAP *imap = data->req.protop; 696 struct imap_conn *imapc = &conn->proto.imapc; 697 char *mailbox; 698 699 /* Invalidate old information as we are switching mailboxes */ 700 Curl_safefree(imapc->mailbox); 701 Curl_safefree(imapc->mailbox_uidvalidity); 702 703 /* Check we have a mailbox */ 704 if(!imap->mailbox) { 705 failf(conn->data, "Cannot SELECT without a mailbox."); 706 return CURLE_URL_MALFORMAT; 707 } 708 709 /* Make sure the mailbox is in the correct atom format */ 710 mailbox = imap_atom(imap->mailbox); 711 if(!mailbox) 712 return CURLE_OUT_OF_MEMORY; 713 714 /* Send the SELECT command */ 715 result = imap_sendf(conn, "SELECT %s", mailbox); 716 717 Curl_safefree(mailbox); 718 719 if(!result) 720 state(conn, IMAP_SELECT); 721 722 return result; 723} 724 725/*********************************************************************** 726 * 727 * imap_perform_fetch() 728 * 729 * Sends a FETCH command to initiate the download of a message. 730 */ 731static CURLcode imap_perform_fetch(struct connectdata *conn) 732{ 733 CURLcode result = CURLE_OK; 734 struct IMAP *imap = conn->data->req.protop; 735 736 /* Check we have a UID */ 737 if(!imap->uid) { 738 failf(conn->data, "Cannot FETCH without a UID."); 739 return CURLE_URL_MALFORMAT; 740 } 741 742 /* Send the FETCH command */ 743 if(imap->partial) 744 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>", 745 imap->uid, 746 imap->section ? imap->section : "", 747 imap->partial); 748 else 749 result = imap_sendf(conn, "FETCH %s BODY[%s]", 750 imap->uid, 751 imap->section ? imap->section : ""); 752 753 if(!result) 754 state(conn, IMAP_FETCH); 755 756 return result; 757} 758 759/*********************************************************************** 760 * 761 * imap_perform_append() 762 * 763 * Sends an APPEND command to initiate the upload of a message. 764 */ 765static CURLcode imap_perform_append(struct connectdata *conn) 766{ 767 CURLcode result = CURLE_OK; 768 struct IMAP *imap = conn->data->req.protop; 769 char *mailbox; 770 771 /* Check we have a mailbox */ 772 if(!imap->mailbox) { 773 failf(conn->data, "Cannot APPEND without a mailbox."); 774 return CURLE_URL_MALFORMAT; 775 } 776 777 /* Check we know the size of the upload */ 778 if(conn->data->state.infilesize < 0) { 779 failf(conn->data, "Cannot APPEND with unknown input file size\n"); 780 return CURLE_UPLOAD_FAILED; 781 } 782 783 /* Make sure the mailbox is in the correct atom format */ 784 mailbox = imap_atom(imap->mailbox); 785 if(!mailbox) 786 return CURLE_OUT_OF_MEMORY; 787 788 /* Send the APPEND command */ 789 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}", 790 mailbox, conn->data->state.infilesize); 791 792 Curl_safefree(mailbox); 793 794 if(!result) 795 state(conn, IMAP_APPEND); 796 797 return result; 798} 799 800/*********************************************************************** 801 * 802 * imap_perform_search() 803 * 804 * Sends a SEARCH command. 805 */ 806static CURLcode imap_perform_search(struct connectdata *conn) 807{ 808 CURLcode result = CURLE_OK; 809 struct IMAP *imap = conn->data->req.protop; 810 811 /* Check we have a query string */ 812 if(!imap->query) { 813 failf(conn->data, "Cannot SEARCH without a query string."); 814 return CURLE_URL_MALFORMAT; 815 } 816 817 /* Send the SEARCH command */ 818 result = imap_sendf(conn, "SEARCH %s", imap->query); 819 820 if(!result) 821 state(conn, IMAP_SEARCH); 822 823 return result; 824} 825 826/*********************************************************************** 827 * 828 * imap_perform_logout() 829 * 830 * Performs the logout action prior to sclose() being called. 831 */ 832static CURLcode imap_perform_logout(struct connectdata *conn) 833{ 834 CURLcode result = CURLE_OK; 835 836 /* Send the LOGOUT command */ 837 result = imap_sendf(conn, "LOGOUT"); 838 839 if(!result) 840 state(conn, IMAP_LOGOUT); 841 842 return result; 843} 844 845/* For the initial server greeting */ 846static CURLcode imap_state_servergreet_resp(struct connectdata *conn, 847 int imapcode, 848 imapstate instate) 849{ 850 CURLcode result = CURLE_OK; 851 struct SessionHandle *data = conn->data; 852 853 (void)instate; /* no use for this yet */ 854 855 if(imapcode != 'O') { 856 failf(data, "Got unexpected imap-server response"); 857 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */ 858 } 859 else 860 result = imap_perform_capability(conn); 861 862 return result; 863} 864 865/* For CAPABILITY responses */ 866static CURLcode imap_state_capability_resp(struct connectdata *conn, 867 int imapcode, 868 imapstate instate) 869{ 870 CURLcode result = CURLE_OK; 871 struct SessionHandle *data = conn->data; 872 struct imap_conn *imapc = &conn->proto.imapc; 873 const char *line = data->state.buffer; 874 size_t wordlen; 875 876 (void)instate; /* no use for this yet */ 877 878 /* Do we have a untagged response? */ 879 if(imapcode == '*') { 880 line += 2; 881 882 /* Loop through the data line */ 883 for(;;) { 884 while(*line && 885 (*line == ' ' || *line == '\t' || 886 *line == '\r' || *line == '\n')) { 887 888 line++; 889 } 890 891 if(!*line) 892 break; 893 894 /* Extract the word */ 895 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' && 896 line[wordlen] != '\t' && line[wordlen] != '\r' && 897 line[wordlen] != '\n';) 898 wordlen++; 899 900 /* Does the server support the STARTTLS capability? */ 901 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8)) 902 imapc->tls_supported = TRUE; 903 904 /* Has the server explicitly disabled clear text authentication? */ 905 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13)) 906 imapc->login_disabled = TRUE; 907 908 /* Does the server support the SASL-IR capability? */ 909 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7)) 910 imapc->ir_supported = TRUE; 911 912 /* Do we have a SASL based authentication mechanism? */ 913 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) { 914 line += 5; 915 wordlen -= 5; 916 917 /* Test the word for a matching authentication mechanism */ 918 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN)) 919 imapc->authmechs |= SASL_MECH_LOGIN; 920 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN)) 921 imapc->authmechs |= SASL_MECH_PLAIN; 922 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5)) 923 imapc->authmechs |= SASL_MECH_CRAM_MD5; 924 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5)) 925 imapc->authmechs |= SASL_MECH_DIGEST_MD5; 926 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI)) 927 imapc->authmechs |= SASL_MECH_GSSAPI; 928 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL)) 929 imapc->authmechs |= SASL_MECH_EXTERNAL; 930 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM)) 931 imapc->authmechs |= SASL_MECH_NTLM; 932 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2)) 933 imapc->authmechs |= SASL_MECH_XOAUTH2; 934 } 935 936 line += wordlen; 937 } 938 } 939 else if(imapcode == 'O') { 940 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) { 941 /* We don't have a SSL/TLS connection yet, but SSL is requested */ 942 if(imapc->tls_supported) 943 /* Switch to TLS connection now */ 944 result = imap_perform_starttls(conn); 945 else if(data->set.use_ssl == CURLUSESSL_TRY) 946 /* Fallback and carry on with authentication */ 947 result = imap_perform_authentication(conn); 948 else { 949 failf(data, "STARTTLS not supported."); 950 result = CURLE_USE_SSL_FAILED; 951 } 952 } 953 else 954 result = imap_perform_authentication(conn); 955 } 956 else 957 result = imap_perform_authentication(conn); 958 959 return result; 960} 961 962/* For STARTTLS responses */ 963static CURLcode imap_state_starttls_resp(struct connectdata *conn, 964 int imapcode, 965 imapstate instate) 966{ 967 CURLcode result = CURLE_OK; 968 struct SessionHandle *data = conn->data; 969 970 (void)instate; /* no use for this yet */ 971 972 if(imapcode != 'O') { 973 if(data->set.use_ssl != CURLUSESSL_TRY) { 974 failf(data, "STARTTLS denied. %c", imapcode); 975 result = CURLE_USE_SSL_FAILED; 976 } 977 else 978 result = imap_perform_authentication(conn); 979 } 980 else 981 result = imap_perform_upgrade_tls(conn); 982 983 return result; 984} 985 986/* For AUTHENTICATE PLAIN (without initial response) responses */ 987static CURLcode imap_state_auth_plain_resp(struct connectdata *conn, 988 int imapcode, 989 imapstate instate) 990{ 991 CURLcode result = CURLE_OK; 992 struct SessionHandle *data = conn->data; 993 size_t len = 0; 994 char *plainauth = NULL; 995 996 (void)instate; /* no use for this yet */ 997 998 if(imapcode != '+') { 999 failf(data, "Access denied. %c", imapcode); 1000 result = CURLE_LOGIN_DENIED; 1001 } 1002 else { 1003 /* Create the authorisation message */ 1004 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, 1005 &plainauth, &len); 1006 if(!result && plainauth) { 1007 /* Send the message */ 1008 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth); 1009 1010 if(!result) 1011 state(conn, IMAP_AUTHENTICATE_FINAL); 1012 } 1013 } 1014 1015 Curl_safefree(plainauth); 1016 1017 return result; 1018} 1019 1020/* For AUTHENTICATE LOGIN (without initial response) responses */ 1021static CURLcode imap_state_auth_login_resp(struct connectdata *conn, 1022 int imapcode, 1023 imapstate instate) 1024{ 1025 CURLcode result = CURLE_OK; 1026 struct SessionHandle *data = conn->data; 1027 size_t len = 0; 1028 char *authuser = NULL; 1029 1030 (void)instate; /* no use for this yet */ 1031 1032 if(imapcode != '+') { 1033 failf(data, "Access denied: %d", imapcode); 1034 result = CURLE_LOGIN_DENIED; 1035 } 1036 else { 1037 /* Create the user message */ 1038 result = Curl_sasl_create_login_message(data, conn->user, 1039 &authuser, &len); 1040 if(!result && authuser) { 1041 /* Send the user */ 1042 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser); 1043 1044 if(!result) 1045 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD); 1046 } 1047 } 1048 1049 Curl_safefree(authuser); 1050 1051 return result; 1052} 1053 1054/* For AUTHENTICATE LOGIN user entry responses */ 1055static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn, 1056 int imapcode, 1057 imapstate instate) 1058{ 1059 CURLcode result = CURLE_OK; 1060 struct SessionHandle *data = conn->data; 1061 size_t len = 0; 1062 char *authpasswd = NULL; 1063 1064 (void)instate; /* no use for this yet */ 1065 1066 if(imapcode != '+') { 1067 failf(data, "Access denied: %d", imapcode); 1068 result = CURLE_LOGIN_DENIED; 1069 } 1070 else { 1071 /* Create the password message */ 1072 result = Curl_sasl_create_login_message(data, conn->passwd, 1073 &authpasswd, &len); 1074 if(!result && authpasswd) { 1075 /* Send the password */ 1076 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd); 1077 1078 if(!result) 1079 state(conn, IMAP_AUTHENTICATE_FINAL); 1080 } 1081 } 1082 1083 Curl_safefree(authpasswd); 1084 1085 return result; 1086} 1087 1088#ifndef CURL_DISABLE_CRYPTO_AUTH 1089/* For AUTHENTICATE CRAM-MD5 responses */ 1090static CURLcode imap_state_auth_cram_resp(struct connectdata *conn, 1091 int imapcode, 1092 imapstate instate) 1093{ 1094 CURLcode result = CURLE_OK; 1095 struct SessionHandle *data = conn->data; 1096 char *chlg = NULL; 1097 char *chlg64 = NULL; 1098 char *rplyb64 = NULL; 1099 size_t len = 0; 1100 1101 (void)instate; /* no use for this yet */ 1102 1103 if(imapcode != '+') { 1104 failf(data, "Access denied: %d", imapcode); 1105 return CURLE_LOGIN_DENIED; 1106 } 1107 1108 /* Get the challenge message */ 1109 imap_get_message(data->state.buffer, &chlg64); 1110 1111 /* Decode the challenge message */ 1112 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len); 1113 if(result) { 1114 /* Send the cancellation */ 1115 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*"); 1116 1117 if(!result) 1118 state(conn, IMAP_AUTHENTICATE_CANCEL); 1119 } 1120 else { 1121 /* Create the response message */ 1122 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user, 1123 conn->passwd, &rplyb64, &len); 1124 if(!result && rplyb64) { 1125 /* Send the response */ 1126 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64); 1127 1128 if(!result) 1129 state(conn, IMAP_AUTHENTICATE_FINAL); 1130 } 1131 } 1132 1133 Curl_safefree(chlg); 1134 Curl_safefree(rplyb64); 1135 1136 return result; 1137} 1138 1139/* For AUTHENTICATE DIGEST-MD5 challenge responses */ 1140static CURLcode imap_state_auth_digest_resp(struct connectdata *conn, 1141 int imapcode, 1142 imapstate instate) 1143{ 1144 CURLcode result = CURLE_OK; 1145 struct SessionHandle *data = conn->data; 1146 char *chlg64 = NULL; 1147 char *rplyb64 = NULL; 1148 size_t len = 0; 1149 1150 (void)instate; /* no use for this yet */ 1151 1152 if(imapcode != '+') { 1153 failf(data, "Access denied: %d", imapcode); 1154 return CURLE_LOGIN_DENIED; 1155 } 1156 1157 /* Get the challenge message */ 1158 imap_get_message(data->state.buffer, &chlg64); 1159 1160 /* Create the response message */ 1161 result = Curl_sasl_create_digest_md5_message(data, chlg64, 1162 conn->user, conn->passwd, 1163 "imap", &rplyb64, &len); 1164 if(result) { 1165 if(result == CURLE_BAD_CONTENT_ENCODING) { 1166 /* Send the cancellation */ 1167 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*"); 1168 1169 if(!result) 1170 state(conn, IMAP_AUTHENTICATE_CANCEL); 1171 } 1172 } 1173 else { 1174 /* Send the response */ 1175 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64); 1176 1177 if(!result) 1178 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP); 1179 } 1180 1181 Curl_safefree(rplyb64); 1182 1183 return result; 1184} 1185 1186/* For AUTHENTICATE DIGEST-MD5 challenge-response responses */ 1187static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn, 1188 int imapcode, 1189 imapstate instate) 1190{ 1191 CURLcode result = CURLE_OK; 1192 struct SessionHandle *data = conn->data; 1193 1194 (void)instate; /* no use for this yet */ 1195 1196 if(imapcode != '+') { 1197 failf(data, "Authentication failed: %d", imapcode); 1198 result = CURLE_LOGIN_DENIED; 1199 } 1200 else { 1201 /* Send an empty response */ 1202 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", ""); 1203 1204 if(!result) 1205 state(conn, IMAP_AUTHENTICATE_FINAL); 1206 } 1207 1208 return result; 1209} 1210#endif 1211 1212#ifdef USE_NTLM 1213/* For AUTHENTICATE NTLM (without initial response) responses */ 1214static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn, 1215 int imapcode, 1216 imapstate instate) 1217{ 1218 CURLcode result = CURLE_OK; 1219 struct SessionHandle *data = conn->data; 1220 size_t len = 0; 1221 char *type1msg = NULL; 1222 1223 (void)instate; /* no use for this yet */ 1224 1225 if(imapcode != '+') { 1226 failf(data, "Access denied: %d", imapcode); 1227 result = CURLE_LOGIN_DENIED; 1228 } 1229 else { 1230 /* Create the type-1 message */ 1231 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, 1232 &conn->ntlm, 1233 &type1msg, &len); 1234 if(!result && type1msg) { 1235 /* Send the message */ 1236 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg); 1237 1238 if(!result) 1239 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG); 1240 } 1241 } 1242 1243 Curl_safefree(type1msg); 1244 1245 return result; 1246} 1247 1248/* For NTLM type-2 responses (sent in reponse to our type-1 message) */ 1249static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn, 1250 int imapcode, 1251 imapstate instate) 1252{ 1253 CURLcode result = CURLE_OK; 1254 struct SessionHandle *data = conn->data; 1255 char *type2msg = NULL; 1256 char *type3msg = NULL; 1257 size_t len = 0; 1258 1259 (void)instate; /* no use for this yet */ 1260 1261 if(imapcode != '+') { 1262 failf(data, "Access denied: %d", imapcode); 1263 result = CURLE_LOGIN_DENIED; 1264 } 1265 else { 1266 /* Get the challenge message */ 1267 imap_get_message(data->state.buffer, &type2msg); 1268 1269 /* Decode the type-2 message */ 1270 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm); 1271 if(result) { 1272 /* Send the cancellation */ 1273 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*"); 1274 1275 if(!result) 1276 state(conn, IMAP_AUTHENTICATE_CANCEL); 1277 } 1278 else { 1279 /* Create the type-3 message */ 1280 result = Curl_sasl_create_ntlm_type3_message(data, conn->user, 1281 conn->passwd, &conn->ntlm, 1282 &type3msg, &len); 1283 if(!result && type3msg) { 1284 /* Send the message */ 1285 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg); 1286 1287 if(!result) 1288 state(conn, IMAP_AUTHENTICATE_FINAL); 1289 } 1290 } 1291 } 1292 1293 Curl_safefree(type3msg); 1294 1295 return result; 1296} 1297#endif 1298 1299/* For AUTHENTICATE XOAUTH2 (without initial response) responses */ 1300static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn, 1301 int imapcode, 1302 imapstate instate) 1303{ 1304 CURLcode result = CURLE_OK; 1305 struct SessionHandle *data = conn->data; 1306 size_t len = 0; 1307 char *xoauth = NULL; 1308 1309 (void)instate; /* no use for this yet */ 1310 1311 if(imapcode != '+') { 1312 failf(data, "Access denied: %d", imapcode); 1313 result = CURLE_LOGIN_DENIED; 1314 } 1315 else { 1316 /* Create the authorisation message */ 1317 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user, 1318 conn->xoauth2_bearer, 1319 &xoauth, &len); 1320 if(!result && xoauth) { 1321 /* Send the message */ 1322 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth); 1323 1324 if(!result) 1325 state(conn, IMAP_AUTHENTICATE_FINAL); 1326 } 1327 } 1328 1329 Curl_safefree(xoauth); 1330 1331 return result; 1332} 1333 1334/* For AUTHENTICATE cancellation responses */ 1335static CURLcode imap_state_auth_cancel_resp(struct connectdata *conn, 1336 int imapcode, 1337 imapstate instate) 1338{ 1339 CURLcode result = CURLE_OK; 1340 struct SessionHandle *data = conn->data; 1341 struct imap_conn *imapc = &conn->proto.imapc; 1342 const char *mech = NULL; 1343 char *initresp = NULL; 1344 size_t len = 0; 1345 imapstate state1 = IMAP_STOP; 1346 imapstate state2 = IMAP_STOP; 1347 1348 (void)imapcode; 1349 (void)instate; /* no use for this yet */ 1350 1351 /* Remove the offending mechanism from the supported list */ 1352 imapc->authmechs ^= imapc->authused; 1353 1354 /* Calculate alternative SASL login details */ 1355 result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1, 1356 &state2); 1357 1358 if(!result) { 1359 /* Do we have any mechanisms left or can we fallback to clear text? */ 1360 if(mech) { 1361 /* Retry SASL based authentication */ 1362 result = imap_perform_authenticate(conn, mech, initresp, state1, state2); 1363 1364 Curl_safefree(initresp); 1365 } 1366 else if((!imapc->login_disabled) && 1367 (imapc->preftype & IMAP_TYPE_CLEARTEXT)) 1368 /* Perform clear text authentication */ 1369 result = imap_perform_login(conn); 1370 else { 1371 failf(data, "Authentication cancelled"); 1372 1373 result = CURLE_LOGIN_DENIED; 1374 } 1375 } 1376 1377 return result; 1378} 1379 1380/* For final responses in the AUTHENTICATE sequence */ 1381static CURLcode imap_state_auth_final_resp(struct connectdata *conn, 1382 int imapcode, 1383 imapstate instate) 1384{ 1385 CURLcode result = CURLE_OK; 1386 struct SessionHandle *data = conn->data; 1387 1388 (void)instate; /* no use for this yet */ 1389 1390 if(imapcode != 'O') { 1391 failf(data, "Authentication failed: %d", imapcode); 1392 result = CURLE_LOGIN_DENIED; 1393 } 1394 else 1395 /* End of connect phase */ 1396 state(conn, IMAP_STOP); 1397 1398 return result; 1399} 1400 1401/* For LOGIN responses */ 1402static CURLcode imap_state_login_resp(struct connectdata *conn, 1403 int imapcode, 1404 imapstate instate) 1405{ 1406 CURLcode result = CURLE_OK; 1407 struct SessionHandle *data = conn->data; 1408 1409 (void)instate; /* no use for this yet */ 1410 1411 if(imapcode != 'O') { 1412 failf(data, "Access denied. %c", imapcode); 1413 result = CURLE_LOGIN_DENIED; 1414 } 1415 else 1416 /* End of connect phase */ 1417 state(conn, IMAP_STOP); 1418 1419 return result; 1420} 1421 1422/* For LIST responses */ 1423static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode, 1424 imapstate instate) 1425{ 1426 CURLcode result = CURLE_OK; 1427 char *line = conn->data->state.buffer; 1428 size_t len = strlen(line); 1429 1430 (void)instate; /* No use for this yet */ 1431 1432 if(imapcode == '*') { 1433 /* Temporarily add the LF character back and send as body to the client */ 1434 line[len] = '\n'; 1435 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1); 1436 line[len] = '\0'; 1437 } 1438 else if(imapcode != 'O') 1439 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */ 1440 else 1441 /* End of DO phase */ 1442 state(conn, IMAP_STOP); 1443 1444 return result; 1445} 1446 1447/* For SELECT responses */ 1448static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode, 1449 imapstate instate) 1450{ 1451 CURLcode result = CURLE_OK; 1452 struct SessionHandle *data = conn->data; 1453 struct IMAP *imap = conn->data->req.protop; 1454 struct imap_conn *imapc = &conn->proto.imapc; 1455 const char *line = data->state.buffer; 1456 char tmp[20]; 1457 1458 (void)instate; /* no use for this yet */ 1459 1460 if(imapcode == '*') { 1461 /* See if this is an UIDVALIDITY response */ 1462 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) { 1463 Curl_safefree(imapc->mailbox_uidvalidity); 1464 imapc->mailbox_uidvalidity = strdup(tmp); 1465 } 1466 } 1467 else if(imapcode == 'O') { 1468 /* Check if the UIDVALIDITY has been specified and matches */ 1469 if(imap->uidvalidity && imapc->mailbox_uidvalidity && 1470 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) { 1471 failf(conn->data, "Mailbox UIDVALIDITY has changed"); 1472 result = CURLE_REMOTE_FILE_NOT_FOUND; 1473 } 1474 else { 1475 /* Note the currently opened mailbox on this connection */ 1476 imapc->mailbox = strdup(imap->mailbox); 1477 1478 if(imap->custom) 1479 result = imap_perform_list(conn); 1480 else if(imap->query) 1481 result = imap_perform_search(conn); 1482 else 1483 result = imap_perform_fetch(conn); 1484 } 1485 } 1486 else { 1487 failf(data, "Select failed"); 1488 result = CURLE_LOGIN_DENIED; 1489 } 1490 1491 return result; 1492} 1493 1494/* For the (first line of the) FETCH responses */ 1495static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode, 1496 imapstate instate) 1497{ 1498 CURLcode result = CURLE_OK; 1499 struct SessionHandle *data = conn->data; 1500 struct imap_conn *imapc = &conn->proto.imapc; 1501 struct pingpong *pp = &imapc->pp; 1502 const char *ptr = data->state.buffer; 1503 bool parsed = FALSE; 1504 curl_off_t size; 1505 1506 (void)instate; /* no use for this yet */ 1507 1508 if(imapcode != '*') { 1509 Curl_pgrsSetDownloadSize(data, 0); 1510 state(conn, IMAP_STOP); 1511 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */ 1512 } 1513 1514 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse 1515 the continuation data contained within the curly brackets */ 1516 while(*ptr && (*ptr != '{')) 1517 ptr++; 1518 1519 if(*ptr == '{') { 1520 char *endptr; 1521 size = curlx_strtoofft(ptr + 1, &endptr, 10); 1522 if(endptr - ptr > 1 && endptr[0] == '}' && 1523 endptr[1] == '\r' && endptr[2] == '\0') 1524 parsed = TRUE; 1525 } 1526 1527 if(parsed) { 1528 infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n", 1529 size); 1530 Curl_pgrsSetDownloadSize(data, size); 1531 1532 if(pp->cache) { 1533 /* At this point there is a bunch of data in the header "cache" that is 1534 actually body content, send it as body and then skip it. Do note 1535 that there may even be additional "headers" after the body. */ 1536 size_t chunk = pp->cache_size; 1537 1538 if(chunk > (size_t)size) 1539 /* The conversion from curl_off_t to size_t is always fine here */ 1540 chunk = (size_t)size; 1541 1542 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk); 1543 if(result) 1544 return result; 1545 1546 data->req.bytecount += chunk; 1547 1548 infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU 1549 " bytes, %" CURL_FORMAT_CURL_OFF_TU 1550 " bytes are left for transfer\n", (curl_off_t)chunk, 1551 size - chunk); 1552 1553 /* Have we used the entire cache or just part of it?*/ 1554 if(pp->cache_size > chunk) { 1555 /* Only part of it so shrink the cache to fit the trailing data */ 1556 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk); 1557 pp->cache_size -= chunk; 1558 } 1559 else { 1560 /* Free the cache */ 1561 Curl_safefree(pp->cache); 1562 1563 /* Reset the cache size */ 1564 pp->cache_size = 0; 1565 } 1566 } 1567 1568 if(data->req.bytecount == size) 1569 /* The entire data is already transferred! */ 1570 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); 1571 else { 1572 /* IMAP download */ 1573 data->req.maxdownload = size; 1574 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL); 1575 } 1576 } 1577 else { 1578 /* We don't know how to parse this line */ 1579 failf(pp->conn->data, "Failed to parse FETCH response."); 1580 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */ 1581 } 1582 1583 /* End of DO phase */ 1584 state(conn, IMAP_STOP); 1585 1586 return result; 1587} 1588 1589/* For final FETCH responses performed after the download */ 1590static CURLcode imap_state_fetch_final_resp(struct connectdata *conn, 1591 int imapcode, 1592 imapstate instate) 1593{ 1594 CURLcode result = CURLE_OK; 1595 1596 (void)instate; /* No use for this yet */ 1597 1598 if(imapcode != 'O') 1599 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */ 1600 else 1601 /* End of DONE phase */ 1602 state(conn, IMAP_STOP); 1603 1604 return result; 1605} 1606 1607/* For APPEND responses */ 1608static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode, 1609 imapstate instate) 1610{ 1611 CURLcode result = CURLE_OK; 1612 struct SessionHandle *data = conn->data; 1613 1614 (void)instate; /* No use for this yet */ 1615 1616 if(imapcode != '+') { 1617 result = CURLE_UPLOAD_FAILED; 1618 } 1619 else { 1620 /* Set the progress upload size */ 1621 Curl_pgrsSetUploadSize(data, data->state.infilesize); 1622 1623 /* IMAP upload */ 1624 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); 1625 1626 /* End of DO phase */ 1627 state(conn, IMAP_STOP); 1628 } 1629 1630 return result; 1631} 1632 1633/* For final APPEND responses performed after the upload */ 1634static CURLcode imap_state_append_final_resp(struct connectdata *conn, 1635 int imapcode, 1636 imapstate instate) 1637{ 1638 CURLcode result = CURLE_OK; 1639 1640 (void)instate; /* No use for this yet */ 1641 1642 if(imapcode != 'O') 1643 result = CURLE_UPLOAD_FAILED; 1644 else 1645 /* End of DONE phase */ 1646 state(conn, IMAP_STOP); 1647 1648 return result; 1649} 1650 1651/* For SEARCH responses */ 1652static CURLcode imap_state_search_resp(struct connectdata *conn, int imapcode, 1653 imapstate instate) 1654{ 1655 CURLcode result = CURLE_OK; 1656 char *line = conn->data->state.buffer; 1657 size_t len = strlen(line); 1658 1659 (void)instate; /* No use for this yet */ 1660 1661 if(imapcode == '*') { 1662 /* Temporarily add the LF character back and send as body to the client */ 1663 line[len] = '\n'; 1664 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1); 1665 line[len] = '\0'; 1666 } 1667 else if(imapcode != 'O') 1668 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */ 1669 else 1670 /* End of DO phase */ 1671 state(conn, IMAP_STOP); 1672 1673 return result; 1674} 1675 1676static CURLcode imap_statemach_act(struct connectdata *conn) 1677{ 1678 CURLcode result = CURLE_OK; 1679 curl_socket_t sock = conn->sock[FIRSTSOCKET]; 1680 int imapcode; 1681 struct imap_conn *imapc = &conn->proto.imapc; 1682 struct pingpong *pp = &imapc->pp; 1683 size_t nread = 0; 1684 1685 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */ 1686 if(imapc->state == IMAP_UPGRADETLS) 1687 return imap_perform_upgrade_tls(conn); 1688 1689 /* Flush any data that needs to be sent */ 1690 if(pp->sendleft) 1691 return Curl_pp_flushsend(pp); 1692 1693 do { 1694 /* Read the response from the server */ 1695 result = Curl_pp_readresp(sock, pp, &imapcode, &nread); 1696 if(result) 1697 return result; 1698 1699 /* Was there an error parsing the response line? */ 1700 if(imapcode == -1) 1701 return CURLE_FTP_WEIRD_SERVER_REPLY; 1702 1703 if(!imapcode) 1704 break; 1705 1706 /* We have now received a full IMAP server response */ 1707 switch(imapc->state) { 1708 case IMAP_SERVERGREET: 1709 result = imap_state_servergreet_resp(conn, imapcode, imapc->state); 1710 break; 1711 1712 case IMAP_CAPABILITY: 1713 result = imap_state_capability_resp(conn, imapcode, imapc->state); 1714 break; 1715 1716 case IMAP_STARTTLS: 1717 result = imap_state_starttls_resp(conn, imapcode, imapc->state); 1718 break; 1719 1720 case IMAP_AUTHENTICATE_PLAIN: 1721 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state); 1722 break; 1723 1724 case IMAP_AUTHENTICATE_LOGIN: 1725 result = imap_state_auth_login_resp(conn, imapcode, imapc->state); 1726 break; 1727 1728 case IMAP_AUTHENTICATE_LOGIN_PASSWD: 1729 result = imap_state_auth_login_password_resp(conn, imapcode, 1730 imapc->state); 1731 break; 1732 1733#ifndef CURL_DISABLE_CRYPTO_AUTH 1734 case IMAP_AUTHENTICATE_CRAMMD5: 1735 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state); 1736 break; 1737 1738 case IMAP_AUTHENTICATE_DIGESTMD5: 1739 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state); 1740 break; 1741 1742 case IMAP_AUTHENTICATE_DIGESTMD5_RESP: 1743 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state); 1744 break; 1745#endif 1746 1747#ifdef USE_NTLM 1748 case IMAP_AUTHENTICATE_NTLM: 1749 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state); 1750 break; 1751 1752 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG: 1753 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode, 1754 imapc->state); 1755 break; 1756#endif 1757 1758 case IMAP_AUTHENTICATE_XOAUTH2: 1759 result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state); 1760 break; 1761 1762 case IMAP_AUTHENTICATE_CANCEL: 1763 result = imap_state_auth_cancel_resp(conn, imapcode, imapc->state); 1764 break; 1765 1766 case IMAP_AUTHENTICATE_FINAL: 1767 result = imap_state_auth_final_resp(conn, imapcode, imapc->state); 1768 break; 1769 1770 case IMAP_LOGIN: 1771 result = imap_state_login_resp(conn, imapcode, imapc->state); 1772 break; 1773 1774 case IMAP_LIST: 1775 result = imap_state_list_resp(conn, imapcode, imapc->state); 1776 break; 1777 1778 case IMAP_SELECT: 1779 result = imap_state_select_resp(conn, imapcode, imapc->state); 1780 break; 1781 1782 case IMAP_FETCH: 1783 result = imap_state_fetch_resp(conn, imapcode, imapc->state); 1784 break; 1785 1786 case IMAP_FETCH_FINAL: 1787 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state); 1788 break; 1789 1790 case IMAP_APPEND: 1791 result = imap_state_append_resp(conn, imapcode, imapc->state); 1792 break; 1793 1794 case IMAP_APPEND_FINAL: 1795 result = imap_state_append_final_resp(conn, imapcode, imapc->state); 1796 break; 1797 1798 case IMAP_SEARCH: 1799 result = imap_state_search_resp(conn, imapcode, imapc->state); 1800 break; 1801 1802 case IMAP_LOGOUT: 1803 /* fallthrough, just stop! */ 1804 default: 1805 /* internal error */ 1806 state(conn, IMAP_STOP); 1807 break; 1808 } 1809 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp)); 1810 1811 return result; 1812} 1813 1814/* Called repeatedly until done from multi.c */ 1815static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done) 1816{ 1817 CURLcode result = CURLE_OK; 1818 struct imap_conn *imapc = &conn->proto.imapc; 1819 1820 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) { 1821 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone); 1822 if(result || !imapc->ssldone) 1823 return result; 1824 } 1825 1826 result = Curl_pp_statemach(&imapc->pp, FALSE); 1827 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE; 1828 1829 return result; 1830} 1831 1832static CURLcode imap_block_statemach(struct connectdata *conn) 1833{ 1834 CURLcode result = CURLE_OK; 1835 struct imap_conn *imapc = &conn->proto.imapc; 1836 1837 while(imapc->state != IMAP_STOP && !result) 1838 result = Curl_pp_statemach(&imapc->pp, TRUE); 1839 1840 return result; 1841} 1842 1843/* Allocate and initialize the struct IMAP for the current SessionHandle if 1844 required */ 1845static CURLcode imap_init(struct connectdata *conn) 1846{ 1847 CURLcode result = CURLE_OK; 1848 struct SessionHandle *data = conn->data; 1849 struct IMAP *imap; 1850 1851 imap = data->req.protop = calloc(sizeof(struct IMAP), 1); 1852 if(!imap) 1853 result = CURLE_OUT_OF_MEMORY; 1854 1855 return result; 1856} 1857 1858/* For the IMAP "protocol connect" and "doing" phases only */ 1859static int imap_getsock(struct connectdata *conn, curl_socket_t *socks, 1860 int numsocks) 1861{ 1862 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks); 1863} 1864 1865/*********************************************************************** 1866 * 1867 * imap_connect() 1868 * 1869 * This function should do everything that is to be considered a part of the 1870 * connection phase. 1871 * 1872 * The variable 'done' points to will be TRUE if the protocol-layer connect 1873 * phase is done when this function returns, or FALSE if not. 1874 */ 1875static CURLcode imap_connect(struct connectdata *conn, bool *done) 1876{ 1877 CURLcode result = CURLE_OK; 1878 struct imap_conn *imapc = &conn->proto.imapc; 1879 struct pingpong *pp = &imapc->pp; 1880 1881 *done = FALSE; /* default to not done yet */ 1882 1883 /* We always support persistent connections in IMAP */ 1884 connkeep(conn, "IMAP default"); 1885 1886 /* Set the default response time-out */ 1887 pp->response_time = RESP_TIMEOUT; 1888 pp->statemach_act = imap_statemach_act; 1889 pp->endofresp = imap_endofresp; 1890 pp->conn = conn; 1891 1892 /* Set the default preferred authentication type and mechanism */ 1893 imapc->preftype = IMAP_TYPE_ANY; 1894 imapc->prefmech = SASL_AUTH_ANY; 1895 1896 /* Initialise the pingpong layer */ 1897 Curl_pp_init(pp); 1898 1899 /* Parse the URL options */ 1900 result = imap_parse_url_options(conn); 1901 if(result) 1902 return result; 1903 1904 /* Start off waiting for the server greeting response */ 1905 state(conn, IMAP_SERVERGREET); 1906 1907 /* Start off with an response id of '*' */ 1908 strcpy(imapc->resptag, "*"); 1909 1910 result = imap_multi_statemach(conn, done); 1911 1912 return result; 1913} 1914 1915/*********************************************************************** 1916 * 1917 * imap_done() 1918 * 1919 * The DONE function. This does what needs to be done after a single DO has 1920 * performed. 1921 * 1922 * Input argument is already checked for validity. 1923 */ 1924static CURLcode imap_done(struct connectdata *conn, CURLcode status, 1925 bool premature) 1926{ 1927 CURLcode result = CURLE_OK; 1928 struct SessionHandle *data = conn->data; 1929 struct IMAP *imap = data->req.protop; 1930 1931 (void)premature; 1932 1933 if(!imap) 1934 /* When the easy handle is removed from the multi interface while libcurl 1935 is still trying to resolve the host name, the IMAP struct is not yet 1936 initialized. However, the removal action calls Curl_done() which in 1937 turn calls this function, so we simply return success. */ 1938 return CURLE_OK; 1939 1940 if(status) { 1941 connclose(conn, "IMAP done with bad status"); /* marked for closure */ 1942 result = status; /* use the already set error code */ 1943 } 1944 else if(!data->set.connect_only && !imap->custom && 1945 (imap->uid || data->set.upload)) { 1946 /* Handle responses after FETCH or APPEND transfer has finished */ 1947 if(!data->set.upload) 1948 state(conn, IMAP_FETCH_FINAL); 1949 else { 1950 /* End the APPEND command first by sending an empty line */ 1951 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", ""); 1952 if(!result) 1953 state(conn, IMAP_APPEND_FINAL); 1954 } 1955 1956 /* Run the state-machine 1957 1958 TODO: when the multi interface is used, this _really_ should be using 1959 the imap_multi_statemach function but we have no general support for 1960 non-blocking DONE operations, not in the multi state machine and with 1961 Curl_done() invokes on several places in the code! 1962 */ 1963 if(!result) 1964 result = imap_block_statemach(conn); 1965 } 1966 1967 /* Cleanup our per-request based variables */ 1968 Curl_safefree(imap->mailbox); 1969 Curl_safefree(imap->uidvalidity); 1970 Curl_safefree(imap->uid); 1971 Curl_safefree(imap->section); 1972 Curl_safefree(imap->partial); 1973 Curl_safefree(imap->query); 1974 Curl_safefree(imap->custom); 1975 Curl_safefree(imap->custom_params); 1976 1977 /* Clear the transfer mode for the next request */ 1978 imap->transfer = FTPTRANSFER_BODY; 1979 1980 return result; 1981} 1982 1983/*********************************************************************** 1984 * 1985 * imap_perform() 1986 * 1987 * This is the actual DO function for IMAP. Fetch or append a message, or do 1988 * other things according to the options previously setup. 1989 */ 1990static CURLcode imap_perform(struct connectdata *conn, bool *connected, 1991 bool *dophase_done) 1992{ 1993 /* This is IMAP and no proxy */ 1994 CURLcode result = CURLE_OK; 1995 struct SessionHandle *data = conn->data; 1996 struct IMAP *imap = data->req.protop; 1997 struct imap_conn *imapc = &conn->proto.imapc; 1998 bool selected = FALSE; 1999 2000 DEBUGF(infof(conn->data, "DO phase starts\n")); 2001 2002 if(conn->data->set.opt_no_body) { 2003 /* Requested no body means no transfer */ 2004 imap->transfer = FTPTRANSFER_INFO; 2005 } 2006 2007 *dophase_done = FALSE; /* not done yet */ 2008 2009 /* Determine if the requested mailbox (with the same UIDVALIDITY if set) 2010 has already been selected on this connection */ 2011 if(imap->mailbox && imapc->mailbox && 2012 !strcmp(imap->mailbox, imapc->mailbox) && 2013 (!imap->uidvalidity || !imapc->mailbox_uidvalidity || 2014 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity))) 2015 selected = TRUE; 2016 2017 /* Start the first command in the DO phase */ 2018 if(conn->data->set.upload) 2019 /* APPEND can be executed directly */ 2020 result = imap_perform_append(conn); 2021 else if(imap->custom && (selected || !imap->mailbox)) 2022 /* Custom command using the same mailbox or no mailbox */ 2023 result = imap_perform_list(conn); 2024 else if(!imap->custom && selected && imap->uid) 2025 /* FETCH from the same mailbox */ 2026 result = imap_perform_fetch(conn); 2027 else if(!imap->custom && selected && imap->query) 2028 /* SEARCH the current mailbox */ 2029 result = imap_perform_search(conn); 2030 else if(imap->mailbox && !selected && 2031 (imap->custom || imap->uid || imap->query)) 2032 /* SELECT the mailbox */ 2033 result = imap_perform_select(conn); 2034 else 2035 /* LIST */ 2036 result = imap_perform_list(conn); 2037 2038 if(result) 2039 return result; 2040 2041 /* Run the state-machine */ 2042 result = imap_multi_statemach(conn, dophase_done); 2043 2044 *connected = conn->bits.tcpconnect[FIRSTSOCKET]; 2045 2046 if(*dophase_done) 2047 DEBUGF(infof(conn->data, "DO phase is complete\n")); 2048 2049 return result; 2050} 2051 2052/*********************************************************************** 2053 * 2054 * imap_do() 2055 * 2056 * This function is registered as 'curl_do' function. It decodes the path 2057 * parts etc as a wrapper to the actual DO function (imap_perform). 2058 * 2059 * The input argument is already checked for validity. 2060 */ 2061static CURLcode imap_do(struct connectdata *conn, bool *done) 2062{ 2063 CURLcode result = CURLE_OK; 2064 2065 *done = FALSE; /* default to false */ 2066 2067 /* Parse the URL path */ 2068 result = imap_parse_url_path(conn); 2069 if(result) 2070 return result; 2071 2072 /* Parse the custom request */ 2073 result = imap_parse_custom_request(conn); 2074 if(result) 2075 return result; 2076 2077 result = imap_regular_transfer(conn, done); 2078 2079 return result; 2080} 2081 2082/*********************************************************************** 2083 * 2084 * imap_disconnect() 2085 * 2086 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection 2087 * resources. BLOCKING. 2088 */ 2089static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection) 2090{ 2091 struct imap_conn *imapc = &conn->proto.imapc; 2092 2093 /* We cannot send quit unconditionally. If this connection is stale or 2094 bad in any way, sending quit and waiting around here will make the 2095 disconnect wait in vain and cause more problems than we need to. */ 2096 2097 /* The IMAP session may or may not have been allocated/setup at this 2098 point! */ 2099 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart) 2100 if(!imap_perform_logout(conn)) 2101 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */ 2102 2103 /* Disconnect from the server */ 2104 Curl_pp_disconnect(&imapc->pp); 2105 2106 /* Cleanup the SASL module */ 2107 Curl_sasl_cleanup(conn, imapc->authused); 2108 2109 /* Cleanup our connection based variables */ 2110 Curl_safefree(imapc->mailbox); 2111 Curl_safefree(imapc->mailbox_uidvalidity); 2112 2113 return CURLE_OK; 2114} 2115 2116/* Call this when the DO phase has completed */ 2117static CURLcode imap_dophase_done(struct connectdata *conn, bool connected) 2118{ 2119 struct IMAP *imap = conn->data->req.protop; 2120 2121 (void)connected; 2122 2123 if(imap->transfer != FTPTRANSFER_BODY) 2124 /* no data to transfer */ 2125 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); 2126 2127 return CURLE_OK; 2128} 2129 2130/* Called from multi.c while DOing */ 2131static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done) 2132{ 2133 CURLcode result = imap_multi_statemach(conn, dophase_done); 2134 2135 if(result) 2136 DEBUGF(infof(conn->data, "DO phase failed\n")); 2137 else if(*dophase_done) { 2138 result = imap_dophase_done(conn, FALSE /* not connected */); 2139 2140 DEBUGF(infof(conn->data, "DO phase is complete\n")); 2141 } 2142 2143 return result; 2144} 2145 2146/*********************************************************************** 2147 * 2148 * imap_regular_transfer() 2149 * 2150 * The input argument is already checked for validity. 2151 * 2152 * Performs all commands done before a regular transfer between a local and a 2153 * remote host. 2154 */ 2155static CURLcode imap_regular_transfer(struct connectdata *conn, 2156 bool *dophase_done) 2157{ 2158 CURLcode result = CURLE_OK; 2159 bool connected = FALSE; 2160 struct SessionHandle *data = conn->data; 2161 2162 /* Make sure size is unknown at this point */ 2163 data->req.size = -1; 2164 2165 /* Set the progress data */ 2166 Curl_pgrsSetUploadCounter(data, 0); 2167 Curl_pgrsSetDownloadCounter(data, 0); 2168 Curl_pgrsSetUploadSize(data, 0); 2169 Curl_pgrsSetDownloadSize(data, 0); 2170 2171 /* Carry out the perform */ 2172 result = imap_perform(conn, &connected, dophase_done); 2173 2174 /* Perform post DO phase operations if necessary */ 2175 if(!result && *dophase_done) 2176 result = imap_dophase_done(conn, connected); 2177 2178 return result; 2179} 2180 2181static CURLcode imap_setup_connection(struct connectdata *conn) 2182{ 2183 struct SessionHandle *data = conn->data; 2184 2185 /* Initialise the IMAP layer */ 2186 CURLcode result = imap_init(conn); 2187 if(result) 2188 return result; 2189 2190 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { 2191 /* Unless we have asked to tunnel IMAP operations through the proxy, we 2192 switch and use HTTP operations only */ 2193#ifndef CURL_DISABLE_HTTP 2194 if(conn->handler == &Curl_handler_imap) 2195 conn->handler = &Curl_handler_imap_proxy; 2196 else { 2197#ifdef USE_SSL 2198 conn->handler = &Curl_handler_imaps_proxy; 2199#else 2200 failf(data, "IMAPS not supported!"); 2201 return CURLE_UNSUPPORTED_PROTOCOL; 2202#endif 2203 } 2204 2205 /* set it up as an HTTP connection instead */ 2206 return conn->handler->setup_connection(conn); 2207#else 2208 failf(data, "IMAP over http proxy requires HTTP support built-in!"); 2209 return CURLE_UNSUPPORTED_PROTOCOL; 2210#endif 2211 } 2212 2213 data->state.path++; /* don't include the initial slash */ 2214 2215 return CURLE_OK; 2216} 2217 2218/*********************************************************************** 2219 * 2220 * imap_sendf() 2221 * 2222 * Sends the formated string as an IMAP command to the server. 2223 * 2224 * Designed to never block. 2225 */ 2226static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...) 2227{ 2228 CURLcode result = CURLE_OK; 2229 struct imap_conn *imapc = &conn->proto.imapc; 2230 char *taggedfmt; 2231 va_list ap; 2232 2233 DEBUGASSERT(fmt); 2234 2235 /* Calculate the next command ID wrapping at 3 digits */ 2236 imapc->cmdid = (imapc->cmdid + 1) % 1000; 2237 2238 /* Calculate the tag based on the connection ID and command ID */ 2239 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d", 2240 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid); 2241 2242 /* Prefix the format with the tag */ 2243 taggedfmt = aprintf("%s %s", imapc->resptag, fmt); 2244 if(!taggedfmt) 2245 return CURLE_OUT_OF_MEMORY; 2246 2247 /* Send the data with the tag */ 2248 va_start(ap, fmt); 2249 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap); 2250 va_end(ap); 2251 2252 Curl_safefree(taggedfmt); 2253 2254 return result; 2255} 2256 2257/*********************************************************************** 2258 * 2259 * imap_atom() 2260 * 2261 * Checks the input string for characters that need escaping and returns an 2262 * atom ready for sending to the server. 2263 * 2264 * The returned string needs to be freed. 2265 * 2266 */ 2267static char *imap_atom(const char *str) 2268{ 2269 const char *p1; 2270 char *p2; 2271 size_t backsp_count = 0; 2272 size_t quote_count = 0; 2273 bool space_exists = FALSE; 2274 size_t newlen = 0; 2275 char *newstr = NULL; 2276 2277 if(!str) 2278 return NULL; 2279 2280 /* Count any unescapped characters */ 2281 p1 = str; 2282 while(*p1) { 2283 if(*p1 == '\\') 2284 backsp_count++; 2285 else if(*p1 == '"') 2286 quote_count++; 2287 else if(*p1 == ' ') 2288 space_exists = TRUE; 2289 2290 p1++; 2291 } 2292 2293 /* Does the input contain any unescapped characters? */ 2294 if(!backsp_count && !quote_count && !space_exists) 2295 return strdup(str); 2296 2297 /* Calculate the new string length */ 2298 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0); 2299 2300 /* Allocate the new string */ 2301 newstr = (char *) malloc((newlen + 1) * sizeof(char)); 2302 if(!newstr) 2303 return NULL; 2304 2305 /* Surround the string in quotes if necessary */ 2306 p2 = newstr; 2307 if(space_exists) { 2308 newstr[0] = '"'; 2309 newstr[newlen - 1] = '"'; 2310 p2++; 2311 } 2312 2313 /* Copy the string, escaping backslash and quote characters along the way */ 2314 p1 = str; 2315 while(*p1) { 2316 if(*p1 == '\\' || *p1 == '"') { 2317 *p2 = '\\'; 2318 p2++; 2319 } 2320 2321 *p2 = *p1; 2322 2323 p1++; 2324 p2++; 2325 } 2326 2327 /* Terminate the string */ 2328 newstr[newlen] = '\0'; 2329 2330 return newstr; 2331} 2332 2333/*********************************************************************** 2334 * 2335 * imap_is_bchar() 2336 * 2337 * Portable test of whether the specified char is a "bchar" as defined in the 2338 * grammar of RFC-5092. 2339 */ 2340static bool imap_is_bchar(char ch) 2341{ 2342 switch(ch) { 2343 /* bchar */ 2344 case ':': case '@': case '/': 2345 /* bchar -> achar */ 2346 case '&': case '=': 2347 /* bchar -> achar -> uchar -> unreserved */ 2348 case '0': case '1': case '2': case '3': case '4': case '5': case '6': 2349 case '7': case '8': case '9': 2350 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 2351 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 2352 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 2353 case 'V': case 'W': case 'X': case 'Y': case 'Z': 2354 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': 2355 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': 2356 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': 2357 case 'v': case 'w': case 'x': case 'y': case 'z': 2358 case '-': case '.': case '_': case '~': 2359 /* bchar -> achar -> uchar -> sub-delims-sh */ 2360 case '!': case '$': case '\'': case '(': case ')': case '*': 2361 case '+': case ',': 2362 /* bchar -> achar -> uchar -> pct-encoded */ 2363 case '%': /* HEXDIG chars are already included above */ 2364 return true; 2365 2366 default: 2367 return false; 2368 } 2369} 2370 2371/*********************************************************************** 2372 * 2373 * imap_parse_url_options() 2374 * 2375 * Parse the URL login options. 2376 */ 2377static CURLcode imap_parse_url_options(struct connectdata *conn) 2378{ 2379 CURLcode result = CURLE_OK; 2380 struct imap_conn *imapc = &conn->proto.imapc; 2381 const char *options = conn->options; 2382 const char *ptr = options; 2383 bool reset = TRUE; 2384 2385 while(ptr && *ptr) { 2386 const char *key = ptr; 2387 2388 while(*ptr && *ptr != '=') 2389 ptr++; 2390 2391 if(strnequal(key, "AUTH", 4)) { 2392 size_t len = 0; 2393 const char *value = ++ptr; 2394 2395 if(reset) { 2396 reset = FALSE; 2397 imapc->preftype = IMAP_TYPE_NONE; 2398 imapc->prefmech = SASL_AUTH_NONE; 2399 } 2400 2401 while(*ptr && *ptr != ';') { 2402 ptr++; 2403 len++; 2404 } 2405 2406 if(strnequal(value, "*", len)) { 2407 imapc->preftype = IMAP_TYPE_ANY; 2408 imapc->prefmech = SASL_AUTH_ANY; 2409 } 2410 else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) { 2411 imapc->preftype = IMAP_TYPE_SASL; 2412 imapc->prefmech |= SASL_MECH_LOGIN; 2413 } 2414 else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) { 2415 imapc->preftype = IMAP_TYPE_SASL; 2416 imapc->prefmech |= SASL_MECH_PLAIN; 2417 } 2418 else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) { 2419 imapc->preftype = IMAP_TYPE_SASL; 2420 imapc->prefmech |= SASL_MECH_CRAM_MD5; 2421 } 2422 else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) { 2423 imapc->preftype = IMAP_TYPE_SASL; 2424 imapc->prefmech |= SASL_MECH_DIGEST_MD5; 2425 } 2426 else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) { 2427 imapc->preftype = IMAP_TYPE_SASL; 2428 imapc->prefmech |= SASL_MECH_GSSAPI; 2429 } 2430 else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) { 2431 imapc->preftype = IMAP_TYPE_SASL; 2432 imapc->prefmech |= SASL_MECH_NTLM; 2433 } 2434 else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) { 2435 imapc->preftype = IMAP_TYPE_SASL; 2436 imapc->prefmech |= SASL_MECH_XOAUTH2; 2437 } 2438 2439 if(*ptr == ';') 2440 ptr++; 2441 } 2442 else 2443 result = CURLE_URL_MALFORMAT; 2444 } 2445 2446 return result; 2447} 2448 2449/*********************************************************************** 2450 * 2451 * imap_parse_url_path() 2452 * 2453 * Parse the URL path into separate path components. 2454 * 2455 */ 2456static CURLcode imap_parse_url_path(struct connectdata *conn) 2457{ 2458 /* The imap struct is already initialised in imap_connect() */ 2459 CURLcode result = CURLE_OK; 2460 struct SessionHandle *data = conn->data; 2461 struct IMAP *imap = data->req.protop; 2462 const char *begin = data->state.path; 2463 const char *ptr = begin; 2464 2465 /* See how much of the URL is a valid path and decode it */ 2466 while(imap_is_bchar(*ptr)) 2467 ptr++; 2468 2469 if(ptr != begin) { 2470 /* Remove the trailing slash if present */ 2471 const char *end = ptr; 2472 if(end > begin && end[-1] == '/') 2473 end--; 2474 2475 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL, 2476 TRUE); 2477 if(result) 2478 return result; 2479 } 2480 else 2481 imap->mailbox = NULL; 2482 2483 /* There can be any number of parameters in the form ";NAME=VALUE" */ 2484 while(*ptr == ';') { 2485 char *name; 2486 char *value; 2487 size_t valuelen; 2488 2489 /* Find the length of the name parameter */ 2490 begin = ++ptr; 2491 while(*ptr && *ptr != '=') 2492 ptr++; 2493 2494 if(!*ptr) 2495 return CURLE_URL_MALFORMAT; 2496 2497 /* Decode the name parameter */ 2498 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE); 2499 if(result) 2500 return result; 2501 2502 /* Find the length of the value parameter */ 2503 begin = ++ptr; 2504 while(imap_is_bchar(*ptr)) 2505 ptr++; 2506 2507 /* Decode the value parameter */ 2508 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE); 2509 if(result) { 2510 Curl_safefree(name); 2511 return result; 2512 } 2513 2514 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value)); 2515 2516 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and 2517 PARTIAL) stripping of the trailing slash character if it is present. 2518 2519 Note: Unknown parameters trigger a URL_MALFORMAT error. */ 2520 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) { 2521 if(valuelen > 0 && value[valuelen - 1] == '/') 2522 value[valuelen - 1] = '\0'; 2523 2524 imap->uidvalidity = value; 2525 value = NULL; 2526 } 2527 else if(Curl_raw_equal(name, "UID") && !imap->uid) { 2528 if(valuelen > 0 && value[valuelen - 1] == '/') 2529 value[valuelen - 1] = '\0'; 2530 2531 imap->uid = value; 2532 value = NULL; 2533 } 2534 else if(Curl_raw_equal(name, "SECTION") && !imap->section) { 2535 if(valuelen > 0 && value[valuelen - 1] == '/') 2536 value[valuelen - 1] = '\0'; 2537 2538 imap->section = value; 2539 value = NULL; 2540 } 2541 else if(Curl_raw_equal(name, "PARTIAL") && !imap->partial) { 2542 if(valuelen > 0 && value[valuelen - 1] == '/') 2543 value[valuelen - 1] = '\0'; 2544 2545 imap->partial = value; 2546 value = NULL; 2547 } 2548 else { 2549 Curl_safefree(name); 2550 Curl_safefree(value); 2551 2552 return CURLE_URL_MALFORMAT; 2553 } 2554 2555 Curl_safefree(name); 2556 Curl_safefree(value); 2557 } 2558 2559 /* Does the URL contain a query parameter? Only valid when we have a mailbox 2560 and no UID as per RFC-5092 */ 2561 if(imap->mailbox && !imap->uid && *ptr == '?') { 2562 /* Find the length of the query parameter */ 2563 begin = ++ptr; 2564 while(imap_is_bchar(*ptr)) 2565 ptr++; 2566 2567 /* Decode the query parameter */ 2568 result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL, 2569 TRUE); 2570 if(result) 2571 return result; 2572 } 2573 2574 /* Any extra stuff at the end of the URL is an error */ 2575 if(*ptr) 2576 return CURLE_URL_MALFORMAT; 2577 2578 return CURLE_OK; 2579} 2580 2581/*********************************************************************** 2582 * 2583 * imap_parse_custom_request() 2584 * 2585 * Parse the custom request. 2586 */ 2587static CURLcode imap_parse_custom_request(struct connectdata *conn) 2588{ 2589 CURLcode result = CURLE_OK; 2590 struct SessionHandle *data = conn->data; 2591 struct IMAP *imap = data->req.protop; 2592 const char *custom = data->set.str[STRING_CUSTOMREQUEST]; 2593 2594 if(custom) { 2595 /* URL decode the custom request */ 2596 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE); 2597 2598 /* Extract the parameters if specified */ 2599 if(!result) { 2600 const char *params = imap->custom; 2601 2602 while(*params && *params != ' ') 2603 params++; 2604 2605 if(*params) { 2606 imap->custom_params = strdup(params); 2607 imap->custom[params - imap->custom] = '\0'; 2608 2609 if(!imap->custom_params) 2610 result = CURLE_OUT_OF_MEMORY; 2611 } 2612 } 2613 } 2614 2615 return result; 2616} 2617 2618/*********************************************************************** 2619 * 2620 * imap_calc_sasl_details() 2621 * 2622 * Calculate the required login details for SASL authentication. 2623 */ 2624static CURLcode imap_calc_sasl_details(struct connectdata *conn, 2625 const char **mech, 2626 char **initresp, size_t *len, 2627 imapstate *state1, imapstate *state2) 2628{ 2629 CURLcode result = CURLE_OK; 2630 struct SessionHandle *data = conn->data; 2631 struct imap_conn *imapc = &conn->proto.imapc; 2632 2633 /* Calculate the supported authentication mechanism, by decreasing order of 2634 security, as well as the initial response where appropriate */ 2635#ifndef CURL_DISABLE_CRYPTO_AUTH 2636 if((imapc->authmechs & SASL_MECH_DIGEST_MD5) && 2637 (imapc->prefmech & SASL_MECH_DIGEST_MD5)) { 2638 *mech = SASL_MECH_STRING_DIGEST_MD5; 2639 *state1 = IMAP_AUTHENTICATE_DIGESTMD5; 2640 imapc->authused = SASL_MECH_DIGEST_MD5; 2641 } 2642 else if((imapc->authmechs & SASL_MECH_CRAM_MD5) && 2643 (imapc->prefmech & SASL_MECH_CRAM_MD5)) { 2644 *mech = SASL_MECH_STRING_CRAM_MD5; 2645 *state1 = IMAP_AUTHENTICATE_CRAMMD5; 2646 imapc->authused = SASL_MECH_CRAM_MD5; 2647 } 2648 else 2649#endif 2650#ifdef USE_NTLM 2651 if((imapc->authmechs & SASL_MECH_NTLM) && 2652 (imapc->prefmech & SASL_MECH_NTLM)) { 2653 *mech = SASL_MECH_STRING_NTLM; 2654 *state1 = IMAP_AUTHENTICATE_NTLM; 2655 *state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG; 2656 imapc->authused = SASL_MECH_NTLM; 2657 2658 if(imapc->ir_supported || data->set.sasl_ir) 2659 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd, 2660 &conn->ntlm, 2661 initresp, len); 2662 } 2663 else 2664#endif 2665 if(((imapc->authmechs & SASL_MECH_XOAUTH2) && 2666 (imapc->prefmech & SASL_MECH_XOAUTH2) && 2667 (imapc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) { 2668 *mech = SASL_MECH_STRING_XOAUTH2; 2669 *state1 = IMAP_AUTHENTICATE_XOAUTH2; 2670 *state2 = IMAP_AUTHENTICATE_FINAL; 2671 imapc->authused = SASL_MECH_XOAUTH2; 2672 2673 if(imapc->ir_supported || data->set.sasl_ir) 2674 result = Curl_sasl_create_xoauth2_message(data, conn->user, 2675 conn->xoauth2_bearer, 2676 initresp, len); 2677 } 2678 else if((imapc->authmechs & SASL_MECH_LOGIN) && 2679 (imapc->prefmech & SASL_MECH_LOGIN)) { 2680 *mech = SASL_MECH_STRING_LOGIN; 2681 *state1 = IMAP_AUTHENTICATE_LOGIN; 2682 *state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD; 2683 imapc->authused = SASL_MECH_LOGIN; 2684 2685 if(imapc->ir_supported || data->set.sasl_ir) 2686 result = Curl_sasl_create_login_message(data, conn->user, initresp, len); 2687 } 2688 else if((imapc->authmechs & SASL_MECH_PLAIN) && 2689 (imapc->prefmech & SASL_MECH_PLAIN)) { 2690 *mech = SASL_MECH_STRING_PLAIN; 2691 *state1 = IMAP_AUTHENTICATE_PLAIN; 2692 *state2 = IMAP_AUTHENTICATE_FINAL; 2693 imapc->authused = SASL_MECH_PLAIN; 2694 2695 if(imapc->ir_supported || data->set.sasl_ir) 2696 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd, 2697 initresp, len); 2698 } 2699 2700 return result; 2701} 2702 2703#endif /* CURL_DISABLE_IMAP */ 2704