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