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