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