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