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