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