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