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