quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

pop3.c (52444B)


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