pop3.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  * RFC1734 POP3 Authentication
00022  * RFC1939 POP3 protocol
00023  * RFC2195 CRAM-MD5 authentication
00024  * RFC2384 POP URL Scheme
00025  * RFC2449 POP3 Extension Mechanism
00026  * RFC2595 Using TLS with IMAP, POP3 and ACAP
00027  * RFC2831 DIGEST-MD5 authentication
00028  * RFC4422 Simple Authentication and Security Layer (SASL)
00029  * RFC4616 PLAIN authentication
00030  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
00031  * RFC5034 POP3 SASL Authentication Mechanism
00032  * RFC6749 OAuth 2.0 Authorization Framework
00033  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
00034  *
00035  ***************************************************************************/
00036 
00037 #include "curl_setup.h"
00038 
00039 #ifndef CURL_DISABLE_POP3
00040 
00041 #ifdef HAVE_NETINET_IN_H
00042 #include <netinet/in.h>
00043 #endif
00044 #ifdef HAVE_ARPA_INET_H
00045 #include <arpa/inet.h>
00046 #endif
00047 #ifdef HAVE_UTSNAME_H
00048 #include <sys/utsname.h>
00049 #endif
00050 #ifdef HAVE_NETDB_H
00051 #include <netdb.h>
00052 #endif
00053 #ifdef __VMS
00054 #include <in.h>
00055 #include <inet.h>
00056 #endif
00057 
00058 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
00059 #undef in_addr_t
00060 #define in_addr_t unsigned long
00061 #endif
00062 
00063 #include <curl/curl.h>
00064 #include "urldata.h"
00065 #include "sendf.h"
00066 #include "hostip.h"
00067 #include "progress.h"
00068 #include "transfer.h"
00069 #include "escape.h"
00070 #include "http.h" /* for HTTP proxy tunnel stuff */
00071 #include "socks.h"
00072 #include "pop3.h"
00073 #include "strtoofft.h"
00074 #include "strcase.h"
00075 #include "vtls/vtls.h"
00076 #include "connect.h"
00077 #include "strerror.h"
00078 #include "select.h"
00079 #include "multiif.h"
00080 #include "url.h"
00081 #include "curl_sasl.h"
00082 #include "curl_md5.h"
00083 #include "warnless.h"
00084 /* The last 3 #include files should be in this order */
00085 #include "curl_printf.h"
00086 #include "curl_memory.h"
00087 #include "memdebug.h"
00088 
00089 /* Local API functions */
00090 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
00091 static CURLcode pop3_do(struct connectdata *conn, bool *done);
00092 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
00093                           bool premature);
00094 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
00095 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
00096 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
00097 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
00098                         int numsocks);
00099 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
00100 static CURLcode pop3_setup_connection(struct connectdata *conn);
00101 static CURLcode pop3_parse_url_options(struct connectdata *conn);
00102 static CURLcode pop3_parse_url_path(struct connectdata *conn);
00103 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
00104 static CURLcode pop3_perform_auth(struct connectdata *conn, const char *mech,
00105                                   const char *initresp);
00106 static CURLcode pop3_continue_auth(struct connectdata *conn, const char *resp);
00107 static void pop3_get_message(char *buffer, char **outptr);
00108 
00109 /*
00110  * POP3 protocol handler.
00111  */
00112 
00113 const struct Curl_handler Curl_handler_pop3 = {
00114   "POP3",                           /* scheme */
00115   pop3_setup_connection,            /* setup_connection */
00116   pop3_do,                          /* do_it */
00117   pop3_done,                        /* done */
00118   ZERO_NULL,                        /* do_more */
00119   pop3_connect,                     /* connect_it */
00120   pop3_multi_statemach,             /* connecting */
00121   pop3_doing,                       /* doing */
00122   pop3_getsock,                     /* proto_getsock */
00123   pop3_getsock,                     /* doing_getsock */
00124   ZERO_NULL,                        /* domore_getsock */
00125   ZERO_NULL,                        /* perform_getsock */
00126   pop3_disconnect,                  /* disconnect */
00127   ZERO_NULL,                        /* readwrite */
00128   PORT_POP3,                        /* defport */
00129   CURLPROTO_POP3,                   /* protocol */
00130   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
00131 };
00132 
00133 #ifdef USE_SSL
00134 /*
00135  * POP3S protocol handler.
00136  */
00137 
00138 const struct Curl_handler Curl_handler_pop3s = {
00139   "POP3S",                          /* scheme */
00140   pop3_setup_connection,            /* setup_connection */
00141   pop3_do,                          /* do_it */
00142   pop3_done,                        /* done */
00143   ZERO_NULL,                        /* do_more */
00144   pop3_connect,                     /* connect_it */
00145   pop3_multi_statemach,             /* connecting */
00146   pop3_doing,                       /* doing */
00147   pop3_getsock,                     /* proto_getsock */
00148   pop3_getsock,                     /* doing_getsock */
00149   ZERO_NULL,                        /* domore_getsock */
00150   ZERO_NULL,                        /* perform_getsock */
00151   pop3_disconnect,                  /* disconnect */
00152   ZERO_NULL,                        /* readwrite */
00153   PORT_POP3S,                       /* defport */
00154   CURLPROTO_POP3S,                  /* protocol */
00155   PROTOPT_CLOSEACTION | PROTOPT_SSL
00156   | PROTOPT_NOURLQUERY              /* flags */
00157 };
00158 #endif
00159 
00160 #ifndef CURL_DISABLE_HTTP
00161 /*
00162  * HTTP-proxyed POP3 protocol handler.
00163  */
00164 
00165 static const struct Curl_handler Curl_handler_pop3_proxy = {
00166   "POP3",                               /* scheme */
00167   Curl_http_setup_conn,                 /* setup_connection */
00168   Curl_http,                            /* do_it */
00169   Curl_http_done,                       /* done */
00170   ZERO_NULL,                            /* do_more */
00171   ZERO_NULL,                            /* connect_it */
00172   ZERO_NULL,                            /* connecting */
00173   ZERO_NULL,                            /* doing */
00174   ZERO_NULL,                            /* proto_getsock */
00175   ZERO_NULL,                            /* doing_getsock */
00176   ZERO_NULL,                            /* domore_getsock */
00177   ZERO_NULL,                            /* perform_getsock */
00178   ZERO_NULL,                            /* disconnect */
00179   ZERO_NULL,                            /* readwrite */
00180   PORT_POP3,                            /* defport */
00181   CURLPROTO_HTTP,                       /* protocol */
00182   PROTOPT_NONE                          /* flags */
00183 };
00184 
00185 #ifdef USE_SSL
00186 /*
00187  * HTTP-proxyed POP3S protocol handler.
00188  */
00189 
00190 static const struct Curl_handler Curl_handler_pop3s_proxy = {
00191   "POP3S",                              /* scheme */
00192   Curl_http_setup_conn,                 /* setup_connection */
00193   Curl_http,                            /* do_it */
00194   Curl_http_done,                       /* done */
00195   ZERO_NULL,                            /* do_more */
00196   ZERO_NULL,                            /* connect_it */
00197   ZERO_NULL,                            /* connecting */
00198   ZERO_NULL,                            /* doing */
00199   ZERO_NULL,                            /* proto_getsock */
00200   ZERO_NULL,                            /* doing_getsock */
00201   ZERO_NULL,                            /* domore_getsock */
00202   ZERO_NULL,                            /* perform_getsock */
00203   ZERO_NULL,                            /* disconnect */
00204   ZERO_NULL,                            /* readwrite */
00205   PORT_POP3S,                           /* defport */
00206   CURLPROTO_HTTP,                       /* protocol */
00207   PROTOPT_NONE                          /* flags */
00208 };
00209 #endif
00210 #endif
00211 
00212 /* SASL parameters for the pop3 protocol */
00213 static const struct SASLproto saslpop3 = {
00214   "pop",                      /* The service name */
00215   '*',                        /* Code received when continuation is expected */
00216   '+',                        /* Code to receive upon authentication success */
00217   255 - 8,                    /* Maximum initial response length (no max) */
00218   pop3_perform_auth,          /* Send authentication command */
00219   pop3_continue_auth,         /* Send authentication continuation */
00220   pop3_get_message            /* Get SASL response message */
00221 };
00222 
00223 #ifdef USE_SSL
00224 static void pop3_to_pop3s(struct connectdata *conn)
00225 {
00226   /* Change the connection handler */
00227   conn->handler = &Curl_handler_pop3s;
00228 
00229   /* Set the connection's upgraded to TLS flag */
00230   conn->tls_upgraded = TRUE;
00231 }
00232 #else
00233 #define pop3_to_pop3s(x) Curl_nop_stmt
00234 #endif
00235 
00236 /***********************************************************************
00237  *
00238  * pop3_endofresp()
00239  *
00240  * Checks for an ending POP3 status code at the start of the given string, but
00241  * also detects the APOP timestamp from the server greeting and various
00242  * capabilities from the CAPA response including the supported authentication
00243  * types and allowed SASL mechanisms.
00244  */
00245 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
00246                            int *resp)
00247 {
00248   struct pop3_conn *pop3c = &conn->proto.pop3c;
00249 
00250   /* Do we have an error response? */
00251   if(len >= 4 && !memcmp("-ERR", line, 4)) {
00252     *resp = '-';
00253 
00254     return TRUE;
00255   }
00256 
00257   /* Are we processing CAPA command responses? */
00258   if(pop3c->state == POP3_CAPA) {
00259     /* Do we have the terminating line? */
00260     if(len >= 1 && !memcmp(line, ".", 1))
00261       /* Treat the response as a success */
00262       *resp = '+';
00263     else
00264       /* Treat the response as an untagged continuation */
00265       *resp = '*';
00266 
00267     return TRUE;
00268   }
00269 
00270   /* Do we have a success response? */
00271   if(len >= 3 && !memcmp("+OK", line, 3)) {
00272     *resp = '+';
00273 
00274     return TRUE;
00275   }
00276 
00277   /* Do we have a continuation response? */
00278   if(len >= 1 && !memcmp("+", line, 1)) {
00279     *resp = '*';
00280 
00281     return TRUE;
00282   }
00283 
00284   return FALSE; /* Nothing for us */
00285 }
00286 
00287 /***********************************************************************
00288  *
00289  * pop3_get_message()
00290  *
00291  * Gets the authentication message from the response buffer.
00292  */
00293 static void pop3_get_message(char *buffer, char **outptr)
00294 {
00295   size_t len = 0;
00296   char *message = NULL;
00297 
00298   /* Find the start of the message */
00299   for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
00300     ;
00301 
00302   /* Find the end of the message */
00303   for(len = strlen(message); len--;)
00304     if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
00305         message[len] != '\t')
00306       break;
00307 
00308   /* Terminate the message */
00309   if(++len) {
00310     message[len] = '\0';
00311   }
00312 
00313   *outptr = message;
00314 }
00315 
00316 /***********************************************************************
00317  *
00318  * state()
00319  *
00320  * This is the ONLY way to change POP3 state!
00321  */
00322 static void state(struct connectdata *conn, pop3state newstate)
00323 {
00324   struct pop3_conn *pop3c = &conn->proto.pop3c;
00325 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
00326   /* for debug purposes */
00327   static const char * const names[] = {
00328     "STOP",
00329     "SERVERGREET",
00330     "CAPA",
00331     "STARTTLS",
00332     "UPGRADETLS",
00333     "AUTH",
00334     "APOP",
00335     "USER",
00336     "PASS",
00337     "COMMAND",
00338     "QUIT",
00339     /* LAST */
00340   };
00341 
00342   if(pop3c->state != newstate)
00343     infof(conn->data, "POP3 %p state change from %s to %s\n",
00344           (void *)pop3c, names[pop3c->state], names[newstate]);
00345 #endif
00346 
00347   pop3c->state = newstate;
00348 }
00349 
00350 /***********************************************************************
00351  *
00352  * pop3_perform_capa()
00353  *
00354  * Sends the CAPA command in order to obtain a list of server side supported
00355  * capabilities.
00356  */
00357 static CURLcode pop3_perform_capa(struct connectdata *conn)
00358 {
00359   CURLcode result = CURLE_OK;
00360   struct pop3_conn *pop3c = &conn->proto.pop3c;
00361 
00362   pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
00363   pop3c->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
00364   pop3c->tls_supported = FALSE;           /* Clear the TLS capability */
00365 
00366   /* Send the CAPA command */
00367   result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA");
00368 
00369   if(!result)
00370     state(conn, POP3_CAPA);
00371 
00372   return result;
00373 }
00374 
00375 /***********************************************************************
00376  *
00377  * pop3_perform_starttls()
00378  *
00379  * Sends the STLS command to start the upgrade to TLS.
00380  */
00381 static CURLcode pop3_perform_starttls(struct connectdata *conn)
00382 {
00383   CURLcode result = CURLE_OK;
00384 
00385   /* Send the STLS command */
00386   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS");
00387 
00388   if(!result)
00389     state(conn, POP3_STARTTLS);
00390 
00391   return result;
00392 }
00393 
00394 /***********************************************************************
00395  *
00396  * pop3_perform_upgrade_tls()
00397  *
00398  * Performs the upgrade to TLS.
00399  */
00400 static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
00401 {
00402   CURLcode result = CURLE_OK;
00403   struct pop3_conn *pop3c = &conn->proto.pop3c;
00404 
00405   /* Start the SSL connection */
00406   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
00407 
00408   if(!result) {
00409     if(pop3c->state != POP3_UPGRADETLS)
00410       state(conn, POP3_UPGRADETLS);
00411 
00412     if(pop3c->ssldone) {
00413       pop3_to_pop3s(conn);
00414       result = pop3_perform_capa(conn);
00415     }
00416   }
00417 
00418   return result;
00419 }
00420 
00421 /***********************************************************************
00422  *
00423  * pop3_perform_user()
00424  *
00425  * Sends a clear text USER command to authenticate with.
00426  */
00427 static CURLcode pop3_perform_user(struct connectdata *conn)
00428 {
00429   CURLcode result = CURLE_OK;
00430 
00431   /* Check we have a username and password to authenticate with and end the
00432      connect phase if we don't */
00433   if(!conn->bits.user_passwd) {
00434     state(conn, POP3_STOP);
00435 
00436     return result;
00437   }
00438 
00439   /* Send the USER command */
00440   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
00441                          conn->user ? conn->user : "");
00442   if(!result)
00443     state(conn, POP3_USER);
00444 
00445   return result;
00446 }
00447 
00448 #ifndef CURL_DISABLE_CRYPTO_AUTH
00449 /***********************************************************************
00450  *
00451  * pop3_perform_apop()
00452  *
00453  * Sends an APOP command to authenticate with.
00454  */
00455 static CURLcode pop3_perform_apop(struct connectdata *conn)
00456 {
00457   CURLcode result = CURLE_OK;
00458   struct pop3_conn *pop3c = &conn->proto.pop3c;
00459   size_t i;
00460   MD5_context *ctxt;
00461   unsigned char digest[MD5_DIGEST_LEN];
00462   char secret[2 * MD5_DIGEST_LEN + 1];
00463 
00464   /* Check we have a username and password to authenticate with and end the
00465      connect phase if we don't */
00466   if(!conn->bits.user_passwd) {
00467     state(conn, POP3_STOP);
00468 
00469     return result;
00470   }
00471 
00472   /* Create the digest */
00473   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
00474   if(!ctxt)
00475     return CURLE_OUT_OF_MEMORY;
00476 
00477   Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
00478                   curlx_uztoui(strlen(pop3c->apoptimestamp)));
00479 
00480   Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
00481                   curlx_uztoui(strlen(conn->passwd)));
00482 
00483   /* Finalise the digest */
00484   Curl_MD5_final(ctxt, digest);
00485 
00486   /* Convert the calculated 16 octet digest into a 32 byte hex string */
00487   for(i = 0; i < MD5_DIGEST_LEN; i++)
00488     snprintf(&secret[2 * i], 3, "%02x", digest[i]);
00489 
00490   result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
00491 
00492   if(!result)
00493     state(conn, POP3_APOP);
00494 
00495   return result;
00496 }
00497 #endif
00498 
00499 /***********************************************************************
00500  *
00501  * pop3_perform_auth()
00502  *
00503  * Sends an AUTH command allowing the client to login with the given SASL
00504  * authentication mechanism.
00505  */
00506 static CURLcode pop3_perform_auth(struct connectdata *conn,
00507                                   const char *mech,
00508                                   const char *initresp)
00509 {
00510   CURLcode result = CURLE_OK;
00511   struct pop3_conn *pop3c = &conn->proto.pop3c;
00512 
00513   if(initresp) {                                  /* AUTH <mech> ...<crlf> */
00514     /* Send the AUTH command with the initial response */
00515     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
00516   }
00517   else {
00518     /* Send the AUTH command */
00519     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
00520   }
00521 
00522   return result;
00523 }
00524 
00525 /***********************************************************************
00526  *
00527  * pop3_continue_auth()
00528  *
00529  * Sends SASL continuation data or cancellation.
00530  */
00531 static CURLcode pop3_continue_auth(struct connectdata *conn,
00532                                    const char *resp)
00533 {
00534   struct pop3_conn *pop3c = &conn->proto.pop3c;
00535 
00536   return Curl_pp_sendf(&pop3c->pp, "%s", resp);
00537 }
00538 
00539 /***********************************************************************
00540  *
00541  * pop3_perform_authentication()
00542  *
00543  * Initiates the authentication sequence, with the appropriate SASL
00544  * authentication mechanism, falling back to APOP and clear text should a
00545  * common mechanism not be available between the client and server.
00546  */
00547 static CURLcode pop3_perform_authentication(struct connectdata *conn)
00548 {
00549   CURLcode result = CURLE_OK;
00550   struct pop3_conn *pop3c = &conn->proto.pop3c;
00551   saslprogress progress = SASL_IDLE;
00552 
00553   /* Check we have enough data to authenticate with and end the
00554      connect phase if we don't */
00555   if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) {
00556     state(conn, POP3_STOP);
00557     return result;
00558   }
00559 
00560   if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
00561     /* Calculate the SASL login details */
00562     result = Curl_sasl_start(&pop3c->sasl, conn, FALSE, &progress);
00563 
00564     if(!result)
00565       if(progress == SASL_INPROGRESS)
00566         state(conn, POP3_AUTH);
00567   }
00568 
00569   if(!result && progress == SASL_IDLE) {
00570 #ifndef CURL_DISABLE_CRYPTO_AUTH
00571     if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
00572       /* Perform APOP authentication */
00573       result = pop3_perform_apop(conn);
00574     else
00575 #endif
00576     if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
00577       /* Perform clear text authentication */
00578       result = pop3_perform_user(conn);
00579     else {
00580       /* Other mechanisms not supported */
00581       infof(conn->data, "No known authentication mechanisms supported!\n");
00582       result = CURLE_LOGIN_DENIED;
00583     }
00584   }
00585 
00586   return result;
00587 }
00588 
00589 /***********************************************************************
00590  *
00591  * pop3_perform_command()
00592  *
00593  * Sends a POP3 based command.
00594  */
00595 static CURLcode pop3_perform_command(struct connectdata *conn)
00596 {
00597   CURLcode result = CURLE_OK;
00598   struct Curl_easy *data = conn->data;
00599   struct POP3 *pop3 = data->req.protop;
00600   const char *command = NULL;
00601 
00602   /* Calculate the default command */
00603   if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
00604     command = "LIST";
00605 
00606     if(pop3->id[0] != '\0')
00607       /* Message specific LIST so skip the BODY transfer */
00608       pop3->transfer = FTPTRANSFER_INFO;
00609   }
00610   else
00611     command = "RETR";
00612 
00613   /* Send the command */
00614   if(pop3->id[0] != '\0')
00615     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
00616                            (pop3->custom && pop3->custom[0] != '\0' ?
00617                             pop3->custom : command), pop3->id);
00618   else
00619     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s",
00620                            (pop3->custom && pop3->custom[0] != '\0' ?
00621                             pop3->custom : command));
00622 
00623   if(!result)
00624     state(conn, POP3_COMMAND);
00625 
00626   return result;
00627 }
00628 
00629 /***********************************************************************
00630  *
00631  * pop3_perform_quit()
00632  *
00633  * Performs the quit action prior to sclose() be called.
00634  */
00635 static CURLcode pop3_perform_quit(struct connectdata *conn)
00636 {
00637   CURLcode result = CURLE_OK;
00638 
00639   /* Send the QUIT command */
00640   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT");
00641 
00642   if(!result)
00643     state(conn, POP3_QUIT);
00644 
00645   return result;
00646 }
00647 
00648 /* For the initial server greeting */
00649 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
00650                                             int pop3code,
00651                                             pop3state instate)
00652 {
00653   CURLcode result = CURLE_OK;
00654   struct Curl_easy *data = conn->data;
00655   struct pop3_conn *pop3c = &conn->proto.pop3c;
00656   const char *line = data->state.buffer;
00657   size_t len = strlen(line);
00658   size_t i;
00659 
00660   (void)instate; /* no use for this yet */
00661 
00662   if(pop3code != '+') {
00663     failf(data, "Got unexpected pop3-server response");
00664     result = CURLE_WEIRD_SERVER_REPLY;
00665   }
00666   else {
00667     /* Does the server support APOP authentication? */
00668     if(len >= 4 && line[len - 2] == '>') {
00669       /* Look for the APOP timestamp */
00670       for(i = 3; i < len - 2; ++i) {
00671         if(line[i] == '<') {
00672           /* Calculate the length of the timestamp */
00673           size_t timestamplen = len - 1 - i;
00674           if(!timestamplen)
00675             break;
00676 
00677           /* Allocate some memory for the timestamp */
00678           pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
00679 
00680           if(!pop3c->apoptimestamp)
00681             break;
00682 
00683           /* Copy the timestamp */
00684           memcpy(pop3c->apoptimestamp, line + i, timestamplen);
00685           pop3c->apoptimestamp[timestamplen] = '\0';
00686 
00687           /* Store the APOP capability */
00688           pop3c->authtypes |= POP3_TYPE_APOP;
00689           break;
00690         }
00691       }
00692     }
00693 
00694     result = pop3_perform_capa(conn);
00695   }
00696 
00697   return result;
00698 }
00699 
00700 /* For CAPA responses */
00701 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
00702                                      pop3state instate)
00703 {
00704   CURLcode result = CURLE_OK;
00705   struct Curl_easy *data = conn->data;
00706   struct pop3_conn *pop3c = &conn->proto.pop3c;
00707   const char *line = data->state.buffer;
00708   size_t len = strlen(line);
00709   size_t wordlen;
00710 
00711   (void)instate; /* no use for this yet */
00712 
00713   /* Do we have a untagged continuation response? */
00714   if(pop3code == '*') {
00715     /* Does the server support the STLS capability? */
00716     if(len >= 4 && !memcmp(line, "STLS", 4))
00717       pop3c->tls_supported = TRUE;
00718 
00719     /* Does the server support clear text authentication? */
00720     else if(len >= 4 && !memcmp(line, "USER", 4))
00721       pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
00722 
00723     /* Does the server support SASL based authentication? */
00724     else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
00725       pop3c->authtypes |= POP3_TYPE_SASL;
00726 
00727       /* Advance past the SASL keyword */
00728       line += 5;
00729       len -= 5;
00730 
00731       /* Loop through the data line */
00732       for(;;) {
00733         size_t llen;
00734         unsigned int mechbit;
00735 
00736         while(len &&
00737               (*line == ' ' || *line == '\t' ||
00738                *line == '\r' || *line == '\n')) {
00739 
00740           line++;
00741           len--;
00742         }
00743 
00744         if(!len)
00745           break;
00746 
00747         /* Extract the word */
00748         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
00749               line[wordlen] != '\t' && line[wordlen] != '\r' &&
00750               line[wordlen] != '\n';)
00751           wordlen++;
00752 
00753         /* Test the word for a matching authentication mechanism */
00754         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
00755         if(mechbit && llen == wordlen)
00756           pop3c->sasl.authmechs |= mechbit;
00757 
00758         line += wordlen;
00759         len -= wordlen;
00760       }
00761     }
00762   }
00763   else if(pop3code == '+') {
00764     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
00765       /* We don't have a SSL/TLS connection yet, but SSL is requested */
00766       if(pop3c->tls_supported)
00767         /* Switch to TLS connection now */
00768         result = pop3_perform_starttls(conn);
00769       else if(data->set.use_ssl == CURLUSESSL_TRY)
00770         /* Fallback and carry on with authentication */
00771         result = pop3_perform_authentication(conn);
00772       else {
00773         failf(data, "STLS not supported.");
00774         result = CURLE_USE_SSL_FAILED;
00775       }
00776     }
00777     else
00778       result = pop3_perform_authentication(conn);
00779   }
00780   else {
00781     /* Clear text is supported when CAPA isn't recognised */
00782     pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
00783 
00784     result = pop3_perform_authentication(conn);
00785   }
00786 
00787   return result;
00788 }
00789 
00790 /* For STARTTLS responses */
00791 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
00792                                          int pop3code,
00793                                          pop3state instate)
00794 {
00795   CURLcode result = CURLE_OK;
00796   struct Curl_easy *data = conn->data;
00797 
00798   (void)instate; /* no use for this yet */
00799 
00800   if(pop3code != '+') {
00801     if(data->set.use_ssl != CURLUSESSL_TRY) {
00802       failf(data, "STARTTLS denied. %c", pop3code);
00803       result = CURLE_USE_SSL_FAILED;
00804     }
00805     else
00806       result = pop3_perform_authentication(conn);
00807   }
00808   else
00809     result = pop3_perform_upgrade_tls(conn);
00810 
00811   return result;
00812 }
00813 
00814 /* For SASL authentication responses */
00815 static CURLcode pop3_state_auth_resp(struct connectdata *conn,
00816                                      int pop3code,
00817                                      pop3state instate)
00818 {
00819   CURLcode result = CURLE_OK;
00820   struct Curl_easy *data = conn->data;
00821   struct pop3_conn *pop3c = &conn->proto.pop3c;
00822   saslprogress progress;
00823 
00824   (void)instate; /* no use for this yet */
00825 
00826   result = Curl_sasl_continue(&pop3c->sasl, conn, pop3code, &progress);
00827   if(!result)
00828     switch(progress) {
00829     case SASL_DONE:
00830       state(conn, POP3_STOP);  /* Authenticated */
00831       break;
00832     case SASL_IDLE:            /* No mechanism left after cancellation */
00833 #ifndef CURL_DISABLE_CRYPTO_AUTH
00834       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
00835         /* Perform APOP authentication */
00836         result = pop3_perform_apop(conn);
00837       else
00838 #endif
00839       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
00840         /* Perform clear text authentication */
00841         result = pop3_perform_user(conn);
00842       else {
00843         failf(data, "Authentication cancelled");
00844         result = CURLE_LOGIN_DENIED;
00845       }
00846       break;
00847     default:
00848       break;
00849     }
00850 
00851   return result;
00852 }
00853 
00854 #ifndef CURL_DISABLE_CRYPTO_AUTH
00855 /* For APOP responses */
00856 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
00857                                      pop3state instate)
00858 {
00859   CURLcode result = CURLE_OK;
00860   struct Curl_easy *data = conn->data;
00861 
00862   (void)instate; /* no use for this yet */
00863 
00864   if(pop3code != '+') {
00865     failf(data, "Authentication failed: %d", pop3code);
00866     result = CURLE_LOGIN_DENIED;
00867   }
00868   else
00869     /* End of connect phase */
00870     state(conn, POP3_STOP);
00871 
00872   return result;
00873 }
00874 #endif
00875 
00876 /* For USER responses */
00877 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
00878                                      pop3state instate)
00879 {
00880   CURLcode result = CURLE_OK;
00881   struct Curl_easy *data = conn->data;
00882 
00883   (void)instate; /* no use for this yet */
00884 
00885   if(pop3code != '+') {
00886     failf(data, "Access denied. %c", pop3code);
00887     result = CURLE_LOGIN_DENIED;
00888   }
00889   else
00890     /* Send the PASS command */
00891     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
00892                            conn->passwd ? conn->passwd : "");
00893   if(!result)
00894     state(conn, POP3_PASS);
00895 
00896   return result;
00897 }
00898 
00899 /* For PASS responses */
00900 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
00901                                      pop3state instate)
00902 {
00903   CURLcode result = CURLE_OK;
00904   struct Curl_easy *data = conn->data;
00905 
00906   (void)instate; /* no use for this yet */
00907 
00908   if(pop3code != '+') {
00909     failf(data, "Access denied. %c", pop3code);
00910     result = CURLE_LOGIN_DENIED;
00911   }
00912   else
00913     /* End of connect phase */
00914     state(conn, POP3_STOP);
00915 
00916   return result;
00917 }
00918 
00919 /* For command responses */
00920 static CURLcode pop3_state_command_resp(struct connectdata *conn,
00921                                         int pop3code,
00922                                         pop3state instate)
00923 {
00924   CURLcode result = CURLE_OK;
00925   struct Curl_easy *data = conn->data;
00926   struct POP3 *pop3 = data->req.protop;
00927   struct pop3_conn *pop3c = &conn->proto.pop3c;
00928   struct pingpong *pp = &pop3c->pp;
00929 
00930   (void)instate; /* no use for this yet */
00931 
00932   if(pop3code != '+') {
00933     state(conn, POP3_STOP);
00934     return CURLE_RECV_ERROR;
00935   }
00936 
00937   /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
00938      EOB string so count this is two matching bytes. This is necessary to make
00939      the code detect the EOB if the only data than comes now is %2e CR LF like
00940      when there is no body to return. */
00941   pop3c->eob = 2;
00942 
00943   /* But since this initial CR LF pair is not part of the actual body, we set
00944      the strip counter here so that these bytes won't be delivered. */
00945   pop3c->strip = 2;
00946 
00947   if(pop3->transfer == FTPTRANSFER_BODY) {
00948     /* POP3 download */
00949     Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
00950 
00951     if(pp->cache) {
00952       /* The header "cache" contains a bunch of data that is actually body
00953          content so send it as such. Note that there may even be additional
00954          "headers" after the body */
00955 
00956       if(!data->set.opt_no_body) {
00957         result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
00958         if(result)
00959           return result;
00960       }
00961 
00962       /* Free the cache */
00963       Curl_safefree(pp->cache);
00964 
00965       /* Reset the cache size */
00966       pp->cache_size = 0;
00967     }
00968   }
00969 
00970   /* End of DO phase */
00971   state(conn, POP3_STOP);
00972 
00973   return result;
00974 }
00975 
00976 static CURLcode pop3_statemach_act(struct connectdata *conn)
00977 {
00978   CURLcode result = CURLE_OK;
00979   curl_socket_t sock = conn->sock[FIRSTSOCKET];
00980   int pop3code;
00981   struct pop3_conn *pop3c = &conn->proto.pop3c;
00982   struct pingpong *pp = &pop3c->pp;
00983   size_t nread = 0;
00984 
00985   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
00986   if(pop3c->state == POP3_UPGRADETLS)
00987     return pop3_perform_upgrade_tls(conn);
00988 
00989   /* Flush any data that needs to be sent */
00990   if(pp->sendleft)
00991     return Curl_pp_flushsend(pp);
00992 
00993  do {
00994     /* Read the response from the server */
00995     result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
00996     if(result)
00997       return result;
00998 
00999     if(!pop3code)
01000       break;
01001 
01002     /* We have now received a full POP3 server response */
01003     switch(pop3c->state) {
01004     case POP3_SERVERGREET:
01005       result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
01006       break;
01007 
01008     case POP3_CAPA:
01009       result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
01010       break;
01011 
01012     case POP3_STARTTLS:
01013       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
01014       break;
01015 
01016     case POP3_AUTH:
01017       result = pop3_state_auth_resp(conn, pop3code, pop3c->state);
01018       break;
01019 
01020 #ifndef CURL_DISABLE_CRYPTO_AUTH
01021     case POP3_APOP:
01022       result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
01023       break;
01024 #endif
01025 
01026     case POP3_USER:
01027       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
01028       break;
01029 
01030     case POP3_PASS:
01031       result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
01032       break;
01033 
01034     case POP3_COMMAND:
01035       result = pop3_state_command_resp(conn, pop3code, pop3c->state);
01036       break;
01037 
01038     case POP3_QUIT:
01039       /* fallthrough, just stop! */
01040     default:
01041       /* internal error */
01042       state(conn, POP3_STOP);
01043       break;
01044     }
01045   } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
01046 
01047   return result;
01048 }
01049 
01050 /* Called repeatedly until done from multi.c */
01051 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
01052 {
01053   CURLcode result = CURLE_OK;
01054   struct pop3_conn *pop3c = &conn->proto.pop3c;
01055 
01056   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
01057     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
01058     if(result || !pop3c->ssldone)
01059       return result;
01060   }
01061 
01062   result = Curl_pp_statemach(&pop3c->pp, FALSE);
01063   *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
01064 
01065   return result;
01066 }
01067 
01068 static CURLcode pop3_block_statemach(struct connectdata *conn)
01069 {
01070   CURLcode result = CURLE_OK;
01071   struct pop3_conn *pop3c = &conn->proto.pop3c;
01072 
01073   while(pop3c->state != POP3_STOP && !result)
01074     result = Curl_pp_statemach(&pop3c->pp, TRUE);
01075 
01076   return result;
01077 }
01078 
01079 /* Allocate and initialize the POP3 struct for the current Curl_easy if
01080    required */
01081 static CURLcode pop3_init(struct connectdata *conn)
01082 {
01083   CURLcode result = CURLE_OK;
01084   struct Curl_easy *data = conn->data;
01085   struct POP3 *pop3;
01086 
01087   pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
01088   if(!pop3)
01089     result = CURLE_OUT_OF_MEMORY;
01090 
01091   return result;
01092 }
01093 
01094 /* For the POP3 "protocol connect" and "doing" phases only */
01095 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
01096                         int numsocks)
01097 {
01098   return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
01099 }
01100 
01101 /***********************************************************************
01102  *
01103  * pop3_connect()
01104  *
01105  * This function should do everything that is to be considered a part of the
01106  * connection phase.
01107  *
01108  * The variable 'done' points to will be TRUE if the protocol-layer connect
01109  * phase is done when this function returns, or FALSE if not.
01110  */
01111 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
01112 {
01113   CURLcode result = CURLE_OK;
01114   struct pop3_conn *pop3c = &conn->proto.pop3c;
01115   struct pingpong *pp = &pop3c->pp;
01116 
01117   *done = FALSE; /* default to not done yet */
01118 
01119   /* We always support persistent connections in POP3 */
01120   connkeep(conn, "POP3 default");
01121 
01122   /* Set the default response time-out */
01123   pp->response_time = RESP_TIMEOUT;
01124   pp->statemach_act = pop3_statemach_act;
01125   pp->endofresp = pop3_endofresp;
01126   pp->conn = conn;
01127 
01128   /* Set the default preferred authentication type and mechanism */
01129   pop3c->preftype = POP3_TYPE_ANY;
01130   Curl_sasl_init(&pop3c->sasl, &saslpop3);
01131 
01132   /* Initialise the pingpong layer */
01133   Curl_pp_init(pp);
01134 
01135   /* Parse the URL options */
01136   result = pop3_parse_url_options(conn);
01137   if(result)
01138     return result;
01139 
01140   /* Start off waiting for the server greeting response */
01141   state(conn, POP3_SERVERGREET);
01142 
01143   result = pop3_multi_statemach(conn, done);
01144 
01145   return result;
01146 }
01147 
01148 /***********************************************************************
01149  *
01150  * pop3_done()
01151  *
01152  * The DONE function. This does what needs to be done after a single DO has
01153  * performed.
01154  *
01155  * Input argument is already checked for validity.
01156  */
01157 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
01158                           bool premature)
01159 {
01160   CURLcode result = CURLE_OK;
01161   struct Curl_easy *data = conn->data;
01162   struct POP3 *pop3 = data->req.protop;
01163 
01164   (void)premature;
01165 
01166   if(!pop3)
01167     return CURLE_OK;
01168 
01169   if(status) {
01170     connclose(conn, "POP3 done with bad status");
01171     result = status;         /* use the already set error code */
01172   }
01173 
01174   /* Cleanup our per-request based variables */
01175   Curl_safefree(pop3->id);
01176   Curl_safefree(pop3->custom);
01177 
01178   /* Clear the transfer mode for the next request */
01179   pop3->transfer = FTPTRANSFER_BODY;
01180 
01181   return result;
01182 }
01183 
01184 /***********************************************************************
01185  *
01186  * pop3_perform()
01187  *
01188  * This is the actual DO function for POP3. Get a message/listing according to
01189  * the options previously setup.
01190  */
01191 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
01192                              bool *dophase_done)
01193 {
01194   /* This is POP3 and no proxy */
01195   CURLcode result = CURLE_OK;
01196   struct POP3 *pop3 = conn->data->req.protop;
01197 
01198   DEBUGF(infof(conn->data, "DO phase starts\n"));
01199 
01200   if(conn->data->set.opt_no_body) {
01201     /* Requested no body means no transfer */
01202     pop3->transfer = FTPTRANSFER_INFO;
01203   }
01204 
01205   *dophase_done = FALSE; /* not done yet */
01206 
01207   /* Start the first command in the DO phase */
01208   result = pop3_perform_command(conn);
01209   if(result)
01210     return result;
01211 
01212   /* Run the state-machine */
01213   result = pop3_multi_statemach(conn, dophase_done);
01214 
01215   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
01216 
01217   if(*dophase_done)
01218     DEBUGF(infof(conn->data, "DO phase is complete\n"));
01219 
01220   return result;
01221 }
01222 
01223 /***********************************************************************
01224  *
01225  * pop3_do()
01226  *
01227  * This function is registered as 'curl_do' function. It decodes the path
01228  * parts etc as a wrapper to the actual DO function (pop3_perform).
01229  *
01230  * The input argument is already checked for validity.
01231  */
01232 static CURLcode pop3_do(struct connectdata *conn, bool *done)
01233 {
01234   CURLcode result = CURLE_OK;
01235 
01236   *done = FALSE; /* default to false */
01237 
01238   /* Parse the URL path */
01239   result = pop3_parse_url_path(conn);
01240   if(result)
01241     return result;
01242 
01243   /* Parse the custom request */
01244   result = pop3_parse_custom_request(conn);
01245   if(result)
01246     return result;
01247 
01248   result = pop3_regular_transfer(conn, done);
01249 
01250   return result;
01251 }
01252 
01253 /***********************************************************************
01254  *
01255  * pop3_disconnect()
01256  *
01257  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
01258  * resources. BLOCKING.
01259  */
01260 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
01261 {
01262   struct pop3_conn *pop3c = &conn->proto.pop3c;
01263 
01264   /* We cannot send quit unconditionally. If this connection is stale or
01265      bad in any way, sending quit and waiting around here will make the
01266      disconnect wait in vain and cause more problems than we need to. */
01267 
01268   /* The POP3 session may or may not have been allocated/setup at this
01269      point! */
01270   if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
01271     if(!pop3_perform_quit(conn))
01272       (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
01273 
01274   /* Disconnect from the server */
01275   Curl_pp_disconnect(&pop3c->pp);
01276 
01277   /* Cleanup the SASL module */
01278   Curl_sasl_cleanup(conn, pop3c->sasl.authused);
01279 
01280   /* Cleanup our connection based variables */
01281   Curl_safefree(pop3c->apoptimestamp);
01282 
01283   return CURLE_OK;
01284 }
01285 
01286 /* Call this when the DO phase has completed */
01287 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
01288 {
01289   (void)conn;
01290   (void)connected;
01291 
01292   return CURLE_OK;
01293 }
01294 
01295 /* Called from multi.c while DOing */
01296 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
01297 {
01298   CURLcode result = pop3_multi_statemach(conn, dophase_done);
01299 
01300   if(result)
01301     DEBUGF(infof(conn->data, "DO phase failed\n"));
01302   else if(*dophase_done) {
01303     result = pop3_dophase_done(conn, FALSE /* not connected */);
01304 
01305     DEBUGF(infof(conn->data, "DO phase is complete\n"));
01306   }
01307 
01308   return result;
01309 }
01310 
01311 /***********************************************************************
01312  *
01313  * pop3_regular_transfer()
01314  *
01315  * The input argument is already checked for validity.
01316  *
01317  * Performs all commands done before a regular transfer between a local and a
01318  * remote host.
01319  */
01320 static CURLcode pop3_regular_transfer(struct connectdata *conn,
01321                                       bool *dophase_done)
01322 {
01323   CURLcode result = CURLE_OK;
01324   bool connected = FALSE;
01325   struct Curl_easy *data = conn->data;
01326 
01327   /* Make sure size is unknown at this point */
01328   data->req.size = -1;
01329 
01330   /* Set the progress data */
01331   Curl_pgrsSetUploadCounter(data, 0);
01332   Curl_pgrsSetDownloadCounter(data, 0);
01333   Curl_pgrsSetUploadSize(data, -1);
01334   Curl_pgrsSetDownloadSize(data, -1);
01335 
01336   /* Carry out the perform */
01337   result = pop3_perform(conn, &connected, dophase_done);
01338 
01339   /* Perform post DO phase operations if necessary */
01340   if(!result && *dophase_done)
01341     result = pop3_dophase_done(conn, connected);
01342 
01343   return result;
01344 }
01345 
01346 static CURLcode pop3_setup_connection(struct connectdata *conn)
01347 {
01348   struct Curl_easy *data = conn->data;
01349 
01350   /* Initialise the POP3 layer */
01351   CURLcode result = pop3_init(conn);
01352   if(result)
01353     return result;
01354 
01355   /* Clear the TLS upgraded flag */
01356   conn->tls_upgraded = FALSE;
01357 
01358   /* Set up the proxy if necessary */
01359   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
01360     /* Unless we have asked to tunnel POP3 operations through the proxy, we
01361        switch and use HTTP operations only */
01362 #ifndef CURL_DISABLE_HTTP
01363     if(conn->handler == &Curl_handler_pop3)
01364       conn->handler = &Curl_handler_pop3_proxy;
01365     else {
01366 #ifdef USE_SSL
01367       conn->handler = &Curl_handler_pop3s_proxy;
01368 #else
01369       failf(data, "POP3S not supported!");
01370       return CURLE_UNSUPPORTED_PROTOCOL;
01371 #endif
01372     }
01373 
01374     /* set it up as an HTTP connection instead */
01375     return conn->handler->setup_connection(conn);
01376 #else
01377     failf(data, "POP3 over http proxy requires HTTP support built-in!");
01378     return CURLE_UNSUPPORTED_PROTOCOL;
01379 #endif
01380   }
01381 
01382   data->state.path++;   /* don't include the initial slash */
01383 
01384   return CURLE_OK;
01385 }
01386 
01387 /***********************************************************************
01388  *
01389  * pop3_parse_url_options()
01390  *
01391  * Parse the URL login options.
01392  */
01393 static CURLcode pop3_parse_url_options(struct connectdata *conn)
01394 {
01395   CURLcode result = CURLE_OK;
01396   struct pop3_conn *pop3c = &conn->proto.pop3c;
01397   const char *ptr = conn->options;
01398 
01399   pop3c->sasl.resetprefs = TRUE;
01400 
01401   while(!result && ptr && *ptr) {
01402     const char *key = ptr;
01403     const char *value;
01404 
01405     while(*ptr && *ptr != '=')
01406         ptr++;
01407 
01408     value = ptr + 1;
01409 
01410     while(*ptr && *ptr != ';')
01411       ptr++;
01412 
01413     if(strncasecompare(key, "AUTH=", 5)) {
01414       result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
01415                                                value, ptr - value);
01416 
01417       if(result && strncasecompare(value, "+APOP", ptr - value)) {
01418         pop3c->preftype = POP3_TYPE_APOP;
01419         pop3c->sasl.prefmech = SASL_AUTH_NONE;
01420         result = CURLE_OK;
01421       }
01422     }
01423     else
01424       result = CURLE_URL_MALFORMAT;
01425 
01426     if(*ptr == ';')
01427       ptr++;
01428   }
01429 
01430   if(pop3c->preftype != POP3_TYPE_APOP)
01431     switch(pop3c->sasl.prefmech) {
01432     case SASL_AUTH_NONE:
01433       pop3c->preftype = POP3_TYPE_NONE;
01434       break;
01435     case SASL_AUTH_DEFAULT:
01436       pop3c->preftype = POP3_TYPE_ANY;
01437       break;
01438     default:
01439       pop3c->preftype = POP3_TYPE_SASL;
01440       break;
01441     }
01442 
01443   return result;
01444 }
01445 
01446 /***********************************************************************
01447  *
01448  * pop3_parse_url_path()
01449  *
01450  * Parse the URL path into separate path components.
01451  */
01452 static CURLcode pop3_parse_url_path(struct connectdata *conn)
01453 {
01454   /* The POP3 struct is already initialised in pop3_connect() */
01455   struct Curl_easy *data = conn->data;
01456   struct POP3 *pop3 = data->req.protop;
01457   const char *path = data->state.path;
01458 
01459   /* URL decode the path for the message ID */
01460   return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
01461 }
01462 
01463 /***********************************************************************
01464  *
01465  * pop3_parse_custom_request()
01466  *
01467  * Parse the custom request.
01468  */
01469 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
01470 {
01471   CURLcode result = CURLE_OK;
01472   struct Curl_easy *data = conn->data;
01473   struct POP3 *pop3 = data->req.protop;
01474   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
01475 
01476   /* URL decode the custom request */
01477   if(custom)
01478     result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
01479 
01480   return result;
01481 }
01482 
01483 /***********************************************************************
01484  *
01485  * Curl_pop3_write()
01486  *
01487  * This function scans the body after the end-of-body and writes everything
01488  * until the end is found.
01489  */
01490 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
01491 {
01492   /* This code could be made into a special function in the handler struct */
01493   CURLcode result = CURLE_OK;
01494   struct Curl_easy *data = conn->data;
01495   struct SingleRequest *k = &data->req;
01496 
01497   struct pop3_conn *pop3c = &conn->proto.pop3c;
01498   bool strip_dot = FALSE;
01499   size_t last = 0;
01500   size_t i;
01501 
01502   /* Search through the buffer looking for the end-of-body marker which is
01503      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
01504      the eob so the server will have prefixed it with an extra dot which we
01505      need to strip out. Additionally the marker could of course be spread out
01506      over 5 different data chunks. */
01507   for(i = 0; i < nread; i++) {
01508     size_t prev = pop3c->eob;
01509 
01510     switch(str[i]) {
01511     case 0x0d:
01512       if(pop3c->eob == 0) {
01513         pop3c->eob++;
01514 
01515         if(i) {
01516           /* Write out the body part that didn't match */
01517           result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
01518                                      i - last);
01519 
01520           if(result)
01521             return result;
01522 
01523           last = i;
01524         }
01525       }
01526       else if(pop3c->eob == 3)
01527         pop3c->eob++;
01528       else
01529         /* If the character match wasn't at position 0 or 3 then restart the
01530            pattern matching */
01531         pop3c->eob = 1;
01532       break;
01533 
01534     case 0x0a:
01535       if(pop3c->eob == 1 || pop3c->eob == 4)
01536         pop3c->eob++;
01537       else
01538         /* If the character match wasn't at position 1 or 4 then start the
01539            search again */
01540         pop3c->eob = 0;
01541       break;
01542 
01543     case 0x2e:
01544       if(pop3c->eob == 2)
01545         pop3c->eob++;
01546       else if(pop3c->eob == 3) {
01547         /* We have an extra dot after the CRLF which we need to strip off */
01548         strip_dot = TRUE;
01549         pop3c->eob = 0;
01550       }
01551       else
01552         /* If the character match wasn't at position 2 then start the search
01553            again */
01554         pop3c->eob = 0;
01555       break;
01556 
01557     default:
01558       pop3c->eob = 0;
01559       break;
01560     }
01561 
01562     /* Did we have a partial match which has subsequently failed? */
01563     if(prev && prev >= pop3c->eob) {
01564       /* Strip can only be non-zero for the very first mismatch after CRLF
01565          and then both prev and strip are equal and nothing will be output
01566          below */
01567       while(prev && pop3c->strip) {
01568         prev--;
01569         pop3c->strip--;
01570       }
01571 
01572       if(prev) {
01573         /* If the partial match was the CRLF and dot then only write the CRLF
01574            as the server would have inserted the dot */
01575         result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB,
01576                                    strip_dot ? prev - 1 : prev);
01577 
01578         if(result)
01579           return result;
01580 
01581         last = i;
01582         strip_dot = FALSE;
01583       }
01584     }
01585   }
01586 
01587   if(pop3c->eob == POP3_EOB_LEN) {
01588     /* We have a full match so the transfer is done, however we must transfer
01589     the CRLF at the start of the EOB as this is considered to be part of the
01590     message as per RFC-1939, sect. 3 */
01591     result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
01592 
01593     k->keepon &= ~KEEP_RECV;
01594     pop3c->eob = 0;
01595 
01596     return result;
01597   }
01598 
01599   if(pop3c->eob)
01600     /* While EOB is matching nothing should be output */
01601     return CURLE_OK;
01602 
01603   if(nread - last) {
01604     result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
01605                                nread - last);
01606   }
01607 
01608   return result;
01609 }
01610 
01611 #endif /* CURL_DISABLE_POP3 */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06