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