quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

imap.c (68163B)


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