File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / imap.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (4 years, 1 month ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    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>