Annotation of embedaddon/curl/lib/imap.c, revision 1.1.1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>