Annotation of embedaddon/curl/lib/imap.c, revision 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>