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