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