Annotation of embedaddon/curl/lib/imap.c, revision 1.1
1.1 ! misho 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: case IMAP_SEARCH:
! 1310: result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
! 1311: break;
! 1312:
! 1313: case IMAP_SELECT:
! 1314: result = imap_state_select_resp(conn, imapcode, imapc->state);
! 1315: break;
! 1316:
! 1317: case IMAP_FETCH:
! 1318: result = imap_state_fetch_resp(conn, imapcode, imapc->state);
! 1319: break;
! 1320:
! 1321: case IMAP_FETCH_FINAL:
! 1322: result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
! 1323: break;
! 1324:
! 1325: case IMAP_APPEND:
! 1326: result = imap_state_append_resp(conn, imapcode, imapc->state);
! 1327: break;
! 1328:
! 1329: case IMAP_APPEND_FINAL:
! 1330: result = imap_state_append_final_resp(conn, imapcode, imapc->state);
! 1331: break;
! 1332:
! 1333: case IMAP_LOGOUT:
! 1334: /* fallthrough, just stop! */
! 1335: default:
! 1336: /* internal error */
! 1337: state(conn, IMAP_STOP);
! 1338: break;
! 1339: }
! 1340: } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
! 1341:
! 1342: return result;
! 1343: }
! 1344:
! 1345: /* Called repeatedly until done from multi.c */
! 1346: static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
! 1347: {
! 1348: CURLcode result = CURLE_OK;
! 1349: struct imap_conn *imapc = &conn->proto.imapc;
! 1350:
! 1351: if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
! 1352: result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
! 1353: if(result || !imapc->ssldone)
! 1354: return result;
! 1355: }
! 1356:
! 1357: result = Curl_pp_statemach(&imapc->pp, FALSE, FALSE);
! 1358: *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
! 1359:
! 1360: return result;
! 1361: }
! 1362:
! 1363: static CURLcode imap_block_statemach(struct connectdata *conn,
! 1364: bool disconnecting)
! 1365: {
! 1366: CURLcode result = CURLE_OK;
! 1367: struct imap_conn *imapc = &conn->proto.imapc;
! 1368:
! 1369: while(imapc->state != IMAP_STOP && !result)
! 1370: result = Curl_pp_statemach(&imapc->pp, TRUE, disconnecting);
! 1371:
! 1372: return result;
! 1373: }
! 1374:
! 1375: /* Allocate and initialize the struct IMAP for the current Curl_easy if
! 1376: required */
! 1377: static CURLcode imap_init(struct connectdata *conn)
! 1378: {
! 1379: CURLcode result = CURLE_OK;
! 1380: struct Curl_easy *data = conn->data;
! 1381: struct IMAP *imap;
! 1382:
! 1383: imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
! 1384: if(!imap)
! 1385: result = CURLE_OUT_OF_MEMORY;
! 1386:
! 1387: return result;
! 1388: }
! 1389:
! 1390: /* For the IMAP "protocol connect" and "doing" phases only */
! 1391: static int imap_getsock(struct connectdata *conn, curl_socket_t *socks)
! 1392: {
! 1393: return Curl_pp_getsock(&conn->proto.imapc.pp, socks);
! 1394: }
! 1395:
! 1396: /***********************************************************************
! 1397: *
! 1398: * imap_connect()
! 1399: *
! 1400: * This function should do everything that is to be considered a part of the
! 1401: * connection phase.
! 1402: *
! 1403: * The variable 'done' points to will be TRUE if the protocol-layer connect
! 1404: * phase is done when this function returns, or FALSE if not.
! 1405: */
! 1406: static CURLcode imap_connect(struct connectdata *conn, bool *done)
! 1407: {
! 1408: CURLcode result = CURLE_OK;
! 1409: struct imap_conn *imapc = &conn->proto.imapc;
! 1410: struct pingpong *pp = &imapc->pp;
! 1411:
! 1412: *done = FALSE; /* default to not done yet */
! 1413:
! 1414: /* We always support persistent connections in IMAP */
! 1415: connkeep(conn, "IMAP default");
! 1416:
! 1417: /* Set the default response time-out */
! 1418: pp->response_time = RESP_TIMEOUT;
! 1419: pp->statemach_act = imap_statemach_act;
! 1420: pp->endofresp = imap_endofresp;
! 1421: pp->conn = conn;
! 1422:
! 1423: /* Set the default preferred authentication type and mechanism */
! 1424: imapc->preftype = IMAP_TYPE_ANY;
! 1425: Curl_sasl_init(&imapc->sasl, &saslimap);
! 1426:
! 1427: /* Initialise the pingpong layer */
! 1428: Curl_pp_init(pp);
! 1429:
! 1430: /* Parse the URL options */
! 1431: result = imap_parse_url_options(conn);
! 1432: if(result)
! 1433: return result;
! 1434:
! 1435: /* Start off waiting for the server greeting response */
! 1436: state(conn, IMAP_SERVERGREET);
! 1437:
! 1438: /* Start off with an response id of '*' */
! 1439: strcpy(imapc->resptag, "*");
! 1440:
! 1441: result = imap_multi_statemach(conn, done);
! 1442:
! 1443: return result;
! 1444: }
! 1445:
! 1446: /***********************************************************************
! 1447: *
! 1448: * imap_done()
! 1449: *
! 1450: * The DONE function. This does what needs to be done after a single DO has
! 1451: * performed.
! 1452: *
! 1453: * Input argument is already checked for validity.
! 1454: */
! 1455: static CURLcode imap_done(struct connectdata *conn, CURLcode status,
! 1456: bool premature)
! 1457: {
! 1458: CURLcode result = CURLE_OK;
! 1459: struct Curl_easy *data = conn->data;
! 1460: struct IMAP *imap = data->req.protop;
! 1461:
! 1462: (void)premature;
! 1463:
! 1464: if(!imap)
! 1465: return CURLE_OK;
! 1466:
! 1467: if(status) {
! 1468: connclose(conn, "IMAP done with bad status"); /* marked for closure */
! 1469: result = status; /* use the already set error code */
! 1470: }
! 1471: else if(!data->set.connect_only && !imap->custom &&
! 1472: (imap->uid || imap->mindex || data->set.upload ||
! 1473: data->set.mimepost.kind != MIMEKIND_NONE)) {
! 1474: /* Handle responses after FETCH or APPEND transfer has finished */
! 1475:
! 1476: if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
! 1477: state(conn, IMAP_FETCH_FINAL);
! 1478: else {
! 1479: /* End the APPEND command first by sending an empty line */
! 1480: result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
! 1481: if(!result)
! 1482: state(conn, IMAP_APPEND_FINAL);
! 1483: }
! 1484:
! 1485: /* Run the state-machine */
! 1486: if(!result)
! 1487: result = imap_block_statemach(conn, FALSE);
! 1488: }
! 1489:
! 1490: /* Cleanup our per-request based variables */
! 1491: Curl_safefree(imap->mailbox);
! 1492: Curl_safefree(imap->uidvalidity);
! 1493: Curl_safefree(imap->uid);
! 1494: Curl_safefree(imap->mindex);
! 1495: Curl_safefree(imap->section);
! 1496: Curl_safefree(imap->partial);
! 1497: Curl_safefree(imap->query);
! 1498: Curl_safefree(imap->custom);
! 1499: Curl_safefree(imap->custom_params);
! 1500:
! 1501: /* Clear the transfer mode for the next request */
! 1502: imap->transfer = FTPTRANSFER_BODY;
! 1503:
! 1504: return result;
! 1505: }
! 1506:
! 1507: /***********************************************************************
! 1508: *
! 1509: * imap_perform()
! 1510: *
! 1511: * This is the actual DO function for IMAP. Fetch or append a message, or do
! 1512: * other things according to the options previously setup.
! 1513: */
! 1514: static CURLcode imap_perform(struct connectdata *conn, bool *connected,
! 1515: bool *dophase_done)
! 1516: {
! 1517: /* This is IMAP and no proxy */
! 1518: CURLcode result = CURLE_OK;
! 1519: struct Curl_easy *data = conn->data;
! 1520: struct IMAP *imap = data->req.protop;
! 1521: struct imap_conn *imapc = &conn->proto.imapc;
! 1522: bool selected = FALSE;
! 1523:
! 1524: DEBUGF(infof(conn->data, "DO phase starts\n"));
! 1525:
! 1526: if(conn->data->set.opt_no_body) {
! 1527: /* Requested no body means no transfer */
! 1528: imap->transfer = FTPTRANSFER_INFO;
! 1529: }
! 1530:
! 1531: *dophase_done = FALSE; /* not done yet */
! 1532:
! 1533: /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
! 1534: has already been selected on this connection */
! 1535: if(imap->mailbox && imapc->mailbox &&
! 1536: strcasecompare(imap->mailbox, imapc->mailbox) &&
! 1537: (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
! 1538: strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
! 1539: selected = TRUE;
! 1540:
! 1541: /* Start the first command in the DO phase */
! 1542: if(conn->data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
! 1543: /* APPEND can be executed directly */
! 1544: result = imap_perform_append(conn);
! 1545: else if(imap->custom && (selected || !imap->mailbox))
! 1546: /* Custom command using the same mailbox or no mailbox */
! 1547: result = imap_perform_list(conn);
! 1548: else if(!imap->custom && selected && (imap->uid || imap->mindex))
! 1549: /* FETCH from the same mailbox */
! 1550: result = imap_perform_fetch(conn);
! 1551: else if(!imap->custom && selected && imap->query)
! 1552: /* SEARCH the current mailbox */
! 1553: result = imap_perform_search(conn);
! 1554: else if(imap->mailbox && !selected &&
! 1555: (imap->custom || imap->uid || imap->mindex || imap->query))
! 1556: /* SELECT the mailbox */
! 1557: result = imap_perform_select(conn);
! 1558: else
! 1559: /* LIST */
! 1560: result = imap_perform_list(conn);
! 1561:
! 1562: if(result)
! 1563: return result;
! 1564:
! 1565: /* Run the state-machine */
! 1566: result = imap_multi_statemach(conn, dophase_done);
! 1567:
! 1568: *connected = conn->bits.tcpconnect[FIRSTSOCKET];
! 1569:
! 1570: if(*dophase_done)
! 1571: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 1572:
! 1573: return result;
! 1574: }
! 1575:
! 1576: /***********************************************************************
! 1577: *
! 1578: * imap_do()
! 1579: *
! 1580: * This function is registered as 'curl_do' function. It decodes the path
! 1581: * parts etc as a wrapper to the actual DO function (imap_perform).
! 1582: *
! 1583: * The input argument is already checked for validity.
! 1584: */
! 1585: static CURLcode imap_do(struct connectdata *conn, bool *done)
! 1586: {
! 1587: CURLcode result = CURLE_OK;
! 1588:
! 1589: *done = FALSE; /* default to false */
! 1590:
! 1591: /* Parse the URL path */
! 1592: result = imap_parse_url_path(conn);
! 1593: if(result)
! 1594: return result;
! 1595:
! 1596: /* Parse the custom request */
! 1597: result = imap_parse_custom_request(conn);
! 1598: if(result)
! 1599: return result;
! 1600:
! 1601: result = imap_regular_transfer(conn, done);
! 1602:
! 1603: return result;
! 1604: }
! 1605:
! 1606: /***********************************************************************
! 1607: *
! 1608: * imap_disconnect()
! 1609: *
! 1610: * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
! 1611: * resources. BLOCKING.
! 1612: */
! 1613: static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
! 1614: {
! 1615: struct imap_conn *imapc = &conn->proto.imapc;
! 1616:
! 1617: /* We cannot send quit unconditionally. If this connection is stale or
! 1618: bad in any way, sending quit and waiting around here will make the
! 1619: disconnect wait in vain and cause more problems than we need to. */
! 1620:
! 1621: /* The IMAP session may or may not have been allocated/setup at this
! 1622: point! */
! 1623: if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
! 1624: if(!imap_perform_logout(conn))
! 1625: (void)imap_block_statemach(conn, TRUE); /* ignore errors on LOGOUT */
! 1626:
! 1627: /* Disconnect from the server */
! 1628: Curl_pp_disconnect(&imapc->pp);
! 1629:
! 1630: /* Cleanup the SASL module */
! 1631: Curl_sasl_cleanup(conn, imapc->sasl.authused);
! 1632:
! 1633: /* Cleanup our connection based variables */
! 1634: Curl_safefree(imapc->mailbox);
! 1635: Curl_safefree(imapc->mailbox_uidvalidity);
! 1636:
! 1637: return CURLE_OK;
! 1638: }
! 1639:
! 1640: /* Call this when the DO phase has completed */
! 1641: static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
! 1642: {
! 1643: struct IMAP *imap = conn->data->req.protop;
! 1644:
! 1645: (void)connected;
! 1646:
! 1647: if(imap->transfer != FTPTRANSFER_BODY)
! 1648: /* no data to transfer */
! 1649: Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
! 1650:
! 1651: return CURLE_OK;
! 1652: }
! 1653:
! 1654: /* Called from multi.c while DOing */
! 1655: static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
! 1656: {
! 1657: CURLcode result = imap_multi_statemach(conn, dophase_done);
! 1658:
! 1659: if(result)
! 1660: DEBUGF(infof(conn->data, "DO phase failed\n"));
! 1661: else if(*dophase_done) {
! 1662: result = imap_dophase_done(conn, FALSE /* not connected */);
! 1663:
! 1664: DEBUGF(infof(conn->data, "DO phase is complete\n"));
! 1665: }
! 1666:
! 1667: return result;
! 1668: }
! 1669:
! 1670: /***********************************************************************
! 1671: *
! 1672: * imap_regular_transfer()
! 1673: *
! 1674: * The input argument is already checked for validity.
! 1675: *
! 1676: * Performs all commands done before a regular transfer between a local and a
! 1677: * remote host.
! 1678: */
! 1679: static CURLcode imap_regular_transfer(struct connectdata *conn,
! 1680: bool *dophase_done)
! 1681: {
! 1682: CURLcode result = CURLE_OK;
! 1683: bool connected = FALSE;
! 1684: struct Curl_easy *data = conn->data;
! 1685:
! 1686: /* Make sure size is unknown at this point */
! 1687: data->req.size = -1;
! 1688:
! 1689: /* Set the progress data */
! 1690: Curl_pgrsSetUploadCounter(data, 0);
! 1691: Curl_pgrsSetDownloadCounter(data, 0);
! 1692: Curl_pgrsSetUploadSize(data, -1);
! 1693: Curl_pgrsSetDownloadSize(data, -1);
! 1694:
! 1695: /* Carry out the perform */
! 1696: result = imap_perform(conn, &connected, dophase_done);
! 1697:
! 1698: /* Perform post DO phase operations if necessary */
! 1699: if(!result && *dophase_done)
! 1700: result = imap_dophase_done(conn, connected);
! 1701:
! 1702: return result;
! 1703: }
! 1704:
! 1705: static CURLcode imap_setup_connection(struct connectdata *conn)
! 1706: {
! 1707: /* Initialise the IMAP layer */
! 1708: CURLcode result = imap_init(conn);
! 1709: if(result)
! 1710: return result;
! 1711:
! 1712: /* Clear the TLS upgraded flag */
! 1713: conn->tls_upgraded = FALSE;
! 1714:
! 1715: return CURLE_OK;
! 1716: }
! 1717:
! 1718: /***********************************************************************
! 1719: *
! 1720: * imap_sendf()
! 1721: *
! 1722: * Sends the formatted string as an IMAP command to the server.
! 1723: *
! 1724: * Designed to never block.
! 1725: */
! 1726: static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
! 1727: {
! 1728: CURLcode result = CURLE_OK;
! 1729: struct imap_conn *imapc = &conn->proto.imapc;
! 1730: char *taggedfmt;
! 1731: va_list ap;
! 1732:
! 1733: DEBUGASSERT(fmt);
! 1734:
! 1735: /* Calculate the next command ID wrapping at 3 digits */
! 1736: imapc->cmdid = (imapc->cmdid + 1) % 1000;
! 1737:
! 1738: /* Calculate the tag based on the connection ID and command ID */
! 1739: msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
! 1740: 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
! 1741:
! 1742: /* Prefix the format with the tag */
! 1743: taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
! 1744: if(!taggedfmt)
! 1745: return CURLE_OUT_OF_MEMORY;
! 1746:
! 1747: /* Send the data with the tag */
! 1748: va_start(ap, fmt);
! 1749: result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
! 1750: va_end(ap);
! 1751:
! 1752: free(taggedfmt);
! 1753:
! 1754: return result;
! 1755: }
! 1756:
! 1757: /***********************************************************************
! 1758: *
! 1759: * imap_atom()
! 1760: *
! 1761: * Checks the input string for characters that need escaping and returns an
! 1762: * atom ready for sending to the server.
! 1763: *
! 1764: * The returned string needs to be freed.
! 1765: *
! 1766: */
! 1767: static char *imap_atom(const char *str, bool escape_only)
! 1768: {
! 1769: /* !checksrc! disable PARENBRACE 1 */
! 1770: const char atom_specials[] = "(){ %*]";
! 1771: const char *p1;
! 1772: char *p2;
! 1773: size_t backsp_count = 0;
! 1774: size_t quote_count = 0;
! 1775: bool others_exists = FALSE;
! 1776: size_t newlen = 0;
! 1777: char *newstr = NULL;
! 1778:
! 1779: if(!str)
! 1780: return NULL;
! 1781:
! 1782: /* Look for "atom-specials", counting the backslash and quote characters as
! 1783: these will need escaping */
! 1784: p1 = str;
! 1785: while(*p1) {
! 1786: if(*p1 == '\\')
! 1787: backsp_count++;
! 1788: else if(*p1 == '"')
! 1789: quote_count++;
! 1790: else if(!escape_only) {
! 1791: const char *p3 = atom_specials;
! 1792:
! 1793: while(*p3 && !others_exists) {
! 1794: if(*p1 == *p3)
! 1795: others_exists = TRUE;
! 1796:
! 1797: p3++;
! 1798: }
! 1799: }
! 1800:
! 1801: p1++;
! 1802: }
! 1803:
! 1804: /* Does the input contain any "atom-special" characters? */
! 1805: if(!backsp_count && !quote_count && !others_exists)
! 1806: return strdup(str);
! 1807:
! 1808: /* Calculate the new string length */
! 1809: newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2);
! 1810:
! 1811: /* Allocate the new string */
! 1812: newstr = (char *) malloc((newlen + 1) * sizeof(char));
! 1813: if(!newstr)
! 1814: return NULL;
! 1815:
! 1816: /* Surround the string in quotes if necessary */
! 1817: p2 = newstr;
! 1818: if(!escape_only) {
! 1819: newstr[0] = '"';
! 1820: newstr[newlen - 1] = '"';
! 1821: p2++;
! 1822: }
! 1823:
! 1824: /* Copy the string, escaping backslash and quote characters along the way */
! 1825: p1 = str;
! 1826: while(*p1) {
! 1827: if(*p1 == '\\' || *p1 == '"') {
! 1828: *p2 = '\\';
! 1829: p2++;
! 1830: }
! 1831:
! 1832: *p2 = *p1;
! 1833:
! 1834: p1++;
! 1835: p2++;
! 1836: }
! 1837:
! 1838: /* Terminate the string */
! 1839: newstr[newlen] = '\0';
! 1840:
! 1841: return newstr;
! 1842: }
! 1843:
! 1844: /***********************************************************************
! 1845: *
! 1846: * imap_is_bchar()
! 1847: *
! 1848: * Portable test of whether the specified char is a "bchar" as defined in the
! 1849: * grammar of RFC-5092.
! 1850: */
! 1851: static bool imap_is_bchar(char ch)
! 1852: {
! 1853: switch(ch) {
! 1854: /* bchar */
! 1855: case ':': case '@': case '/':
! 1856: /* bchar -> achar */
! 1857: case '&': case '=':
! 1858: /* bchar -> achar -> uchar -> unreserved */
! 1859: case '0': case '1': case '2': case '3': case '4': case '5': case '6':
! 1860: case '7': case '8': case '9':
! 1861: case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
! 1862: case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
! 1863: case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
! 1864: case 'V': case 'W': case 'X': case 'Y': case 'Z':
! 1865: case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
! 1866: case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
! 1867: case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
! 1868: case 'v': case 'w': case 'x': case 'y': case 'z':
! 1869: case '-': case '.': case '_': case '~':
! 1870: /* bchar -> achar -> uchar -> sub-delims-sh */
! 1871: case '!': case '$': case '\'': case '(': case ')': case '*':
! 1872: case '+': case ',':
! 1873: /* bchar -> achar -> uchar -> pct-encoded */
! 1874: case '%': /* HEXDIG chars are already included above */
! 1875: return true;
! 1876:
! 1877: default:
! 1878: return false;
! 1879: }
! 1880: }
! 1881:
! 1882: /***********************************************************************
! 1883: *
! 1884: * imap_parse_url_options()
! 1885: *
! 1886: * Parse the URL login options.
! 1887: */
! 1888: static CURLcode imap_parse_url_options(struct connectdata *conn)
! 1889: {
! 1890: CURLcode result = CURLE_OK;
! 1891: struct imap_conn *imapc = &conn->proto.imapc;
! 1892: const char *ptr = conn->options;
! 1893:
! 1894: imapc->sasl.resetprefs = TRUE;
! 1895:
! 1896: while(!result && ptr && *ptr) {
! 1897: const char *key = ptr;
! 1898: const char *value;
! 1899:
! 1900: while(*ptr && *ptr != '=')
! 1901: ptr++;
! 1902:
! 1903: value = ptr + 1;
! 1904:
! 1905: while(*ptr && *ptr != ';')
! 1906: ptr++;
! 1907:
! 1908: if(strncasecompare(key, "AUTH=", 5))
! 1909: result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
! 1910: value, ptr - value);
! 1911: else
! 1912: result = CURLE_URL_MALFORMAT;
! 1913:
! 1914: if(*ptr == ';')
! 1915: ptr++;
! 1916: }
! 1917:
! 1918: switch(imapc->sasl.prefmech) {
! 1919: case SASL_AUTH_NONE:
! 1920: imapc->preftype = IMAP_TYPE_NONE;
! 1921: break;
! 1922: case SASL_AUTH_DEFAULT:
! 1923: imapc->preftype = IMAP_TYPE_ANY;
! 1924: break;
! 1925: default:
! 1926: imapc->preftype = IMAP_TYPE_SASL;
! 1927: break;
! 1928: }
! 1929:
! 1930: return result;
! 1931: }
! 1932:
! 1933: /***********************************************************************
! 1934: *
! 1935: * imap_parse_url_path()
! 1936: *
! 1937: * Parse the URL path into separate path components.
! 1938: *
! 1939: */
! 1940: static CURLcode imap_parse_url_path(struct connectdata *conn)
! 1941: {
! 1942: /* The imap struct is already initialised in imap_connect() */
! 1943: CURLcode result = CURLE_OK;
! 1944: struct Curl_easy *data = conn->data;
! 1945: struct IMAP *imap = data->req.protop;
! 1946: const char *begin = &data->state.up.path[1]; /* skip leading slash */
! 1947: const char *ptr = begin;
! 1948:
! 1949: /* See how much of the URL is a valid path and decode it */
! 1950: while(imap_is_bchar(*ptr))
! 1951: ptr++;
! 1952:
! 1953: if(ptr != begin) {
! 1954: /* Remove the trailing slash if present */
! 1955: const char *end = ptr;
! 1956: if(end > begin && end[-1] == '/')
! 1957: end--;
! 1958:
! 1959: result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
! 1960: TRUE);
! 1961: if(result)
! 1962: return result;
! 1963: }
! 1964: else
! 1965: imap->mailbox = NULL;
! 1966:
! 1967: /* There can be any number of parameters in the form ";NAME=VALUE" */
! 1968: while(*ptr == ';') {
! 1969: char *name;
! 1970: char *value;
! 1971: size_t valuelen;
! 1972:
! 1973: /* Find the length of the name parameter */
! 1974: begin = ++ptr;
! 1975: while(*ptr && *ptr != '=')
! 1976: ptr++;
! 1977:
! 1978: if(!*ptr)
! 1979: return CURLE_URL_MALFORMAT;
! 1980:
! 1981: /* Decode the name parameter */
! 1982: result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
! 1983: if(result)
! 1984: return result;
! 1985:
! 1986: /* Find the length of the value parameter */
! 1987: begin = ++ptr;
! 1988: while(imap_is_bchar(*ptr))
! 1989: ptr++;
! 1990:
! 1991: /* Decode the value parameter */
! 1992: result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
! 1993: if(result) {
! 1994: free(name);
! 1995: return result;
! 1996: }
! 1997:
! 1998: DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
! 1999:
! 2000: /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
! 2001: PARTIAL) stripping of the trailing slash character if it is present.
! 2002:
! 2003: Note: Unknown parameters trigger a URL_MALFORMAT error. */
! 2004: if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
! 2005: if(valuelen > 0 && value[valuelen - 1] == '/')
! 2006: value[valuelen - 1] = '\0';
! 2007:
! 2008: imap->uidvalidity = value;
! 2009: value = NULL;
! 2010: }
! 2011: else if(strcasecompare(name, "UID") && !imap->uid) {
! 2012: if(valuelen > 0 && value[valuelen - 1] == '/')
! 2013: value[valuelen - 1] = '\0';
! 2014:
! 2015: imap->uid = value;
! 2016: value = NULL;
! 2017: }
! 2018: else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) {
! 2019: if(valuelen > 0 && value[valuelen - 1] == '/')
! 2020: value[valuelen - 1] = '\0';
! 2021:
! 2022: imap->mindex = value;
! 2023: value = NULL;
! 2024: }
! 2025: else if(strcasecompare(name, "SECTION") && !imap->section) {
! 2026: if(valuelen > 0 && value[valuelen - 1] == '/')
! 2027: value[valuelen - 1] = '\0';
! 2028:
! 2029: imap->section = value;
! 2030: value = NULL;
! 2031: }
! 2032: else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
! 2033: if(valuelen > 0 && value[valuelen - 1] == '/')
! 2034: value[valuelen - 1] = '\0';
! 2035:
! 2036: imap->partial = value;
! 2037: value = NULL;
! 2038: }
! 2039: else {
! 2040: free(name);
! 2041: free(value);
! 2042:
! 2043: return CURLE_URL_MALFORMAT;
! 2044: }
! 2045:
! 2046: free(name);
! 2047: free(value);
! 2048: }
! 2049:
! 2050: /* Does the URL contain a query parameter? Only valid when we have a mailbox
! 2051: and no UID as per RFC-5092 */
! 2052: if(imap->mailbox && !imap->uid && !imap->mindex) {
! 2053: /* Get the query parameter, URL decoded */
! 2054: (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query,
! 2055: CURLU_URLDECODE);
! 2056: }
! 2057:
! 2058: /* Any extra stuff at the end of the URL is an error */
! 2059: if(*ptr)
! 2060: return CURLE_URL_MALFORMAT;
! 2061:
! 2062: return CURLE_OK;
! 2063: }
! 2064:
! 2065: /***********************************************************************
! 2066: *
! 2067: * imap_parse_custom_request()
! 2068: *
! 2069: * Parse the custom request.
! 2070: */
! 2071: static CURLcode imap_parse_custom_request(struct connectdata *conn)
! 2072: {
! 2073: CURLcode result = CURLE_OK;
! 2074: struct Curl_easy *data = conn->data;
! 2075: struct IMAP *imap = data->req.protop;
! 2076: const char *custom = data->set.str[STRING_CUSTOMREQUEST];
! 2077:
! 2078: if(custom) {
! 2079: /* URL decode the custom request */
! 2080: result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
! 2081:
! 2082: /* Extract the parameters if specified */
! 2083: if(!result) {
! 2084: const char *params = imap->custom;
! 2085:
! 2086: while(*params && *params != ' ')
! 2087: params++;
! 2088:
! 2089: if(*params) {
! 2090: imap->custom_params = strdup(params);
! 2091: imap->custom[params - imap->custom] = '\0';
! 2092:
! 2093: if(!imap->custom_params)
! 2094: result = CURLE_OUT_OF_MEMORY;
! 2095: }
! 2096: }
! 2097: }
! 2098:
! 2099: return result;
! 2100: }
! 2101:
! 2102: #endif /* CURL_DISABLE_IMAP */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>