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