Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.haxx.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * RFC1734 POP3 Authentication
     22  * RFC1939 POP3 protocol
     23  * RFC2195 CRAM-MD5 authentication
     24  * RFC2384 POP URL Scheme
     25  * RFC2449 POP3 Extension Mechanism
     26  * RFC2595 Using TLS with IMAP, POP3 and ACAP
     27  * RFC2831 DIGEST-MD5 authentication
     28  * RFC4422 Simple Authentication and Security Layer (SASL)
     29  * RFC4616 PLAIN authentication
     30  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
     31  * RFC5034 POP3 SASL Authentication Mechanism
     32  * RFC6749 OAuth 2.0 Authorization Framework
     33  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
     34  *
     35  ***************************************************************************/
     36 
     37 #include "curl_setup.h"
     38 
     39 #ifndef CURL_DISABLE_POP3
     40 
     41 #ifdef HAVE_NETINET_IN_H
     42 #include <netinet/in.h>
     43 #endif
     44 #ifdef HAVE_ARPA_INET_H
     45 #include <arpa/inet.h>
     46 #endif
     47 #ifdef HAVE_UTSNAME_H
     48 #include <sys/utsname.h>
     49 #endif
     50 #ifdef HAVE_NETDB_H
     51 #include <netdb.h>
     52 #endif
     53 #ifdef __VMS
     54 #include <in.h>
     55 #include <inet.h>
     56 #endif
     57 
     58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
     59 #undef in_addr_t
     60 #define in_addr_t unsigned long
     61 #endif
     62 
     63 #include <curl/curl.h>
     64 #include "urldata.h"
     65 #include "sendf.h"
     66 #include "hostip.h"
     67 #include "progress.h"
     68 #include "transfer.h"
     69 #include "escape.h"
     70 #include "http.h" /* for HTTP proxy tunnel stuff */
     71 #include "socks.h"
     72 #include "pop3.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 "curl_sasl.h"
     82 #include "curl_md5.h"
     83 #include "warnless.h"
     84 /* The last 3 #include files should be in this order */
     85 #include "curl_printf.h"
     86 #include "curl_memory.h"
     87 #include "memdebug.h"
     88 
     89 /* Local API functions */
     90 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
     91 static CURLcode pop3_do(struct connectdata *conn, bool *done);
     92 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
     93                           bool premature);
     94 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
     95 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
     96 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
     97 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
     98                         int numsocks);
     99 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
    100 static CURLcode pop3_setup_connection(struct connectdata *conn);
    101 static CURLcode pop3_parse_url_options(struct connectdata *conn);
    102 static CURLcode pop3_parse_url_path(struct connectdata *conn);
    103 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
    104 static CURLcode pop3_perform_auth(struct connectdata *conn, const char *mech,
    105                                   const char *initresp);
    106 static CURLcode pop3_continue_auth(struct connectdata *conn, const char *resp);
    107 static void pop3_get_message(char *buffer, char **outptr);
    108 
    109 /*
    110  * POP3 protocol handler.
    111  */
    112 
    113 const struct Curl_handler Curl_handler_pop3 = {
    114   "POP3",                           /* scheme */
    115   pop3_setup_connection,            /* setup_connection */
    116   pop3_do,                          /* do_it */
    117   pop3_done,                        /* done */
    118   ZERO_NULL,                        /* do_more */
    119   pop3_connect,                     /* connect_it */
    120   pop3_multi_statemach,             /* connecting */
    121   pop3_doing,                       /* doing */
    122   pop3_getsock,                     /* proto_getsock */
    123   pop3_getsock,                     /* doing_getsock */
    124   ZERO_NULL,                        /* domore_getsock */
    125   ZERO_NULL,                        /* perform_getsock */
    126   pop3_disconnect,                  /* disconnect */
    127   ZERO_NULL,                        /* readwrite */
    128   ZERO_NULL,                        /* connection_check */
    129   PORT_POP3,                        /* defport */
    130   CURLPROTO_POP3,                   /* protocol */
    131   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
    132   PROTOPT_URLOPTIONS
    133 };
    134 
    135 #ifdef USE_SSL
    136 /*
    137  * POP3S protocol handler.
    138  */
    139 
    140 const struct Curl_handler Curl_handler_pop3s = {
    141   "POP3S",                          /* scheme */
    142   pop3_setup_connection,            /* setup_connection */
    143   pop3_do,                          /* do_it */
    144   pop3_done,                        /* done */
    145   ZERO_NULL,                        /* do_more */
    146   pop3_connect,                     /* connect_it */
    147   pop3_multi_statemach,             /* connecting */
    148   pop3_doing,                       /* doing */
    149   pop3_getsock,                     /* proto_getsock */
    150   pop3_getsock,                     /* doing_getsock */
    151   ZERO_NULL,                        /* domore_getsock */
    152   ZERO_NULL,                        /* perform_getsock */
    153   pop3_disconnect,                  /* disconnect */
    154   ZERO_NULL,                        /* readwrite */
    155   ZERO_NULL,                        /* connection_check */
    156   PORT_POP3S,                       /* defport */
    157   CURLPROTO_POP3S,                  /* protocol */
    158   PROTOPT_CLOSEACTION | PROTOPT_SSL
    159   | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
    160 };
    161 #endif
    162 
    163 /* SASL parameters for the pop3 protocol */
    164 static const struct SASLproto saslpop3 = {
    165   "pop",                      /* The service name */
    166   '*',                        /* Code received when continuation is expected */
    167   '+',                        /* Code to receive upon authentication success */
    168   255 - 8,                    /* Maximum initial response length (no max) */
    169   pop3_perform_auth,          /* Send authentication command */
    170   pop3_continue_auth,         /* Send authentication continuation */
    171   pop3_get_message            /* Get SASL response message */
    172 };
    173 
    174 #ifdef USE_SSL
    175 static void pop3_to_pop3s(struct connectdata *conn)
    176 {
    177   /* Change the connection handler */
    178   conn->handler = &Curl_handler_pop3s;
    179 
    180   /* Set the connection's upgraded to TLS flag */
    181   conn->tls_upgraded = TRUE;
    182 }
    183 #else
    184 #define pop3_to_pop3s(x) Curl_nop_stmt
    185 #endif
    186 
    187 /***********************************************************************
    188  *
    189  * pop3_endofresp()
    190  *
    191  * Checks for an ending POP3 status code at the start of the given string, but
    192  * also detects the APOP timestamp from the server greeting and various
    193  * capabilities from the CAPA response including the supported authentication
    194  * types and allowed SASL mechanisms.
    195  */
    196 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
    197                            int *resp)
    198 {
    199   struct pop3_conn *pop3c = &conn->proto.pop3c;
    200 
    201   /* Do we have an error response? */
    202   if(len >= 4 && !memcmp("-ERR", line, 4)) {
    203     *resp = '-';
    204 
    205     return TRUE;
    206   }
    207 
    208   /* Are we processing CAPA command responses? */
    209   if(pop3c->state == POP3_CAPA) {
    210     /* Do we have the terminating line? */
    211     if(len >= 1 && line[0] == '.')
    212       /* Treat the response as a success */
    213       *resp = '+';
    214     else
    215       /* Treat the response as an untagged continuation */
    216       *resp = '*';
    217 
    218     return TRUE;
    219   }
    220 
    221   /* Do we have a success response? */
    222   if(len >= 3 && !memcmp("+OK", line, 3)) {
    223     *resp = '+';
    224 
    225     return TRUE;
    226   }
    227 
    228   /* Do we have a continuation response? */
    229   if(len >= 1 && line[0] == '+') {
    230     *resp = '*';
    231 
    232     return TRUE;
    233   }
    234 
    235   return FALSE; /* Nothing for us */
    236 }
    237 
    238 /***********************************************************************
    239  *
    240  * pop3_get_message()
    241  *
    242  * Gets the authentication message from the response buffer.
    243  */
    244 static void pop3_get_message(char *buffer, char **outptr)
    245 {
    246   size_t len = strlen(buffer);
    247   char *message = NULL;
    248 
    249   if(len > 2) {
    250     /* Find the start of the message */
    251     len -= 2;
    252     for(message = buffer + 2; *message == ' ' || *message == '\t';
    253         message++, len--)
    254       ;
    255 
    256     /* Find the end of the message */
    257     for(; len--;)
    258       if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
    259          message[len] != '\t')
    260         break;
    261 
    262     /* Terminate the message */
    263     if(++len) {
    264       message[len] = '\0';
    265     }
    266   }
    267   else
    268     /* junk input => zero length output */
    269     message = &buffer[len];
    270 
    271   *outptr = message;
    272 }
    273 
    274 /***********************************************************************
    275  *
    276  * state()
    277  *
    278  * This is the ONLY way to change POP3 state!
    279  */
    280 static void state(struct connectdata *conn, pop3state newstate)
    281 {
    282   struct pop3_conn *pop3c = &conn->proto.pop3c;
    283 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
    284   /* for debug purposes */
    285   static const char * const names[] = {
    286     "STOP",
    287     "SERVERGREET",
    288     "CAPA",
    289     "STARTTLS",
    290     "UPGRADETLS",
    291     "AUTH",
    292     "APOP",
    293     "USER",
    294     "PASS",
    295     "COMMAND",
    296     "QUIT",
    297     /* LAST */
    298   };
    299 
    300   if(pop3c->state != newstate)
    301     infof(conn->data, "POP3 %p state change from %s to %s\n",
    302           (void *)pop3c, names[pop3c->state], names[newstate]);
    303 #endif
    304 
    305   pop3c->state = newstate;
    306 }
    307 
    308 /***********************************************************************
    309  *
    310  * pop3_perform_capa()
    311  *
    312  * Sends the CAPA command in order to obtain a list of server side supported
    313  * capabilities.
    314  */
    315 static CURLcode pop3_perform_capa(struct connectdata *conn)
    316 {
    317   CURLcode result = CURLE_OK;
    318   struct pop3_conn *pop3c = &conn->proto.pop3c;
    319 
    320   pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
    321   pop3c->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
    322   pop3c->tls_supported = FALSE;           /* Clear the TLS capability */
    323 
    324   /* Send the CAPA command */
    325   result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA");
    326 
    327   if(!result)
    328     state(conn, POP3_CAPA);
    329 
    330   return result;
    331 }
    332 
    333 /***********************************************************************
    334  *
    335  * pop3_perform_starttls()
    336  *
    337  * Sends the STLS command to start the upgrade to TLS.
    338  */
    339 static CURLcode pop3_perform_starttls(struct connectdata *conn)
    340 {
    341   CURLcode result = CURLE_OK;
    342 
    343   /* Send the STLS command */
    344   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS");
    345 
    346   if(!result)
    347     state(conn, POP3_STARTTLS);
    348 
    349   return result;
    350 }
    351 
    352 /***********************************************************************
    353  *
    354  * pop3_perform_upgrade_tls()
    355  *
    356  * Performs the upgrade to TLS.
    357  */
    358 static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
    359 {
    360   CURLcode result = CURLE_OK;
    361   struct pop3_conn *pop3c = &conn->proto.pop3c;
    362 
    363   /* Start the SSL connection */
    364   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
    365 
    366   if(!result) {
    367     if(pop3c->state != POP3_UPGRADETLS)
    368       state(conn, POP3_UPGRADETLS);
    369 
    370     if(pop3c->ssldone) {
    371       pop3_to_pop3s(conn);
    372       result = pop3_perform_capa(conn);
    373     }
    374   }
    375 
    376   return result;
    377 }
    378 
    379 /***********************************************************************
    380  *
    381  * pop3_perform_user()
    382  *
    383  * Sends a clear text USER command to authenticate with.
    384  */
    385 static CURLcode pop3_perform_user(struct connectdata *conn)
    386 {
    387   CURLcode result = CURLE_OK;
    388 
    389   /* Check we have a username and password to authenticate with and end the
    390      connect phase if we don't */
    391   if(!conn->bits.user_passwd) {
    392     state(conn, POP3_STOP);
    393 
    394     return result;
    395   }
    396 
    397   /* Send the USER command */
    398   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
    399                          conn->user ? conn->user : "");
    400   if(!result)
    401     state(conn, POP3_USER);
    402 
    403   return result;
    404 }
    405 
    406 #ifndef CURL_DISABLE_CRYPTO_AUTH
    407 /***********************************************************************
    408  *
    409  * pop3_perform_apop()
    410  *
    411  * Sends an APOP command to authenticate with.
    412  */
    413 static CURLcode pop3_perform_apop(struct connectdata *conn)
    414 {
    415   CURLcode result = CURLE_OK;
    416   struct pop3_conn *pop3c = &conn->proto.pop3c;
    417   size_t i;
    418   MD5_context *ctxt;
    419   unsigned char digest[MD5_DIGEST_LEN];
    420   char secret[2 * MD5_DIGEST_LEN + 1];
    421 
    422   /* Check we have a username and password to authenticate with and end the
    423      connect phase if we don't */
    424   if(!conn->bits.user_passwd) {
    425     state(conn, POP3_STOP);
    426 
    427     return result;
    428   }
    429 
    430   /* Create the digest */
    431   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
    432   if(!ctxt)
    433     return CURLE_OUT_OF_MEMORY;
    434 
    435   Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
    436                   curlx_uztoui(strlen(pop3c->apoptimestamp)));
    437 
    438   Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
    439                   curlx_uztoui(strlen(conn->passwd)));
    440 
    441   /* Finalise the digest */
    442   Curl_MD5_final(ctxt, digest);
    443 
    444   /* Convert the calculated 16 octet digest into a 32 byte hex string */
    445   for(i = 0; i < MD5_DIGEST_LEN; i++)
    446     msnprintf(&secret[2 * i], 3, "%02x", digest[i]);
    447 
    448   result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
    449 
    450   if(!result)
    451     state(conn, POP3_APOP);
    452 
    453   return result;
    454 }
    455 #endif
    456 
    457 /***********************************************************************
    458  *
    459  * pop3_perform_auth()
    460  *
    461  * Sends an AUTH command allowing the client to login with the given SASL
    462  * authentication mechanism.
    463  */
    464 static CURLcode pop3_perform_auth(struct connectdata *conn,
    465                                   const char *mech,
    466                                   const char *initresp)
    467 {
    468   CURLcode result = CURLE_OK;
    469   struct pop3_conn *pop3c = &conn->proto.pop3c;
    470 
    471   if(initresp) {                                  /* AUTH <mech> ...<crlf> */
    472     /* Send the AUTH command with the initial response */
    473     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
    474   }
    475   else {
    476     /* Send the AUTH command */
    477     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
    478   }
    479 
    480   return result;
    481 }
    482 
    483 /***********************************************************************
    484  *
    485  * pop3_continue_auth()
    486  *
    487  * Sends SASL continuation data or cancellation.
    488  */
    489 static CURLcode pop3_continue_auth(struct connectdata *conn,
    490                                    const char *resp)
    491 {
    492   struct pop3_conn *pop3c = &conn->proto.pop3c;
    493 
    494   return Curl_pp_sendf(&pop3c->pp, "%s", resp);
    495 }
    496 
    497 /***********************************************************************
    498  *
    499  * pop3_perform_authentication()
    500  *
    501  * Initiates the authentication sequence, with the appropriate SASL
    502  * authentication mechanism, falling back to APOP and clear text should a
    503  * common mechanism not be available between the client and server.
    504  */
    505 static CURLcode pop3_perform_authentication(struct connectdata *conn)
    506 {
    507   CURLcode result = CURLE_OK;
    508   struct pop3_conn *pop3c = &conn->proto.pop3c;
    509   saslprogress progress = SASL_IDLE;
    510 
    511   /* Check we have enough data to authenticate with and end the
    512      connect phase if we don't */
    513   if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) {
    514     state(conn, POP3_STOP);
    515     return result;
    516   }
    517 
    518   if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
    519     /* Calculate the SASL login details */
    520     result = Curl_sasl_start(&pop3c->sasl, conn, FALSE, &progress);
    521 
    522     if(!result)
    523       if(progress == SASL_INPROGRESS)
    524         state(conn, POP3_AUTH);
    525   }
    526 
    527   if(!result && progress == SASL_IDLE) {
    528 #ifndef CURL_DISABLE_CRYPTO_AUTH
    529     if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
    530       /* Perform APOP authentication */
    531       result = pop3_perform_apop(conn);
    532     else
    533 #endif
    534     if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
    535       /* Perform clear text authentication */
    536       result = pop3_perform_user(conn);
    537     else {
    538       /* Other mechanisms not supported */
    539       infof(conn->data, "No known authentication mechanisms supported!\n");
    540       result = CURLE_LOGIN_DENIED;
    541     }
    542   }
    543 
    544   return result;
    545 }
    546 
    547 /***********************************************************************
    548  *
    549  * pop3_perform_command()
    550  *
    551  * Sends a POP3 based command.
    552  */
    553 static CURLcode pop3_perform_command(struct connectdata *conn)
    554 {
    555   CURLcode result = CURLE_OK;
    556   struct Curl_easy *data = conn->data;
    557   struct POP3 *pop3 = data->req.protop;
    558   const char *command = NULL;
    559 
    560   /* Calculate the default command */
    561   if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
    562     command = "LIST";
    563 
    564     if(pop3->id[0] != '\0')
    565       /* Message specific LIST so skip the BODY transfer */
    566       pop3->transfer = FTPTRANSFER_INFO;
    567   }
    568   else
    569     command = "RETR";
    570 
    571   /* Send the command */
    572   if(pop3->id[0] != '\0')
    573     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
    574                            (pop3->custom && pop3->custom[0] != '\0' ?
    575                             pop3->custom : command), pop3->id);
    576   else
    577     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s",
    578                            (pop3->custom && pop3->custom[0] != '\0' ?
    579                             pop3->custom : command));
    580 
    581   if(!result)
    582     state(conn, POP3_COMMAND);
    583 
    584   return result;
    585 }
    586 
    587 /***********************************************************************
    588  *
    589  * pop3_perform_quit()
    590  *
    591  * Performs the quit action prior to sclose() be called.
    592  */
    593 static CURLcode pop3_perform_quit(struct connectdata *conn)
    594 {
    595   CURLcode result = CURLE_OK;
    596 
    597   /* Send the QUIT command */
    598   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT");
    599 
    600   if(!result)
    601     state(conn, POP3_QUIT);
    602 
    603   return result;
    604 }
    605 
    606 /* For the initial server greeting */
    607 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
    608                                             int pop3code,
    609                                             pop3state instate)
    610 {
    611   CURLcode result = CURLE_OK;
    612   struct Curl_easy *data = conn->data;
    613   struct pop3_conn *pop3c = &conn->proto.pop3c;
    614   const char *line = data->state.buffer;
    615   size_t len = strlen(line);
    616 
    617   (void)instate; /* no use for this yet */
    618 
    619   if(pop3code != '+') {
    620     failf(data, "Got unexpected pop3-server response");
    621     result = CURLE_WEIRD_SERVER_REPLY;
    622   }
    623   else {
    624     /* Does the server support APOP authentication? */
    625     if(len >= 4 && line[len - 2] == '>') {
    626       /* Look for the APOP timestamp */
    627       size_t i;
    628       for(i = 3; i < len - 2; ++i) {
    629         if(line[i] == '<') {
    630           /* Calculate the length of the timestamp */
    631           size_t timestamplen = len - 1 - i;
    632           char *at;
    633           if(!timestamplen)
    634             break;
    635 
    636           /* Allocate some memory for the timestamp */
    637           pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
    638 
    639           if(!pop3c->apoptimestamp)
    640             break;
    641 
    642           /* Copy the timestamp */
    643           memcpy(pop3c->apoptimestamp, line + i, timestamplen);
    644           pop3c->apoptimestamp[timestamplen] = '\0';
    645 
    646           /* If the timestamp does not contain '@' it is not (as required by
    647              RFC-1939) conformant to the RFC-822 message id syntax, and we
    648              therefore do not use APOP authentication. */
    649           at = strchr(pop3c->apoptimestamp, '@');
    650           if(!at)
    651             Curl_safefree(pop3c->apoptimestamp);
    652           else
    653             /* Store the APOP capability */
    654             pop3c->authtypes |= POP3_TYPE_APOP;
    655           break;
    656         }
    657       }
    658     }
    659 
    660     result = pop3_perform_capa(conn);
    661   }
    662 
    663   return result;
    664 }
    665 
    666 /* For CAPA responses */
    667 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
    668                                      pop3state instate)
    669 {
    670   CURLcode result = CURLE_OK;
    671   struct Curl_easy *data = conn->data;
    672   struct pop3_conn *pop3c = &conn->proto.pop3c;
    673   const char *line = data->state.buffer;
    674   size_t len = strlen(line);
    675 
    676   (void)instate; /* no use for this yet */
    677 
    678   /* Do we have a untagged continuation response? */
    679   if(pop3code == '*') {
    680     /* Does the server support the STLS capability? */
    681     if(len >= 4 && !memcmp(line, "STLS", 4))
    682       pop3c->tls_supported = TRUE;
    683 
    684     /* Does the server support clear text authentication? */
    685     else if(len >= 4 && !memcmp(line, "USER", 4))
    686       pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
    687 
    688     /* Does the server support SASL based authentication? */
    689     else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
    690       pop3c->authtypes |= POP3_TYPE_SASL;
    691 
    692       /* Advance past the SASL keyword */
    693       line += 5;
    694       len -= 5;
    695 
    696       /* Loop through the data line */
    697       for(;;) {
    698         size_t llen;
    699         size_t wordlen;
    700         unsigned int mechbit;
    701 
    702         while(len &&
    703               (*line == ' ' || *line == '\t' ||
    704                *line == '\r' || *line == '\n')) {
    705 
    706           line++;
    707           len--;
    708         }
    709 
    710         if(!len)
    711           break;
    712 
    713         /* Extract the word */
    714         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
    715               line[wordlen] != '\t' && line[wordlen] != '\r' &&
    716               line[wordlen] != '\n';)
    717           wordlen++;
    718 
    719         /* Test the word for a matching authentication mechanism */
    720         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
    721         if(mechbit && llen == wordlen)
    722           pop3c->sasl.authmechs |= mechbit;
    723 
    724         line += wordlen;
    725         len -= wordlen;
    726       }
    727     }
    728   }
    729   else if(pop3code == '+') {
    730     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
    731       /* We don't have a SSL/TLS connection yet, but SSL is requested */
    732       if(pop3c->tls_supported)
    733         /* Switch to TLS connection now */
    734         result = pop3_perform_starttls(conn);
    735       else if(data->set.use_ssl == CURLUSESSL_TRY)
    736         /* Fallback and carry on with authentication */
    737         result = pop3_perform_authentication(conn);
    738       else {
    739         failf(data, "STLS not supported.");
    740         result = CURLE_USE_SSL_FAILED;
    741       }
    742     }
    743     else
    744       result = pop3_perform_authentication(conn);
    745   }
    746   else {
    747     /* Clear text is supported when CAPA isn't recognised */
    748     pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
    749 
    750     result = pop3_perform_authentication(conn);
    751   }
    752 
    753   return result;
    754 }
    755 
    756 /* For STARTTLS responses */
    757 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
    758                                          int pop3code,
    759                                          pop3state instate)
    760 {
    761   CURLcode result = CURLE_OK;
    762   struct Curl_easy *data = conn->data;
    763 
    764   (void)instate; /* no use for this yet */
    765 
    766   if(pop3code != '+') {
    767     if(data->set.use_ssl != CURLUSESSL_TRY) {
    768       failf(data, "STARTTLS denied");
    769       result = CURLE_USE_SSL_FAILED;
    770     }
    771     else
    772       result = pop3_perform_authentication(conn);
    773   }
    774   else
    775     result = pop3_perform_upgrade_tls(conn);
    776 
    777   return result;
    778 }
    779 
    780 /* For SASL authentication responses */
    781 static CURLcode pop3_state_auth_resp(struct connectdata *conn,
    782                                      int pop3code,
    783                                      pop3state instate)
    784 {
    785   CURLcode result = CURLE_OK;
    786   struct Curl_easy *data = conn->data;
    787   struct pop3_conn *pop3c = &conn->proto.pop3c;
    788   saslprogress progress;
    789 
    790   (void)instate; /* no use for this yet */
    791 
    792   result = Curl_sasl_continue(&pop3c->sasl, conn, pop3code, &progress);
    793   if(!result)
    794     switch(progress) {
    795     case SASL_DONE:
    796       state(conn, POP3_STOP);  /* Authenticated */
    797       break;
    798     case SASL_IDLE:            /* No mechanism left after cancellation */
    799 #ifndef CURL_DISABLE_CRYPTO_AUTH
    800       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
    801         /* Perform APOP authentication */
    802         result = pop3_perform_apop(conn);
    803       else
    804 #endif
    805       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
    806         /* Perform clear text authentication */
    807         result = pop3_perform_user(conn);
    808       else {
    809         failf(data, "Authentication cancelled");
    810         result = CURLE_LOGIN_DENIED;
    811       }
    812       break;
    813     default:
    814       break;
    815     }
    816 
    817   return result;
    818 }
    819 
    820 #ifndef CURL_DISABLE_CRYPTO_AUTH
    821 /* For APOP responses */
    822 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
    823                                      pop3state instate)
    824 {
    825   CURLcode result = CURLE_OK;
    826   struct Curl_easy *data = conn->data;
    827 
    828   (void)instate; /* no use for this yet */
    829 
    830   if(pop3code != '+') {
    831     failf(data, "Authentication failed: %d", pop3code);
    832     result = CURLE_LOGIN_DENIED;
    833   }
    834   else
    835     /* End of connect phase */
    836     state(conn, POP3_STOP);
    837 
    838   return result;
    839 }
    840 #endif
    841 
    842 /* For USER responses */
    843 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
    844                                      pop3state instate)
    845 {
    846   CURLcode result = CURLE_OK;
    847   struct Curl_easy *data = conn->data;
    848 
    849   (void)instate; /* no use for this yet */
    850 
    851   if(pop3code != '+') {
    852     failf(data, "Access denied. %c", pop3code);
    853     result = CURLE_LOGIN_DENIED;
    854   }
    855   else
    856     /* Send the PASS command */
    857     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
    858                            conn->passwd ? conn->passwd : "");
    859   if(!result)
    860     state(conn, POP3_PASS);
    861 
    862   return result;
    863 }
    864 
    865 /* For PASS responses */
    866 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
    867                                      pop3state instate)
    868 {
    869   CURLcode result = CURLE_OK;
    870   struct Curl_easy *data = conn->data;
    871 
    872   (void)instate; /* no use for this yet */
    873 
    874   if(pop3code != '+') {
    875     failf(data, "Access denied. %c", pop3code);
    876     result = CURLE_LOGIN_DENIED;
    877   }
    878   else
    879     /* End of connect phase */
    880     state(conn, POP3_STOP);
    881 
    882   return result;
    883 }
    884 
    885 /* For command responses */
    886 static CURLcode pop3_state_command_resp(struct connectdata *conn,
    887                                         int pop3code,
    888                                         pop3state instate)
    889 {
    890   CURLcode result = CURLE_OK;
    891   struct Curl_easy *data = conn->data;
    892   struct POP3 *pop3 = data->req.protop;
    893   struct pop3_conn *pop3c = &conn->proto.pop3c;
    894   struct pingpong *pp = &pop3c->pp;
    895 
    896   (void)instate; /* no use for this yet */
    897 
    898   if(pop3code != '+') {
    899     state(conn, POP3_STOP);
    900     return CURLE_RECV_ERROR;
    901   }
    902 
    903   /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
    904      EOB string so count this is two matching bytes. This is necessary to make
    905      the code detect the EOB if the only data than comes now is %2e CR LF like
    906      when there is no body to return. */
    907   pop3c->eob = 2;
    908 
    909   /* But since this initial CR LF pair is not part of the actual body, we set
    910      the strip counter here so that these bytes won't be delivered. */
    911   pop3c->strip = 2;
    912 
    913   if(pop3->transfer == FTPTRANSFER_BODY) {
    914     /* POP3 download */
    915     Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
    916 
    917     if(pp->cache) {
    918       /* The header "cache" contains a bunch of data that is actually body
    919          content so send it as such. Note that there may even be additional
    920          "headers" after the body */
    921 
    922       if(!data->set.opt_no_body) {
    923         result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
    924         if(result)
    925           return result;
    926       }
    927 
    928       /* Free the cache */
    929       Curl_safefree(pp->cache);
    930 
    931       /* Reset the cache size */
    932       pp->cache_size = 0;
    933     }
    934   }
    935 
    936   /* End of DO phase */
    937   state(conn, POP3_STOP);
    938 
    939   return result;
    940 }
    941 
    942 static CURLcode pop3_statemach_act(struct connectdata *conn)
    943 {
    944   CURLcode result = CURLE_OK;
    945   curl_socket_t sock = conn->sock[FIRSTSOCKET];
    946   int pop3code;
    947   struct pop3_conn *pop3c = &conn->proto.pop3c;
    948   struct pingpong *pp = &pop3c->pp;
    949   size_t nread = 0;
    950 
    951   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
    952   if(pop3c->state == POP3_UPGRADETLS)
    953     return pop3_perform_upgrade_tls(conn);
    954 
    955   /* Flush any data that needs to be sent */
    956   if(pp->sendleft)
    957     return Curl_pp_flushsend(pp);
    958 
    959  do {
    960     /* Read the response from the server */
    961     result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
    962     if(result)
    963       return result;
    964 
    965     if(!pop3code)
    966       break;
    967 
    968     /* We have now received a full POP3 server response */
    969     switch(pop3c->state) {
    970     case POP3_SERVERGREET:
    971       result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
    972       break;
    973 
    974     case POP3_CAPA:
    975       result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
    976       break;
    977 
    978     case POP3_STARTTLS:
    979       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
    980       break;
    981 
    982     case POP3_AUTH:
    983       result = pop3_state_auth_resp(conn, pop3code, pop3c->state);
    984       break;
    985 
    986 #ifndef CURL_DISABLE_CRYPTO_AUTH
    987     case POP3_APOP:
    988       result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
    989       break;
    990 #endif
    991 
    992     case POP3_USER:
    993       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
    994       break;
    995 
    996     case POP3_PASS:
    997       result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
    998       break;
    999 
   1000     case POP3_COMMAND:
   1001       result = pop3_state_command_resp(conn, pop3code, pop3c->state);
   1002       break;
   1003 
   1004     case POP3_QUIT:
   1005       /* fallthrough, just stop! */
   1006     default:
   1007       /* internal error */
   1008       state(conn, POP3_STOP);
   1009       break;
   1010     }
   1011   } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
   1012 
   1013   return result;
   1014 }
   1015 
   1016 /* Called repeatedly until done from multi.c */
   1017 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
   1018 {
   1019   CURLcode result = CURLE_OK;
   1020   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1021 
   1022   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
   1023     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
   1024     if(result || !pop3c->ssldone)
   1025       return result;
   1026   }
   1027 
   1028   result = Curl_pp_statemach(&pop3c->pp, FALSE, FALSE);
   1029   *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
   1030 
   1031   return result;
   1032 }
   1033 
   1034 static CURLcode pop3_block_statemach(struct connectdata *conn,
   1035                                      bool disconnecting)
   1036 {
   1037   CURLcode result = CURLE_OK;
   1038   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1039 
   1040   while(pop3c->state != POP3_STOP && !result)
   1041     result = Curl_pp_statemach(&pop3c->pp, TRUE, disconnecting);
   1042 
   1043   return result;
   1044 }
   1045 
   1046 /* Allocate and initialize the POP3 struct for the current Curl_easy if
   1047    required */
   1048 static CURLcode pop3_init(struct connectdata *conn)
   1049 {
   1050   CURLcode result = CURLE_OK;
   1051   struct Curl_easy *data = conn->data;
   1052   struct POP3 *pop3;
   1053 
   1054   pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
   1055   if(!pop3)
   1056     result = CURLE_OUT_OF_MEMORY;
   1057 
   1058   return result;
   1059 }
   1060 
   1061 /* For the POP3 "protocol connect" and "doing" phases only */
   1062 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
   1063                         int numsocks)
   1064 {
   1065   return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
   1066 }
   1067 
   1068 /***********************************************************************
   1069  *
   1070  * pop3_connect()
   1071  *
   1072  * This function should do everything that is to be considered a part of the
   1073  * connection phase.
   1074  *
   1075  * The variable 'done' points to will be TRUE if the protocol-layer connect
   1076  * phase is done when this function returns, or FALSE if not.
   1077  */
   1078 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
   1079 {
   1080   CURLcode result = CURLE_OK;
   1081   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1082   struct pingpong *pp = &pop3c->pp;
   1083 
   1084   *done = FALSE; /* default to not done yet */
   1085 
   1086   /* We always support persistent connections in POP3 */
   1087   connkeep(conn, "POP3 default");
   1088 
   1089   /* Set the default response time-out */
   1090   pp->response_time = RESP_TIMEOUT;
   1091   pp->statemach_act = pop3_statemach_act;
   1092   pp->endofresp = pop3_endofresp;
   1093   pp->conn = conn;
   1094 
   1095   /* Set the default preferred authentication type and mechanism */
   1096   pop3c->preftype = POP3_TYPE_ANY;
   1097   Curl_sasl_init(&pop3c->sasl, &saslpop3);
   1098 
   1099   /* Initialise the pingpong layer */
   1100   Curl_pp_init(pp);
   1101 
   1102   /* Parse the URL options */
   1103   result = pop3_parse_url_options(conn);
   1104   if(result)
   1105     return result;
   1106 
   1107   /* Start off waiting for the server greeting response */
   1108   state(conn, POP3_SERVERGREET);
   1109 
   1110   result = pop3_multi_statemach(conn, done);
   1111 
   1112   return result;
   1113 }
   1114 
   1115 /***********************************************************************
   1116  *
   1117  * pop3_done()
   1118  *
   1119  * The DONE function. This does what needs to be done after a single DO has
   1120  * performed.
   1121  *
   1122  * Input argument is already checked for validity.
   1123  */
   1124 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
   1125                           bool premature)
   1126 {
   1127   CURLcode result = CURLE_OK;
   1128   struct Curl_easy *data = conn->data;
   1129   struct POP3 *pop3 = data->req.protop;
   1130 
   1131   (void)premature;
   1132 
   1133   if(!pop3)
   1134     return CURLE_OK;
   1135 
   1136   if(status) {
   1137     connclose(conn, "POP3 done with bad status");
   1138     result = status;         /* use the already set error code */
   1139   }
   1140 
   1141   /* Cleanup our per-request based variables */
   1142   Curl_safefree(pop3->id);
   1143   Curl_safefree(pop3->custom);
   1144 
   1145   /* Clear the transfer mode for the next request */
   1146   pop3->transfer = FTPTRANSFER_BODY;
   1147 
   1148   return result;
   1149 }
   1150 
   1151 /***********************************************************************
   1152  *
   1153  * pop3_perform()
   1154  *
   1155  * This is the actual DO function for POP3. Get a message/listing according to
   1156  * the options previously setup.
   1157  */
   1158 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
   1159                              bool *dophase_done)
   1160 {
   1161   /* This is POP3 and no proxy */
   1162   CURLcode result = CURLE_OK;
   1163   struct POP3 *pop3 = conn->data->req.protop;
   1164 
   1165   DEBUGF(infof(conn->data, "DO phase starts\n"));
   1166 
   1167   if(conn->data->set.opt_no_body) {
   1168     /* Requested no body means no transfer */
   1169     pop3->transfer = FTPTRANSFER_INFO;
   1170   }
   1171 
   1172   *dophase_done = FALSE; /* not done yet */
   1173 
   1174   /* Start the first command in the DO phase */
   1175   result = pop3_perform_command(conn);
   1176   if(result)
   1177     return result;
   1178 
   1179   /* Run the state-machine */
   1180   result = pop3_multi_statemach(conn, dophase_done);
   1181 
   1182   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
   1183 
   1184   if(*dophase_done)
   1185     DEBUGF(infof(conn->data, "DO phase is complete\n"));
   1186 
   1187   return result;
   1188 }
   1189 
   1190 /***********************************************************************
   1191  *
   1192  * pop3_do()
   1193  *
   1194  * This function is registered as 'curl_do' function. It decodes the path
   1195  * parts etc as a wrapper to the actual DO function (pop3_perform).
   1196  *
   1197  * The input argument is already checked for validity.
   1198  */
   1199 static CURLcode pop3_do(struct connectdata *conn, bool *done)
   1200 {
   1201   CURLcode result = CURLE_OK;
   1202 
   1203   *done = FALSE; /* default to false */
   1204 
   1205   /* Parse the URL path */
   1206   result = pop3_parse_url_path(conn);
   1207   if(result)
   1208     return result;
   1209 
   1210   /* Parse the custom request */
   1211   result = pop3_parse_custom_request(conn);
   1212   if(result)
   1213     return result;
   1214 
   1215   result = pop3_regular_transfer(conn, done);
   1216 
   1217   return result;
   1218 }
   1219 
   1220 /***********************************************************************
   1221  *
   1222  * pop3_disconnect()
   1223  *
   1224  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
   1225  * resources. BLOCKING.
   1226  */
   1227 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
   1228 {
   1229   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1230 
   1231   /* We cannot send quit unconditionally. If this connection is stale or
   1232      bad in any way, sending quit and waiting around here will make the
   1233      disconnect wait in vain and cause more problems than we need to. */
   1234 
   1235   /* The POP3 session may or may not have been allocated/setup at this
   1236      point! */
   1237   if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
   1238     if(!pop3_perform_quit(conn))
   1239       (void)pop3_block_statemach(conn, TRUE); /* ignore errors on QUIT */
   1240 
   1241   /* Disconnect from the server */
   1242   Curl_pp_disconnect(&pop3c->pp);
   1243 
   1244   /* Cleanup the SASL module */
   1245   Curl_sasl_cleanup(conn, pop3c->sasl.authused);
   1246 
   1247   /* Cleanup our connection based variables */
   1248   Curl_safefree(pop3c->apoptimestamp);
   1249 
   1250   return CURLE_OK;
   1251 }
   1252 
   1253 /* Call this when the DO phase has completed */
   1254 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
   1255 {
   1256   (void)conn;
   1257   (void)connected;
   1258 
   1259   return CURLE_OK;
   1260 }
   1261 
   1262 /* Called from multi.c while DOing */
   1263 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
   1264 {
   1265   CURLcode result = pop3_multi_statemach(conn, dophase_done);
   1266 
   1267   if(result)
   1268     DEBUGF(infof(conn->data, "DO phase failed\n"));
   1269   else if(*dophase_done) {
   1270     result = pop3_dophase_done(conn, FALSE /* not connected */);
   1271 
   1272     DEBUGF(infof(conn->data, "DO phase is complete\n"));
   1273   }
   1274 
   1275   return result;
   1276 }
   1277 
   1278 /***********************************************************************
   1279  *
   1280  * pop3_regular_transfer()
   1281  *
   1282  * The input argument is already checked for validity.
   1283  *
   1284  * Performs all commands done before a regular transfer between a local and a
   1285  * remote host.
   1286  */
   1287 static CURLcode pop3_regular_transfer(struct connectdata *conn,
   1288                                       bool *dophase_done)
   1289 {
   1290   CURLcode result = CURLE_OK;
   1291   bool connected = FALSE;
   1292   struct Curl_easy *data = conn->data;
   1293 
   1294   /* Make sure size is unknown at this point */
   1295   data->req.size = -1;
   1296 
   1297   /* Set the progress data */
   1298   Curl_pgrsSetUploadCounter(data, 0);
   1299   Curl_pgrsSetDownloadCounter(data, 0);
   1300   Curl_pgrsSetUploadSize(data, -1);
   1301   Curl_pgrsSetDownloadSize(data, -1);
   1302 
   1303   /* Carry out the perform */
   1304   result = pop3_perform(conn, &connected, dophase_done);
   1305 
   1306   /* Perform post DO phase operations if necessary */
   1307   if(!result && *dophase_done)
   1308     result = pop3_dophase_done(conn, connected);
   1309 
   1310   return result;
   1311 }
   1312 
   1313 static CURLcode pop3_setup_connection(struct connectdata *conn)
   1314 {
   1315   /* Initialise the POP3 layer */
   1316   CURLcode result = pop3_init(conn);
   1317   if(result)
   1318     return result;
   1319 
   1320   /* Clear the TLS upgraded flag */
   1321   conn->tls_upgraded = FALSE;
   1322 
   1323   return CURLE_OK;
   1324 }
   1325 
   1326 /***********************************************************************
   1327  *
   1328  * pop3_parse_url_options()
   1329  *
   1330  * Parse the URL login options.
   1331  */
   1332 static CURLcode pop3_parse_url_options(struct connectdata *conn)
   1333 {
   1334   CURLcode result = CURLE_OK;
   1335   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1336   const char *ptr = conn->options;
   1337 
   1338   pop3c->sasl.resetprefs = TRUE;
   1339 
   1340   while(!result && ptr && *ptr) {
   1341     const char *key = ptr;
   1342     const char *value;
   1343 
   1344     while(*ptr && *ptr != '=')
   1345         ptr++;
   1346 
   1347     value = ptr + 1;
   1348 
   1349     while(*ptr && *ptr != ';')
   1350       ptr++;
   1351 
   1352     if(strncasecompare(key, "AUTH=", 5)) {
   1353       result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
   1354                                                value, ptr - value);
   1355 
   1356       if(result && strncasecompare(value, "+APOP", ptr - value)) {
   1357         pop3c->preftype = POP3_TYPE_APOP;
   1358         pop3c->sasl.prefmech = SASL_AUTH_NONE;
   1359         result = CURLE_OK;
   1360       }
   1361     }
   1362     else
   1363       result = CURLE_URL_MALFORMAT;
   1364 
   1365     if(*ptr == ';')
   1366       ptr++;
   1367   }
   1368 
   1369   if(pop3c->preftype != POP3_TYPE_APOP)
   1370     switch(pop3c->sasl.prefmech) {
   1371     case SASL_AUTH_NONE:
   1372       pop3c->preftype = POP3_TYPE_NONE;
   1373       break;
   1374     case SASL_AUTH_DEFAULT:
   1375       pop3c->preftype = POP3_TYPE_ANY;
   1376       break;
   1377     default:
   1378       pop3c->preftype = POP3_TYPE_SASL;
   1379       break;
   1380     }
   1381 
   1382   return result;
   1383 }
   1384 
   1385 /***********************************************************************
   1386  *
   1387  * pop3_parse_url_path()
   1388  *
   1389  * Parse the URL path into separate path components.
   1390  */
   1391 static CURLcode pop3_parse_url_path(struct connectdata *conn)
   1392 {
   1393   /* The POP3 struct is already initialised in pop3_connect() */
   1394   struct Curl_easy *data = conn->data;
   1395   struct POP3 *pop3 = data->req.protop;
   1396   const char *path = &data->state.up.path[1]; /* skip leading path */
   1397 
   1398   /* URL decode the path for the message ID */
   1399   return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
   1400 }
   1401 
   1402 /***********************************************************************
   1403  *
   1404  * pop3_parse_custom_request()
   1405  *
   1406  * Parse the custom request.
   1407  */
   1408 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
   1409 {
   1410   CURLcode result = CURLE_OK;
   1411   struct Curl_easy *data = conn->data;
   1412   struct POP3 *pop3 = data->req.protop;
   1413   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
   1414 
   1415   /* URL decode the custom request */
   1416   if(custom)
   1417     result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
   1418 
   1419   return result;
   1420 }
   1421 
   1422 /***********************************************************************
   1423  *
   1424  * Curl_pop3_write()
   1425  *
   1426  * This function scans the body after the end-of-body and writes everything
   1427  * until the end is found.
   1428  */
   1429 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
   1430 {
   1431   /* This code could be made into a special function in the handler struct */
   1432   CURLcode result = CURLE_OK;
   1433   struct Curl_easy *data = conn->data;
   1434   struct SingleRequest *k = &data->req;
   1435 
   1436   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1437   bool strip_dot = FALSE;
   1438   size_t last = 0;
   1439   size_t i;
   1440 
   1441   /* Search through the buffer looking for the end-of-body marker which is
   1442      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
   1443      the eob so the server will have prefixed it with an extra dot which we
   1444      need to strip out. Additionally the marker could of course be spread out
   1445      over 5 different data chunks. */
   1446   for(i = 0; i < nread; i++) {
   1447     size_t prev = pop3c->eob;
   1448 
   1449     switch(str[i]) {
   1450     case 0x0d:
   1451       if(pop3c->eob == 0) {
   1452         pop3c->eob++;
   1453 
   1454         if(i) {
   1455           /* Write out the body part that didn't match */
   1456           result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
   1457                                      i - last);
   1458 
   1459           if(result)
   1460             return result;
   1461 
   1462           last = i;
   1463         }
   1464       }
   1465       else if(pop3c->eob == 3)
   1466         pop3c->eob++;
   1467       else
   1468         /* If the character match wasn't at position 0 or 3 then restart the
   1469            pattern matching */
   1470         pop3c->eob = 1;
   1471       break;
   1472 
   1473     case 0x0a:
   1474       if(pop3c->eob == 1 || pop3c->eob == 4)
   1475         pop3c->eob++;
   1476       else
   1477         /* If the character match wasn't at position 1 or 4 then start the
   1478            search again */
   1479         pop3c->eob = 0;
   1480       break;
   1481 
   1482     case 0x2e:
   1483       if(pop3c->eob == 2)
   1484         pop3c->eob++;
   1485       else if(pop3c->eob == 3) {
   1486         /* We have an extra dot after the CRLF which we need to strip off */
   1487         strip_dot = TRUE;
   1488         pop3c->eob = 0;
   1489       }
   1490       else
   1491         /* If the character match wasn't at position 2 then start the search
   1492            again */
   1493         pop3c->eob = 0;
   1494       break;
   1495 
   1496     default:
   1497       pop3c->eob = 0;
   1498       break;
   1499     }
   1500 
   1501     /* Did we have a partial match which has subsequently failed? */
   1502     if(prev && prev >= pop3c->eob) {
   1503       /* Strip can only be non-zero for the very first mismatch after CRLF
   1504          and then both prev and strip are equal and nothing will be output
   1505          below */
   1506       while(prev && pop3c->strip) {
   1507         prev--;
   1508         pop3c->strip--;
   1509       }
   1510 
   1511       if(prev) {
   1512         /* If the partial match was the CRLF and dot then only write the CRLF
   1513            as the server would have inserted the dot */
   1514         result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB,
   1515                                    strip_dot ? prev - 1 : prev);
   1516 
   1517         if(result)
   1518           return result;
   1519 
   1520         last = i;
   1521         strip_dot = FALSE;
   1522       }
   1523     }
   1524   }
   1525 
   1526   if(pop3c->eob == POP3_EOB_LEN) {
   1527     /* We have a full match so the transfer is done, however we must transfer
   1528     the CRLF at the start of the EOB as this is considered to be part of the
   1529     message as per RFC-1939, sect. 3 */
   1530     result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
   1531 
   1532     k->keepon &= ~KEEP_RECV;
   1533     pop3c->eob = 0;
   1534 
   1535     return result;
   1536   }
   1537 
   1538   if(pop3c->eob)
   1539     /* While EOB is matching nothing should be output */
   1540     return CURLE_OK;
   1541 
   1542   if(nread - last) {
   1543     result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
   1544                                nread - last);
   1545   }
   1546 
   1547   return result;
   1548 }
   1549 
   1550 #endif /* CURL_DISABLE_POP3 */
   1551