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