Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2018, 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 && !memcmp(line, ".", 1))
    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 && !memcmp("+", line, 1)) {
    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     snprintf(&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   size_t i;
    617 
    618   (void)instate; /* no use for this yet */
    619 
    620   if(pop3code != '+') {
    621     failf(data, "Got unexpected pop3-server response");
    622     result = CURLE_WEIRD_SERVER_REPLY;
    623   }
    624   else {
    625     /* Does the server support APOP authentication? */
    626     if(len >= 4 && line[len - 2] == '>') {
    627       /* Look for the APOP timestamp */
    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           if(!timestamplen)
    633             break;
    634 
    635           /* Allocate some memory for the timestamp */
    636           pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
    637 
    638           if(!pop3c->apoptimestamp)
    639             break;
    640 
    641           /* Copy the timestamp */
    642           memcpy(pop3c->apoptimestamp, line + i, timestamplen);
    643           pop3c->apoptimestamp[timestamplen] = '\0';
    644 
    645           /* Store the APOP capability */
    646           pop3c->authtypes |= POP3_TYPE_APOP;
    647           break;
    648         }
    649       }
    650     }
    651 
    652     result = pop3_perform_capa(conn);
    653   }
    654 
    655   return result;
    656 }
    657 
    658 /* For CAPA responses */
    659 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
    660                                      pop3state instate)
    661 {
    662   CURLcode result = CURLE_OK;
    663   struct Curl_easy *data = conn->data;
    664   struct pop3_conn *pop3c = &conn->proto.pop3c;
    665   const char *line = data->state.buffer;
    666   size_t len = strlen(line);
    667   size_t wordlen;
    668 
    669   (void)instate; /* no use for this yet */
    670 
    671   /* Do we have a untagged continuation response? */
    672   if(pop3code == '*') {
    673     /* Does the server support the STLS capability? */
    674     if(len >= 4 && !memcmp(line, "STLS", 4))
    675       pop3c->tls_supported = TRUE;
    676 
    677     /* Does the server support clear text authentication? */
    678     else if(len >= 4 && !memcmp(line, "USER", 4))
    679       pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
    680 
    681     /* Does the server support SASL based authentication? */
    682     else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
    683       pop3c->authtypes |= POP3_TYPE_SASL;
    684 
    685       /* Advance past the SASL keyword */
    686       line += 5;
    687       len -= 5;
    688 
    689       /* Loop through the data line */
    690       for(;;) {
    691         size_t llen;
    692         unsigned int mechbit;
    693 
    694         while(len &&
    695               (*line == ' ' || *line == '\t' ||
    696                *line == '\r' || *line == '\n')) {
    697 
    698           line++;
    699           len--;
    700         }
    701 
    702         if(!len)
    703           break;
    704 
    705         /* Extract the word */
    706         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
    707               line[wordlen] != '\t' && line[wordlen] != '\r' &&
    708               line[wordlen] != '\n';)
    709           wordlen++;
    710 
    711         /* Test the word for a matching authentication mechanism */
    712         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
    713         if(mechbit && llen == wordlen)
    714           pop3c->sasl.authmechs |= mechbit;
    715 
    716         line += wordlen;
    717         len -= wordlen;
    718       }
    719     }
    720   }
    721   else if(pop3code == '+') {
    722     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
    723       /* We don't have a SSL/TLS connection yet, but SSL is requested */
    724       if(pop3c->tls_supported)
    725         /* Switch to TLS connection now */
    726         result = pop3_perform_starttls(conn);
    727       else if(data->set.use_ssl == CURLUSESSL_TRY)
    728         /* Fallback and carry on with authentication */
    729         result = pop3_perform_authentication(conn);
    730       else {
    731         failf(data, "STLS not supported.");
    732         result = CURLE_USE_SSL_FAILED;
    733       }
    734     }
    735     else
    736       result = pop3_perform_authentication(conn);
    737   }
    738   else {
    739     /* Clear text is supported when CAPA isn't recognised */
    740     pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
    741 
    742     result = pop3_perform_authentication(conn);
    743   }
    744 
    745   return result;
    746 }
    747 
    748 /* For STARTTLS responses */
    749 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
    750                                          int pop3code,
    751                                          pop3state instate)
    752 {
    753   CURLcode result = CURLE_OK;
    754   struct Curl_easy *data = conn->data;
    755 
    756   (void)instate; /* no use for this yet */
    757 
    758   if(pop3code != '+') {
    759     if(data->set.use_ssl != CURLUSESSL_TRY) {
    760       failf(data, "STARTTLS denied");
    761       result = CURLE_USE_SSL_FAILED;
    762     }
    763     else
    764       result = pop3_perform_authentication(conn);
    765   }
    766   else
    767     result = pop3_perform_upgrade_tls(conn);
    768 
    769   return result;
    770 }
    771 
    772 /* For SASL authentication responses */
    773 static CURLcode pop3_state_auth_resp(struct connectdata *conn,
    774                                      int pop3code,
    775                                      pop3state instate)
    776 {
    777   CURLcode result = CURLE_OK;
    778   struct Curl_easy *data = conn->data;
    779   struct pop3_conn *pop3c = &conn->proto.pop3c;
    780   saslprogress progress;
    781 
    782   (void)instate; /* no use for this yet */
    783 
    784   result = Curl_sasl_continue(&pop3c->sasl, conn, pop3code, &progress);
    785   if(!result)
    786     switch(progress) {
    787     case SASL_DONE:
    788       state(conn, POP3_STOP);  /* Authenticated */
    789       break;
    790     case SASL_IDLE:            /* No mechanism left after cancellation */
    791 #ifndef CURL_DISABLE_CRYPTO_AUTH
    792       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
    793         /* Perform APOP authentication */
    794         result = pop3_perform_apop(conn);
    795       else
    796 #endif
    797       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
    798         /* Perform clear text authentication */
    799         result = pop3_perform_user(conn);
    800       else {
    801         failf(data, "Authentication cancelled");
    802         result = CURLE_LOGIN_DENIED;
    803       }
    804       break;
    805     default:
    806       break;
    807     }
    808 
    809   return result;
    810 }
    811 
    812 #ifndef CURL_DISABLE_CRYPTO_AUTH
    813 /* For APOP responses */
    814 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
    815                                      pop3state instate)
    816 {
    817   CURLcode result = CURLE_OK;
    818   struct Curl_easy *data = conn->data;
    819 
    820   (void)instate; /* no use for this yet */
    821 
    822   if(pop3code != '+') {
    823     failf(data, "Authentication failed: %d", pop3code);
    824     result = CURLE_LOGIN_DENIED;
    825   }
    826   else
    827     /* End of connect phase */
    828     state(conn, POP3_STOP);
    829 
    830   return result;
    831 }
    832 #endif
    833 
    834 /* For USER responses */
    835 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
    836                                      pop3state instate)
    837 {
    838   CURLcode result = CURLE_OK;
    839   struct Curl_easy *data = conn->data;
    840 
    841   (void)instate; /* no use for this yet */
    842 
    843   if(pop3code != '+') {
    844     failf(data, "Access denied. %c", pop3code);
    845     result = CURLE_LOGIN_DENIED;
    846   }
    847   else
    848     /* Send the PASS command */
    849     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
    850                            conn->passwd ? conn->passwd : "");
    851   if(!result)
    852     state(conn, POP3_PASS);
    853 
    854   return result;
    855 }
    856 
    857 /* For PASS responses */
    858 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
    859                                      pop3state instate)
    860 {
    861   CURLcode result = CURLE_OK;
    862   struct Curl_easy *data = conn->data;
    863 
    864   (void)instate; /* no use for this yet */
    865 
    866   if(pop3code != '+') {
    867     failf(data, "Access denied. %c", pop3code);
    868     result = CURLE_LOGIN_DENIED;
    869   }
    870   else
    871     /* End of connect phase */
    872     state(conn, POP3_STOP);
    873 
    874   return result;
    875 }
    876 
    877 /* For command responses */
    878 static CURLcode pop3_state_command_resp(struct connectdata *conn,
    879                                         int pop3code,
    880                                         pop3state instate)
    881 {
    882   CURLcode result = CURLE_OK;
    883   struct Curl_easy *data = conn->data;
    884   struct POP3 *pop3 = data->req.protop;
    885   struct pop3_conn *pop3c = &conn->proto.pop3c;
    886   struct pingpong *pp = &pop3c->pp;
    887 
    888   (void)instate; /* no use for this yet */
    889 
    890   if(pop3code != '+') {
    891     state(conn, POP3_STOP);
    892     return CURLE_RECV_ERROR;
    893   }
    894 
    895   /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
    896      EOB string so count this is two matching bytes. This is necessary to make
    897      the code detect the EOB if the only data than comes now is %2e CR LF like
    898      when there is no body to return. */
    899   pop3c->eob = 2;
    900 
    901   /* But since this initial CR LF pair is not part of the actual body, we set
    902      the strip counter here so that these bytes won't be delivered. */
    903   pop3c->strip = 2;
    904 
    905   if(pop3->transfer == FTPTRANSFER_BODY) {
    906     /* POP3 download */
    907     Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
    908 
    909     if(pp->cache) {
    910       /* The header "cache" contains a bunch of data that is actually body
    911          content so send it as such. Note that there may even be additional
    912          "headers" after the body */
    913 
    914       if(!data->set.opt_no_body) {
    915         result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
    916         if(result)
    917           return result;
    918       }
    919 
    920       /* Free the cache */
    921       Curl_safefree(pp->cache);
    922 
    923       /* Reset the cache size */
    924       pp->cache_size = 0;
    925     }
    926   }
    927 
    928   /* End of DO phase */
    929   state(conn, POP3_STOP);
    930 
    931   return result;
    932 }
    933 
    934 static CURLcode pop3_statemach_act(struct connectdata *conn)
    935 {
    936   CURLcode result = CURLE_OK;
    937   curl_socket_t sock = conn->sock[FIRSTSOCKET];
    938   int pop3code;
    939   struct pop3_conn *pop3c = &conn->proto.pop3c;
    940   struct pingpong *pp = &pop3c->pp;
    941   size_t nread = 0;
    942 
    943   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
    944   if(pop3c->state == POP3_UPGRADETLS)
    945     return pop3_perform_upgrade_tls(conn);
    946 
    947   /* Flush any data that needs to be sent */
    948   if(pp->sendleft)
    949     return Curl_pp_flushsend(pp);
    950 
    951  do {
    952     /* Read the response from the server */
    953     result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
    954     if(result)
    955       return result;
    956 
    957     if(!pop3code)
    958       break;
    959 
    960     /* We have now received a full POP3 server response */
    961     switch(pop3c->state) {
    962     case POP3_SERVERGREET:
    963       result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
    964       break;
    965 
    966     case POP3_CAPA:
    967       result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
    968       break;
    969 
    970     case POP3_STARTTLS:
    971       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
    972       break;
    973 
    974     case POP3_AUTH:
    975       result = pop3_state_auth_resp(conn, pop3code, pop3c->state);
    976       break;
    977 
    978 #ifndef CURL_DISABLE_CRYPTO_AUTH
    979     case POP3_APOP:
    980       result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
    981       break;
    982 #endif
    983 
    984     case POP3_USER:
    985       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
    986       break;
    987 
    988     case POP3_PASS:
    989       result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
    990       break;
    991 
    992     case POP3_COMMAND:
    993       result = pop3_state_command_resp(conn, pop3code, pop3c->state);
    994       break;
    995 
    996     case POP3_QUIT:
    997       /* fallthrough, just stop! */
    998     default:
    999       /* internal error */
   1000       state(conn, POP3_STOP);
   1001       break;
   1002     }
   1003   } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
   1004 
   1005   return result;
   1006 }
   1007 
   1008 /* Called repeatedly until done from multi.c */
   1009 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
   1010 {
   1011   CURLcode result = CURLE_OK;
   1012   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1013 
   1014   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
   1015     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
   1016     if(result || !pop3c->ssldone)
   1017       return result;
   1018   }
   1019 
   1020   result = Curl_pp_statemach(&pop3c->pp, FALSE);
   1021   *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
   1022 
   1023   return result;
   1024 }
   1025 
   1026 static CURLcode pop3_block_statemach(struct connectdata *conn)
   1027 {
   1028   CURLcode result = CURLE_OK;
   1029   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1030 
   1031   while(pop3c->state != POP3_STOP && !result)
   1032     result = Curl_pp_statemach(&pop3c->pp, TRUE);
   1033 
   1034   return result;
   1035 }
   1036 
   1037 /* Allocate and initialize the POP3 struct for the current Curl_easy if
   1038    required */
   1039 static CURLcode pop3_init(struct connectdata *conn)
   1040 {
   1041   CURLcode result = CURLE_OK;
   1042   struct Curl_easy *data = conn->data;
   1043   struct POP3 *pop3;
   1044 
   1045   pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
   1046   if(!pop3)
   1047     result = CURLE_OUT_OF_MEMORY;
   1048 
   1049   return result;
   1050 }
   1051 
   1052 /* For the POP3 "protocol connect" and "doing" phases only */
   1053 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
   1054                         int numsocks)
   1055 {
   1056   return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
   1057 }
   1058 
   1059 /***********************************************************************
   1060  *
   1061  * pop3_connect()
   1062  *
   1063  * This function should do everything that is to be considered a part of the
   1064  * connection phase.
   1065  *
   1066  * The variable 'done' points to will be TRUE if the protocol-layer connect
   1067  * phase is done when this function returns, or FALSE if not.
   1068  */
   1069 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
   1070 {
   1071   CURLcode result = CURLE_OK;
   1072   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1073   struct pingpong *pp = &pop3c->pp;
   1074 
   1075   *done = FALSE; /* default to not done yet */
   1076 
   1077   /* We always support persistent connections in POP3 */
   1078   connkeep(conn, "POP3 default");
   1079 
   1080   /* Set the default response time-out */
   1081   pp->response_time = RESP_TIMEOUT;
   1082   pp->statemach_act = pop3_statemach_act;
   1083   pp->endofresp = pop3_endofresp;
   1084   pp->conn = conn;
   1085 
   1086   /* Set the default preferred authentication type and mechanism */
   1087   pop3c->preftype = POP3_TYPE_ANY;
   1088   Curl_sasl_init(&pop3c->sasl, &saslpop3);
   1089 
   1090   /* Initialise the pingpong layer */
   1091   Curl_pp_init(pp);
   1092 
   1093   /* Parse the URL options */
   1094   result = pop3_parse_url_options(conn);
   1095   if(result)
   1096     return result;
   1097 
   1098   /* Start off waiting for the server greeting response */
   1099   state(conn, POP3_SERVERGREET);
   1100 
   1101   result = pop3_multi_statemach(conn, done);
   1102 
   1103   return result;
   1104 }
   1105 
   1106 /***********************************************************************
   1107  *
   1108  * pop3_done()
   1109  *
   1110  * The DONE function. This does what needs to be done after a single DO has
   1111  * performed.
   1112  *
   1113  * Input argument is already checked for validity.
   1114  */
   1115 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
   1116                           bool premature)
   1117 {
   1118   CURLcode result = CURLE_OK;
   1119   struct Curl_easy *data = conn->data;
   1120   struct POP3 *pop3 = data->req.protop;
   1121 
   1122   (void)premature;
   1123 
   1124   if(!pop3)
   1125     return CURLE_OK;
   1126 
   1127   if(status) {
   1128     connclose(conn, "POP3 done with bad status");
   1129     result = status;         /* use the already set error code */
   1130   }
   1131 
   1132   /* Cleanup our per-request based variables */
   1133   Curl_safefree(pop3->id);
   1134   Curl_safefree(pop3->custom);
   1135 
   1136   /* Clear the transfer mode for the next request */
   1137   pop3->transfer = FTPTRANSFER_BODY;
   1138 
   1139   return result;
   1140 }
   1141 
   1142 /***********************************************************************
   1143  *
   1144  * pop3_perform()
   1145  *
   1146  * This is the actual DO function for POP3. Get a message/listing according to
   1147  * the options previously setup.
   1148  */
   1149 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
   1150                              bool *dophase_done)
   1151 {
   1152   /* This is POP3 and no proxy */
   1153   CURLcode result = CURLE_OK;
   1154   struct POP3 *pop3 = conn->data->req.protop;
   1155 
   1156   DEBUGF(infof(conn->data, "DO phase starts\n"));
   1157 
   1158   if(conn->data->set.opt_no_body) {
   1159     /* Requested no body means no transfer */
   1160     pop3->transfer = FTPTRANSFER_INFO;
   1161   }
   1162 
   1163   *dophase_done = FALSE; /* not done yet */
   1164 
   1165   /* Start the first command in the DO phase */
   1166   result = pop3_perform_command(conn);
   1167   if(result)
   1168     return result;
   1169 
   1170   /* Run the state-machine */
   1171   result = pop3_multi_statemach(conn, dophase_done);
   1172 
   1173   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
   1174 
   1175   if(*dophase_done)
   1176     DEBUGF(infof(conn->data, "DO phase is complete\n"));
   1177 
   1178   return result;
   1179 }
   1180 
   1181 /***********************************************************************
   1182  *
   1183  * pop3_do()
   1184  *
   1185  * This function is registered as 'curl_do' function. It decodes the path
   1186  * parts etc as a wrapper to the actual DO function (pop3_perform).
   1187  *
   1188  * The input argument is already checked for validity.
   1189  */
   1190 static CURLcode pop3_do(struct connectdata *conn, bool *done)
   1191 {
   1192   CURLcode result = CURLE_OK;
   1193 
   1194   *done = FALSE; /* default to false */
   1195 
   1196   /* Parse the URL path */
   1197   result = pop3_parse_url_path(conn);
   1198   if(result)
   1199     return result;
   1200 
   1201   /* Parse the custom request */
   1202   result = pop3_parse_custom_request(conn);
   1203   if(result)
   1204     return result;
   1205 
   1206   result = pop3_regular_transfer(conn, done);
   1207 
   1208   return result;
   1209 }
   1210 
   1211 /***********************************************************************
   1212  *
   1213  * pop3_disconnect()
   1214  *
   1215  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
   1216  * resources. BLOCKING.
   1217  */
   1218 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
   1219 {
   1220   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1221 
   1222   /* We cannot send quit unconditionally. If this connection is stale or
   1223      bad in any way, sending quit and waiting around here will make the
   1224      disconnect wait in vain and cause more problems than we need to. */
   1225 
   1226   /* The POP3 session may or may not have been allocated/setup at this
   1227      point! */
   1228   if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
   1229     if(!pop3_perform_quit(conn))
   1230       (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
   1231 
   1232   /* Disconnect from the server */
   1233   Curl_pp_disconnect(&pop3c->pp);
   1234 
   1235   /* Cleanup the SASL module */
   1236   Curl_sasl_cleanup(conn, pop3c->sasl.authused);
   1237 
   1238   /* Cleanup our connection based variables */
   1239   Curl_safefree(pop3c->apoptimestamp);
   1240 
   1241   return CURLE_OK;
   1242 }
   1243 
   1244 /* Call this when the DO phase has completed */
   1245 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
   1246 {
   1247   (void)conn;
   1248   (void)connected;
   1249 
   1250   return CURLE_OK;
   1251 }
   1252 
   1253 /* Called from multi.c while DOing */
   1254 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
   1255 {
   1256   CURLcode result = pop3_multi_statemach(conn, dophase_done);
   1257 
   1258   if(result)
   1259     DEBUGF(infof(conn->data, "DO phase failed\n"));
   1260   else if(*dophase_done) {
   1261     result = pop3_dophase_done(conn, FALSE /* not connected */);
   1262 
   1263     DEBUGF(infof(conn->data, "DO phase is complete\n"));
   1264   }
   1265 
   1266   return result;
   1267 }
   1268 
   1269 /***********************************************************************
   1270  *
   1271  * pop3_regular_transfer()
   1272  *
   1273  * The input argument is already checked for validity.
   1274  *
   1275  * Performs all commands done before a regular transfer between a local and a
   1276  * remote host.
   1277  */
   1278 static CURLcode pop3_regular_transfer(struct connectdata *conn,
   1279                                       bool *dophase_done)
   1280 {
   1281   CURLcode result = CURLE_OK;
   1282   bool connected = FALSE;
   1283   struct Curl_easy *data = conn->data;
   1284 
   1285   /* Make sure size is unknown at this point */
   1286   data->req.size = -1;
   1287 
   1288   /* Set the progress data */
   1289   Curl_pgrsSetUploadCounter(data, 0);
   1290   Curl_pgrsSetDownloadCounter(data, 0);
   1291   Curl_pgrsSetUploadSize(data, -1);
   1292   Curl_pgrsSetDownloadSize(data, -1);
   1293 
   1294   /* Carry out the perform */
   1295   result = pop3_perform(conn, &connected, dophase_done);
   1296 
   1297   /* Perform post DO phase operations if necessary */
   1298   if(!result && *dophase_done)
   1299     result = pop3_dophase_done(conn, connected);
   1300 
   1301   return result;
   1302 }
   1303 
   1304 static CURLcode pop3_setup_connection(struct connectdata *conn)
   1305 {
   1306   struct Curl_easy *data = conn->data;
   1307 
   1308   /* Initialise the POP3 layer */
   1309   CURLcode result = pop3_init(conn);
   1310   if(result)
   1311     return result;
   1312 
   1313   /* Clear the TLS upgraded flag */
   1314   conn->tls_upgraded = FALSE;
   1315   data->state.path++;   /* don't include the initial slash */
   1316 
   1317   return CURLE_OK;
   1318 }
   1319 
   1320 /***********************************************************************
   1321  *
   1322  * pop3_parse_url_options()
   1323  *
   1324  * Parse the URL login options.
   1325  */
   1326 static CURLcode pop3_parse_url_options(struct connectdata *conn)
   1327 {
   1328   CURLcode result = CURLE_OK;
   1329   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1330   const char *ptr = conn->options;
   1331 
   1332   pop3c->sasl.resetprefs = TRUE;
   1333 
   1334   while(!result && ptr && *ptr) {
   1335     const char *key = ptr;
   1336     const char *value;
   1337 
   1338     while(*ptr && *ptr != '=')
   1339         ptr++;
   1340 
   1341     value = ptr + 1;
   1342 
   1343     while(*ptr && *ptr != ';')
   1344       ptr++;
   1345 
   1346     if(strncasecompare(key, "AUTH=", 5)) {
   1347       result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
   1348                                                value, ptr - value);
   1349 
   1350       if(result && strncasecompare(value, "+APOP", ptr - value)) {
   1351         pop3c->preftype = POP3_TYPE_APOP;
   1352         pop3c->sasl.prefmech = SASL_AUTH_NONE;
   1353         result = CURLE_OK;
   1354       }
   1355     }
   1356     else
   1357       result = CURLE_URL_MALFORMAT;
   1358 
   1359     if(*ptr == ';')
   1360       ptr++;
   1361   }
   1362 
   1363   if(pop3c->preftype != POP3_TYPE_APOP)
   1364     switch(pop3c->sasl.prefmech) {
   1365     case SASL_AUTH_NONE:
   1366       pop3c->preftype = POP3_TYPE_NONE;
   1367       break;
   1368     case SASL_AUTH_DEFAULT:
   1369       pop3c->preftype = POP3_TYPE_ANY;
   1370       break;
   1371     default:
   1372       pop3c->preftype = POP3_TYPE_SASL;
   1373       break;
   1374     }
   1375 
   1376   return result;
   1377 }
   1378 
   1379 /***********************************************************************
   1380  *
   1381  * pop3_parse_url_path()
   1382  *
   1383  * Parse the URL path into separate path components.
   1384  */
   1385 static CURLcode pop3_parse_url_path(struct connectdata *conn)
   1386 {
   1387   /* The POP3 struct is already initialised in pop3_connect() */
   1388   struct Curl_easy *data = conn->data;
   1389   struct POP3 *pop3 = data->req.protop;
   1390   const char *path = data->state.path;
   1391 
   1392   /* URL decode the path for the message ID */
   1393   return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
   1394 }
   1395 
   1396 /***********************************************************************
   1397  *
   1398  * pop3_parse_custom_request()
   1399  *
   1400  * Parse the custom request.
   1401  */
   1402 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
   1403 {
   1404   CURLcode result = CURLE_OK;
   1405   struct Curl_easy *data = conn->data;
   1406   struct POP3 *pop3 = data->req.protop;
   1407   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
   1408 
   1409   /* URL decode the custom request */
   1410   if(custom)
   1411     result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
   1412 
   1413   return result;
   1414 }
   1415 
   1416 /***********************************************************************
   1417  *
   1418  * Curl_pop3_write()
   1419  *
   1420  * This function scans the body after the end-of-body and writes everything
   1421  * until the end is found.
   1422  */
   1423 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
   1424 {
   1425   /* This code could be made into a special function in the handler struct */
   1426   CURLcode result = CURLE_OK;
   1427   struct Curl_easy *data = conn->data;
   1428   struct SingleRequest *k = &data->req;
   1429 
   1430   struct pop3_conn *pop3c = &conn->proto.pop3c;
   1431   bool strip_dot = FALSE;
   1432   size_t last = 0;
   1433   size_t i;
   1434 
   1435   /* Search through the buffer looking for the end-of-body marker which is
   1436      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
   1437      the eob so the server will have prefixed it with an extra dot which we
   1438      need to strip out. Additionally the marker could of course be spread out
   1439      over 5 different data chunks. */
   1440   for(i = 0; i < nread; i++) {
   1441     size_t prev = pop3c->eob;
   1442 
   1443     switch(str[i]) {
   1444     case 0x0d:
   1445       if(pop3c->eob == 0) {
   1446         pop3c->eob++;
   1447 
   1448         if(i) {
   1449           /* Write out the body part that didn't match */
   1450           result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
   1451                                      i - last);
   1452 
   1453           if(result)
   1454             return result;
   1455 
   1456           last = i;
   1457         }
   1458       }
   1459       else if(pop3c->eob == 3)
   1460         pop3c->eob++;
   1461       else
   1462         /* If the character match wasn't at position 0 or 3 then restart the
   1463            pattern matching */
   1464         pop3c->eob = 1;
   1465       break;
   1466 
   1467     case 0x0a:
   1468       if(pop3c->eob == 1 || pop3c->eob == 4)
   1469         pop3c->eob++;
   1470       else
   1471         /* If the character match wasn't at position 1 or 4 then start the
   1472            search again */
   1473         pop3c->eob = 0;
   1474       break;
   1475 
   1476     case 0x2e:
   1477       if(pop3c->eob == 2)
   1478         pop3c->eob++;
   1479       else if(pop3c->eob == 3) {
   1480         /* We have an extra dot after the CRLF which we need to strip off */
   1481         strip_dot = TRUE;
   1482         pop3c->eob = 0;
   1483       }
   1484       else
   1485         /* If the character match wasn't at position 2 then start the search
   1486            again */
   1487         pop3c->eob = 0;
   1488       break;
   1489 
   1490     default:
   1491       pop3c->eob = 0;
   1492       break;
   1493     }
   1494 
   1495     /* Did we have a partial match which has subsequently failed? */
   1496     if(prev && prev >= pop3c->eob) {
   1497       /* Strip can only be non-zero for the very first mismatch after CRLF
   1498          and then both prev and strip are equal and nothing will be output
   1499          below */
   1500       while(prev && pop3c->strip) {
   1501         prev--;
   1502         pop3c->strip--;
   1503       }
   1504 
   1505       if(prev) {
   1506         /* If the partial match was the CRLF and dot then only write the CRLF
   1507            as the server would have inserted the dot */
   1508         result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB,
   1509                                    strip_dot ? prev - 1 : prev);
   1510 
   1511         if(result)
   1512           return result;
   1513 
   1514         last = i;
   1515         strip_dot = FALSE;
   1516       }
   1517     }
   1518   }
   1519 
   1520   if(pop3c->eob == POP3_EOB_LEN) {
   1521     /* We have a full match so the transfer is done, however we must transfer
   1522     the CRLF at the start of the EOB as this is considered to be part of the
   1523     message as per RFC-1939, sect. 3 */
   1524     result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
   1525 
   1526     k->keepon &= ~KEEP_RECV;
   1527     pop3c->eob = 0;
   1528 
   1529     return result;
   1530   }
   1531 
   1532   if(pop3c->eob)
   1533     /* While EOB is matching nothing should be output */
   1534     return CURLE_OK;
   1535 
   1536   if(nread - last) {
   1537     result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
   1538                                nread - last);
   1539   }
   1540 
   1541   return result;
   1542 }
   1543 
   1544 #endif /* CURL_DISABLE_POP3 */
   1545