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