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