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