xref: /curl/lib/pop3.c (revision c294f9cb)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  * RFC1734 POP3 Authentication
24  * RFC1939 POP3 protocol
25  * RFC2195 CRAM-MD5 authentication
26  * RFC2384 POP URL Scheme
27  * RFC2449 POP3 Extension Mechanism
28  * RFC2595 Using TLS with IMAP, POP3 and ACAP
29  * RFC2831 DIGEST-MD5 authentication
30  * RFC4422 Simple Authentication and Security Layer (SASL)
31  * RFC4616 PLAIN authentication
32  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
33  * RFC5034 POP3 SASL Authentication Mechanism
34  * RFC6749 OAuth 2.0 Authorization Framework
35  * RFC8314 Use of TLS for Email Submission and Access
36  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
37  *
38  ***************************************************************************/
39 
40 #include "curl_setup.h"
41 
42 #ifndef CURL_DISABLE_POP3
43 
44 #ifdef HAVE_NETINET_IN_H
45 #include <netinet/in.h>
46 #endif
47 #ifdef HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
49 #endif
50 #ifdef HAVE_NETDB_H
51 #include <netdb.h>
52 #endif
53 #ifdef __VMS
54 #include <in.h>
55 #include <inet.h>
56 #endif
57 
58 #include <curl/curl.h>
59 #include "urldata.h"
60 #include "sendf.h"
61 #include "hostip.h"
62 #include "progress.h"
63 #include "transfer.h"
64 #include "escape.h"
65 #include "http.h" /* for HTTP proxy tunnel stuff */
66 #include "socks.h"
67 #include "pop3.h"
68 #include "strtoofft.h"
69 #include "strcase.h"
70 #include "vtls/vtls.h"
71 #include "cfilters.h"
72 #include "connect.h"
73 #include "select.h"
74 #include "multiif.h"
75 #include "url.h"
76 #include "bufref.h"
77 #include "curl_sasl.h"
78 #include "curl_md5.h"
79 #include "warnless.h"
80 #include "strdup.h"
81 /* The last 3 #include files should be in this order */
82 #include "curl_printf.h"
83 #include "curl_memory.h"
84 #include "memdebug.h"
85 
86 /* Local API functions */
87 static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done);
88 static CURLcode pop3_do(struct Curl_easy *data, bool *done);
89 static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
90                           bool premature);
91 static CURLcode pop3_connect(struct Curl_easy *data, bool *done);
92 static CURLcode pop3_disconnect(struct Curl_easy *data,
93                                 struct connectdata *conn, bool dead);
94 static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done);
95 static int pop3_getsock(struct Curl_easy *data,
96                         struct connectdata *conn, curl_socket_t *socks);
97 static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done);
98 static CURLcode pop3_setup_connection(struct Curl_easy *data,
99                                       struct connectdata *conn);
100 static CURLcode pop3_parse_url_options(struct connectdata *conn);
101 static CURLcode pop3_parse_url_path(struct Curl_easy *data);
102 static CURLcode pop3_parse_custom_request(struct Curl_easy *data);
103 static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech,
104                                   const struct bufref *initresp);
105 static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech,
106                                    const struct bufref *resp);
107 static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech);
108 static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out);
109 
110 /*
111  * POP3 protocol handler.
112  */
113 
114 const struct Curl_handler Curl_handler_pop3 = {
115   "pop3",                           /* scheme */
116   pop3_setup_connection,            /* setup_connection */
117   pop3_do,                          /* do_it */
118   pop3_done,                        /* done */
119   ZERO_NULL,                        /* do_more */
120   pop3_connect,                     /* connect_it */
121   pop3_multi_statemach,             /* connecting */
122   pop3_doing,                       /* doing */
123   pop3_getsock,                     /* proto_getsock */
124   pop3_getsock,                     /* doing_getsock */
125   ZERO_NULL,                        /* domore_getsock */
126   ZERO_NULL,                        /* perform_getsock */
127   pop3_disconnect,                  /* disconnect */
128   ZERO_NULL,                        /* write_resp */
129   ZERO_NULL,                        /* write_resp_hd */
130   ZERO_NULL,                        /* connection_check */
131   ZERO_NULL,                        /* attach connection */
132   PORT_POP3,                        /* defport */
133   CURLPROTO_POP3,                   /* protocol */
134   CURLPROTO_POP3,                   /* family */
135   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
136   PROTOPT_URLOPTIONS
137 };
138 
139 #ifdef USE_SSL
140 /*
141  * POP3S protocol handler.
142  */
143 
144 const struct Curl_handler Curl_handler_pop3s = {
145   "pop3s",                          /* scheme */
146   pop3_setup_connection,            /* setup_connection */
147   pop3_do,                          /* do_it */
148   pop3_done,                        /* done */
149   ZERO_NULL,                        /* do_more */
150   pop3_connect,                     /* connect_it */
151   pop3_multi_statemach,             /* connecting */
152   pop3_doing,                       /* doing */
153   pop3_getsock,                     /* proto_getsock */
154   pop3_getsock,                     /* doing_getsock */
155   ZERO_NULL,                        /* domore_getsock */
156   ZERO_NULL,                        /* perform_getsock */
157   pop3_disconnect,                  /* disconnect */
158   ZERO_NULL,                        /* write_resp */
159   ZERO_NULL,                        /* write_resp_hd */
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 = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
258   size_t len = data->conn->proto.pop3c.pp.nfinal;
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  * pop3_state()
286  *
287  * This is the ONLY way to change POP3 state!
288  */
pop3_state(struct Curl_easy * data,pop3state newstate)289 static void pop3_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     pop3_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     pop3_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       pop3_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     pop3_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     pop3_state(data, POP3_USER);
421 
422   return result;
423 }
424 
425 #ifndef CURL_DISABLE_DIGEST_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     pop3_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     pop3_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     pop3_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         pop3_state(data, POP3_AUTH);
566   }
567 
568   if(!result && progress == SASL_IDLE) {
569 #ifndef CURL_DISABLE_DIGEST_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     pop3_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     pop3_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 = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
655   size_t len = data->conn->proto.pop3c.pp.nfinal;
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 if(len > 3) {
664     /* Does the server support APOP authentication? */
665     char *lt;
666     char *gt = NULL;
667 
668     /* Look for the APOP timestamp */
669     lt = memchr(line, '<', len);
670     if(lt)
671       /* search the remainder for '>' */
672       gt = memchr(lt, '>', len - (lt - line));
673     if(gt) {
674       /* the length of the timestamp, including the brackets */
675       size_t timestamplen = gt - lt + 1;
676       char *at = memchr(lt, '@', timestamplen);
677       /* If the timestamp does not contain '@' it is not (as required by
678          RFC-1939) conformant to the RFC-822 message id syntax, and we
679          therefore do not use APOP authentication. */
680       if(at) {
681         /* dupe the timestamp */
682         pop3c->apoptimestamp = Curl_memdup0(lt, timestamplen);
683         if(!pop3c->apoptimestamp)
684           return CURLE_OUT_OF_MEMORY;
685         /* Store the APOP capability */
686         pop3c->authtypes |= POP3_TYPE_APOP;
687       }
688     }
689 
690     if(!result)
691       result = pop3_perform_capa(data, conn);
692   }
693 
694   return result;
695 }
696 
697 /* For CAPA responses */
pop3_state_capa_resp(struct Curl_easy * data,int pop3code,pop3state instate)698 static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
699                                      pop3state instate)
700 {
701   CURLcode result = CURLE_OK;
702   struct connectdata *conn = data->conn;
703   struct pop3_conn *pop3c = &conn->proto.pop3c;
704   const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
705   size_t len = data->conn->proto.pop3c.pp.nfinal;
706 
707   (void)instate; /* no use for this yet */
708 
709   /* Do we have a untagged continuation response? */
710   if(pop3code == '*') {
711     /* Does the server support the STLS capability? */
712     if(len >= 4 && !memcmp(line, "STLS", 4))
713       pop3c->tls_supported = TRUE;
714 
715     /* Does the server support clear text authentication? */
716     else if(len >= 4 && !memcmp(line, "USER", 4))
717       pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
718 
719     /* Does the server support SASL based authentication? */
720     else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
721       pop3c->authtypes |= POP3_TYPE_SASL;
722 
723       /* Advance past the SASL keyword */
724       line += 5;
725       len -= 5;
726 
727       /* Loop through the data line */
728       for(;;) {
729         size_t llen;
730         size_t wordlen;
731         unsigned short mechbit;
732 
733         while(len &&
734               (*line == ' ' || *line == '\t' ||
735                *line == '\r' || *line == '\n')) {
736 
737           line++;
738           len--;
739         }
740 
741         if(!len)
742           break;
743 
744         /* Extract the word */
745         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
746               line[wordlen] != '\t' && line[wordlen] != '\r' &&
747               line[wordlen] != '\n';)
748           wordlen++;
749 
750         /* Test the word for a matching authentication mechanism */
751         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
752         if(mechbit && llen == wordlen)
753           pop3c->sasl.authmechs |= mechbit;
754 
755         line += wordlen;
756         len -= wordlen;
757       }
758     }
759   }
760   else {
761     /* Clear text is supported when CAPA isn't recognised */
762     if(pop3code != '+')
763       pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
764 
765     if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET))
766       result = pop3_perform_authentication(data, conn);
767     else if(pop3code == '+' && pop3c->tls_supported)
768       /* Switch to TLS connection now */
769       result = pop3_perform_starttls(data, conn);
770     else if(data->set.use_ssl <= CURLUSESSL_TRY)
771       /* Fallback and carry on with authentication */
772       result = pop3_perform_authentication(data, conn);
773     else {
774       failf(data, "STLS not supported.");
775       result = CURLE_USE_SSL_FAILED;
776     }
777   }
778 
779   return result;
780 }
781 
782 /* For STARTTLS responses */
pop3_state_starttls_resp(struct Curl_easy * data,struct connectdata * conn,int pop3code,pop3state instate)783 static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
784                                          struct connectdata *conn,
785                                          int pop3code,
786                                          pop3state instate)
787 {
788   CURLcode result = CURLE_OK;
789   (void)instate; /* no use for this yet */
790 
791   /* Pipelining in response is forbidden. */
792   if(data->conn->proto.pop3c.pp.overflow)
793     return CURLE_WEIRD_SERVER_REPLY;
794 
795   if(pop3code != '+') {
796     if(data->set.use_ssl != CURLUSESSL_TRY) {
797       failf(data, "STARTTLS denied");
798       result = CURLE_USE_SSL_FAILED;
799     }
800     else
801       result = pop3_perform_authentication(data, conn);
802   }
803   else
804     result = pop3_perform_upgrade_tls(data, conn);
805 
806   return result;
807 }
808 
809 /* For SASL authentication responses */
pop3_state_auth_resp(struct Curl_easy * data,int pop3code,pop3state instate)810 static CURLcode pop3_state_auth_resp(struct Curl_easy *data,
811                                      int pop3code,
812                                      pop3state instate)
813 {
814   CURLcode result = CURLE_OK;
815   struct connectdata *conn = data->conn;
816   struct pop3_conn *pop3c = &conn->proto.pop3c;
817   saslprogress progress;
818 
819   (void)instate; /* no use for this yet */
820 
821   result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress);
822   if(!result)
823     switch(progress) {
824     case SASL_DONE:
825       pop3_state(data, POP3_STOP);  /* Authenticated */
826       break;
827     case SASL_IDLE:            /* No mechanism left after cancellation */
828 #ifndef CURL_DISABLE_DIGEST_AUTH
829       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
830         /* Perform APOP authentication */
831         result = pop3_perform_apop(data, conn);
832       else
833 #endif
834       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
835         /* Perform clear text authentication */
836         result = pop3_perform_user(data, conn);
837       else {
838         failf(data, "Authentication cancelled");
839         result = CURLE_LOGIN_DENIED;
840       }
841       break;
842     default:
843       break;
844     }
845 
846   return result;
847 }
848 
849 #ifndef CURL_DISABLE_DIGEST_AUTH
850 /* For APOP responses */
pop3_state_apop_resp(struct Curl_easy * data,int pop3code,pop3state instate)851 static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code,
852                                      pop3state instate)
853 {
854   CURLcode result = CURLE_OK;
855   (void)instate; /* no use for this yet */
856 
857   if(pop3code != '+') {
858     failf(data, "Authentication failed: %d", pop3code);
859     result = CURLE_LOGIN_DENIED;
860   }
861   else
862     /* End of connect phase */
863     pop3_state(data, POP3_STOP);
864 
865   return result;
866 }
867 #endif
868 
869 /* For USER responses */
pop3_state_user_resp(struct Curl_easy * data,int pop3code,pop3state instate)870 static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code,
871                                      pop3state instate)
872 {
873   CURLcode result = CURLE_OK;
874   struct connectdata *conn = data->conn;
875   (void)instate; /* no use for this yet */
876 
877   if(pop3code != '+') {
878     failf(data, "Access denied. %c", pop3code);
879     result = CURLE_LOGIN_DENIED;
880   }
881   else
882     /* Send the PASS command */
883     result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s",
884                            conn->passwd ? conn->passwd : "");
885   if(!result)
886     pop3_state(data, POP3_PASS);
887 
888   return result;
889 }
890 
891 /* For PASS responses */
pop3_state_pass_resp(struct Curl_easy * data,int pop3code,pop3state instate)892 static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code,
893                                      pop3state instate)
894 {
895   CURLcode result = CURLE_OK;
896   (void)instate; /* no use for this yet */
897 
898   if(pop3code != '+') {
899     failf(data, "Access denied. %c", pop3code);
900     result = CURLE_LOGIN_DENIED;
901   }
902   else
903     /* End of connect phase */
904     pop3_state(data, POP3_STOP);
905 
906   return result;
907 }
908 
909 /* For command responses */
pop3_state_command_resp(struct Curl_easy * data,int pop3code,pop3state instate)910 static CURLcode pop3_state_command_resp(struct Curl_easy *data,
911                                         int pop3code,
912                                         pop3state instate)
913 {
914   CURLcode result = CURLE_OK;
915   struct connectdata *conn = data->conn;
916   struct POP3 *pop3 = data->req.p.pop3;
917   struct pop3_conn *pop3c = &conn->proto.pop3c;
918   struct pingpong *pp = &pop3c->pp;
919 
920   (void)instate; /* no use for this yet */
921 
922   if(pop3code != '+') {
923     pop3_state(data, POP3_STOP);
924     return CURLE_WEIRD_SERVER_REPLY;
925   }
926 
927   /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
928      EOB string so count this is two matching bytes. This is necessary to make
929      the code detect the EOB if the only data than comes now is %2e CR LF like
930      when there is no body to return. */
931   pop3c->eob = 2;
932 
933   /* But since this initial CR LF pair is not part of the actual body, we set
934      the strip counter here so that these bytes won't be delivered. */
935   pop3c->strip = 2;
936 
937   if(pop3->transfer == PPTRANSFER_BODY) {
938     /* POP3 download */
939     Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
940 
941     if(pp->overflow) {
942       /* The recv buffer contains data that is actually body content so send
943          it as such. Note that there may even be additional "headers" after
944          the body */
945 
946       /* keep only the overflow */
947       Curl_dyn_tail(&pp->recvbuf, pp->overflow);
948       pp->nfinal = 0; /* done */
949 
950       if(!data->req.no_body) {
951         result = Curl_pop3_write(data, Curl_dyn_ptr(&pp->recvbuf),
952                                  Curl_dyn_len(&pp->recvbuf));
953         if(result)
954           return result;
955       }
956 
957       /* reset the buffer */
958       Curl_dyn_reset(&pp->recvbuf);
959       pp->overflow = 0;
960     }
961   }
962   else
963     pp->overflow = 0;
964 
965   /* End of DO phase */
966   pop3_state(data, POP3_STOP);
967 
968   return result;
969 }
970 
pop3_statemachine(struct Curl_easy * data,struct connectdata * conn)971 static CURLcode pop3_statemachine(struct Curl_easy *data,
972                                   struct connectdata *conn)
973 {
974   CURLcode result = CURLE_OK;
975   int pop3code;
976   struct pop3_conn *pop3c = &conn->proto.pop3c;
977   struct pingpong *pp = &pop3c->pp;
978   size_t nread = 0;
979   (void)data;
980 
981   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
982   if(pop3c->state == POP3_UPGRADETLS)
983     return pop3_perform_upgrade_tls(data, conn);
984 
985   /* Flush any data that needs to be sent */
986   if(pp->sendleft)
987     return Curl_pp_flushsend(data, pp);
988 
989  do {
990     /* Read the response from the server */
991    result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &pop3code, &nread);
992    if(result)
993      return result;
994 
995     if(!pop3code)
996       break;
997 
998     /* We have now received a full POP3 server response */
999     switch(pop3c->state) {
1000     case POP3_SERVERGREET:
1001       result = pop3_state_servergreet_resp(data, pop3code, pop3c->state);
1002       break;
1003 
1004     case POP3_CAPA:
1005       result = pop3_state_capa_resp(data, pop3code, pop3c->state);
1006       break;
1007 
1008     case POP3_STARTTLS:
1009       result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state);
1010       break;
1011 
1012     case POP3_AUTH:
1013       result = pop3_state_auth_resp(data, pop3code, pop3c->state);
1014       break;
1015 
1016 #ifndef CURL_DISABLE_DIGEST_AUTH
1017     case POP3_APOP:
1018       result = pop3_state_apop_resp(data, pop3code, pop3c->state);
1019       break;
1020 #endif
1021 
1022     case POP3_USER:
1023       result = pop3_state_user_resp(data, pop3code, pop3c->state);
1024       break;
1025 
1026     case POP3_PASS:
1027       result = pop3_state_pass_resp(data, pop3code, pop3c->state);
1028       break;
1029 
1030     case POP3_COMMAND:
1031       result = pop3_state_command_resp(data, pop3code, pop3c->state);
1032       break;
1033 
1034     case POP3_QUIT:
1035       pop3_state(data, POP3_STOP);
1036       break;
1037 
1038     default:
1039       /* internal error */
1040       pop3_state(data, POP3_STOP);
1041       break;
1042     }
1043   } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1044 
1045   return result;
1046 }
1047 
1048 /* Called repeatedly until done from multi.c */
pop3_multi_statemach(struct Curl_easy * data,bool * done)1049 static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
1050 {
1051   CURLcode result = CURLE_OK;
1052   struct connectdata *conn = data->conn;
1053   struct pop3_conn *pop3c = &conn->proto.pop3c;
1054 
1055   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1056     bool ssldone = FALSE;
1057     result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
1058     pop3c->ssldone = ssldone;
1059     if(result || !pop3c->ssldone)
1060       return result;
1061   }
1062 
1063   result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE);
1064   *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1065 
1066   return result;
1067 }
1068 
pop3_block_statemach(struct Curl_easy * data,struct connectdata * conn,bool disconnecting)1069 static CURLcode pop3_block_statemach(struct Curl_easy *data,
1070                                      struct connectdata *conn,
1071                                      bool disconnecting)
1072 {
1073   CURLcode result = CURLE_OK;
1074   struct pop3_conn *pop3c = &conn->proto.pop3c;
1075 
1076   while(pop3c->state != POP3_STOP && !result)
1077     result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting);
1078 
1079   return result;
1080 }
1081 
1082 /* Allocate and initialize the POP3 struct for the current Curl_easy if
1083    required */
pop3_init(struct Curl_easy * data)1084 static CURLcode pop3_init(struct Curl_easy *data)
1085 {
1086   CURLcode result = CURLE_OK;
1087   struct POP3 *pop3;
1088 
1089   pop3 = data->req.p.pop3 = calloc(1, sizeof(struct POP3));
1090   if(!pop3)
1091     result = CURLE_OUT_OF_MEMORY;
1092 
1093   return result;
1094 }
1095 
1096 /* For the POP3 "protocol connect" and "doing" phases only */
pop3_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)1097 static int pop3_getsock(struct Curl_easy *data,
1098                         struct connectdata *conn, curl_socket_t *socks)
1099 {
1100   return Curl_pp_getsock(data, &conn->proto.pop3c.pp, socks);
1101 }
1102 
1103 /***********************************************************************
1104  *
1105  * pop3_connect()
1106  *
1107  * This function should do everything that is to be considered a part of the
1108  * connection phase.
1109  *
1110  * The variable 'done' points to will be TRUE if the protocol-layer connect
1111  * phase is done when this function returns, or FALSE if not.
1112  */
pop3_connect(struct Curl_easy * data,bool * done)1113 static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
1114 {
1115   CURLcode result = CURLE_OK;
1116   struct connectdata *conn = data->conn;
1117   struct pop3_conn *pop3c = &conn->proto.pop3c;
1118   struct pingpong *pp = &pop3c->pp;
1119 
1120   *done = FALSE; /* default to not done yet */
1121 
1122   /* We always support persistent connections in POP3 */
1123   connkeep(conn, "POP3 default");
1124 
1125   PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp);
1126 
1127   /* Set the default preferred authentication type and mechanism */
1128   pop3c->preftype = POP3_TYPE_ANY;
1129   Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
1130 
1131   /* Initialise the pingpong layer */
1132   Curl_pp_init(pp);
1133 
1134   /* Parse the URL options */
1135   result = pop3_parse_url_options(conn);
1136   if(result)
1137     return result;
1138 
1139   /* Start off waiting for the server greeting response */
1140   pop3_state(data, POP3_SERVERGREET);
1141 
1142   result = pop3_multi_statemach(data, done);
1143 
1144   return result;
1145 }
1146 
1147 /***********************************************************************
1148  *
1149  * pop3_done()
1150  *
1151  * The DONE function. This does what needs to be done after a single DO has
1152  * performed.
1153  *
1154  * Input argument is already checked for validity.
1155  */
pop3_done(struct Curl_easy * data,CURLcode status,bool premature)1156 static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
1157                           bool premature)
1158 {
1159   CURLcode result = CURLE_OK;
1160   struct POP3 *pop3 = data->req.p.pop3;
1161 
1162   (void)premature;
1163 
1164   if(!pop3)
1165     return CURLE_OK;
1166 
1167   if(status) {
1168     connclose(data->conn, "POP3 done with bad status");
1169     result = status;         /* use the already set error code */
1170   }
1171 
1172   /* Cleanup our per-request based variables */
1173   Curl_safefree(pop3->id);
1174   Curl_safefree(pop3->custom);
1175 
1176   /* Clear the transfer mode for the next request */
1177   pop3->transfer = PPTRANSFER_BODY;
1178 
1179   return result;
1180 }
1181 
1182 /***********************************************************************
1183  *
1184  * pop3_perform()
1185  *
1186  * This is the actual DO function for POP3. Get a message/listing according to
1187  * the options previously setup.
1188  */
pop3_perform(struct Curl_easy * data,bool * connected,bool * dophase_done)1189 static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
1190                              bool *dophase_done)
1191 {
1192   /* This is POP3 and no proxy */
1193   CURLcode result = CURLE_OK;
1194   struct POP3 *pop3 = data->req.p.pop3;
1195 
1196   DEBUGF(infof(data, "DO phase starts"));
1197 
1198   if(data->req.no_body) {
1199     /* Requested no body means no transfer */
1200     pop3->transfer = PPTRANSFER_INFO;
1201   }
1202 
1203   *dophase_done = FALSE; /* not done yet */
1204 
1205   /* Start the first command in the DO phase */
1206   result = pop3_perform_command(data);
1207   if(result)
1208     return result;
1209 
1210   /* Run the state-machine */
1211   result = pop3_multi_statemach(data, dophase_done);
1212   *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
1213 
1214   if(*dophase_done)
1215     DEBUGF(infof(data, "DO phase is complete"));
1216 
1217   return result;
1218 }
1219 
1220 /***********************************************************************
1221  *
1222  * pop3_do()
1223  *
1224  * This function is registered as 'curl_do' function. It decodes the path
1225  * parts etc as a wrapper to the actual DO function (pop3_perform).
1226  *
1227  * The input argument is already checked for validity.
1228  */
pop3_do(struct Curl_easy * data,bool * done)1229 static CURLcode pop3_do(struct Curl_easy *data, bool *done)
1230 {
1231   CURLcode result = CURLE_OK;
1232   *done = FALSE; /* default to false */
1233 
1234   /* Parse the URL path */
1235   result = pop3_parse_url_path(data);
1236   if(result)
1237     return result;
1238 
1239   /* Parse the custom request */
1240   result = pop3_parse_custom_request(data);
1241   if(result)
1242     return result;
1243 
1244   result = pop3_regular_transfer(data, done);
1245 
1246   return result;
1247 }
1248 
1249 /***********************************************************************
1250  *
1251  * pop3_disconnect()
1252  *
1253  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1254  * resources. BLOCKING.
1255  */
pop3_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)1256 static CURLcode pop3_disconnect(struct Curl_easy *data,
1257                                 struct connectdata *conn, bool dead_connection)
1258 {
1259   struct pop3_conn *pop3c = &conn->proto.pop3c;
1260   (void)data;
1261 
1262   /* We cannot send quit unconditionally. If this connection is stale or
1263      bad in any way, sending quit and waiting around here will make the
1264      disconnect wait in vain and cause more problems than we need to. */
1265 
1266   if(!dead_connection && conn->bits.protoconnstart) {
1267     if(!pop3_perform_quit(data, conn))
1268       (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
1269   }
1270 
1271   /* Disconnect from the server */
1272   Curl_pp_disconnect(&pop3c->pp);
1273 
1274   /* Cleanup the SASL module */
1275   Curl_sasl_cleanup(conn, pop3c->sasl.authused);
1276 
1277   /* Cleanup our connection based variables */
1278   Curl_safefree(pop3c->apoptimestamp);
1279 
1280   return CURLE_OK;
1281 }
1282 
1283 /* Call this when the DO phase has completed */
pop3_dophase_done(struct Curl_easy * data,bool connected)1284 static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected)
1285 {
1286   (void)data;
1287   (void)connected;
1288 
1289   return CURLE_OK;
1290 }
1291 
1292 /* Called from multi.c while DOing */
pop3_doing(struct Curl_easy * data,bool * dophase_done)1293 static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done)
1294 {
1295   CURLcode result = pop3_multi_statemach(data, dophase_done);
1296 
1297   if(result)
1298     DEBUGF(infof(data, "DO phase failed"));
1299   else if(*dophase_done) {
1300     result = pop3_dophase_done(data, FALSE /* not connected */);
1301 
1302     DEBUGF(infof(data, "DO phase is complete"));
1303   }
1304 
1305   return result;
1306 }
1307 
1308 /***********************************************************************
1309  *
1310  * pop3_regular_transfer()
1311  *
1312  * The input argument is already checked for validity.
1313  *
1314  * Performs all commands done before a regular transfer between a local and a
1315  * remote host.
1316  */
pop3_regular_transfer(struct Curl_easy * data,bool * dophase_done)1317 static CURLcode pop3_regular_transfer(struct Curl_easy *data,
1318                                       bool *dophase_done)
1319 {
1320   CURLcode result = CURLE_OK;
1321   bool connected = FALSE;
1322 
1323   /* Make sure size is unknown at this point */
1324   data->req.size = -1;
1325 
1326   /* Set the progress data */
1327   Curl_pgrsSetUploadCounter(data, 0);
1328   Curl_pgrsSetDownloadCounter(data, 0);
1329   Curl_pgrsSetUploadSize(data, -1);
1330   Curl_pgrsSetDownloadSize(data, -1);
1331 
1332   /* Carry out the perform */
1333   result = pop3_perform(data, &connected, dophase_done);
1334 
1335   /* Perform post DO phase operations if necessary */
1336   if(!result && *dophase_done)
1337     result = pop3_dophase_done(data, connected);
1338 
1339   return result;
1340 }
1341 
pop3_setup_connection(struct Curl_easy * data,struct connectdata * conn)1342 static CURLcode pop3_setup_connection(struct Curl_easy *data,
1343                                       struct connectdata *conn)
1344 {
1345   /* Initialise the POP3 layer */
1346   CURLcode result = pop3_init(data);
1347   if(result)
1348     return result;
1349 
1350   /* Clear the TLS upgraded flag */
1351   conn->bits.tls_upgraded = FALSE;
1352 
1353   return CURLE_OK;
1354 }
1355 
1356 /***********************************************************************
1357  *
1358  * pop3_parse_url_options()
1359  *
1360  * Parse the URL login options.
1361  */
pop3_parse_url_options(struct connectdata * conn)1362 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1363 {
1364   CURLcode result = CURLE_OK;
1365   struct pop3_conn *pop3c = &conn->proto.pop3c;
1366   const char *ptr = conn->options;
1367 
1368   while(!result && ptr && *ptr) {
1369     const char *key = ptr;
1370     const char *value;
1371 
1372     while(*ptr && *ptr != '=')
1373       ptr++;
1374 
1375     value = ptr + 1;
1376 
1377     while(*ptr && *ptr != ';')
1378       ptr++;
1379 
1380     if(strncasecompare(key, "AUTH=", 5)) {
1381       result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
1382                                                value, ptr - value);
1383 
1384       if(result && strncasecompare(value, "+APOP", ptr - value)) {
1385         pop3c->preftype = POP3_TYPE_APOP;
1386         pop3c->sasl.prefmech = SASL_AUTH_NONE;
1387         result = CURLE_OK;
1388       }
1389     }
1390     else
1391       result = CURLE_URL_MALFORMAT;
1392 
1393     if(*ptr == ';')
1394       ptr++;
1395   }
1396 
1397   if(pop3c->preftype != POP3_TYPE_APOP)
1398     switch(pop3c->sasl.prefmech) {
1399     case SASL_AUTH_NONE:
1400       pop3c->preftype = POP3_TYPE_NONE;
1401       break;
1402     case SASL_AUTH_DEFAULT:
1403       pop3c->preftype = POP3_TYPE_ANY;
1404       break;
1405     default:
1406       pop3c->preftype = POP3_TYPE_SASL;
1407       break;
1408     }
1409 
1410   return result;
1411 }
1412 
1413 /***********************************************************************
1414  *
1415  * pop3_parse_url_path()
1416  *
1417  * Parse the URL path into separate path components.
1418  */
pop3_parse_url_path(struct Curl_easy * data)1419 static CURLcode pop3_parse_url_path(struct Curl_easy *data)
1420 {
1421   /* The POP3 struct is already initialised in pop3_connect() */
1422   struct POP3 *pop3 = data->req.p.pop3;
1423   const char *path = &data->state.up.path[1]; /* skip leading path */
1424 
1425   /* URL decode the path for the message ID */
1426   return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL);
1427 }
1428 
1429 /***********************************************************************
1430  *
1431  * pop3_parse_custom_request()
1432  *
1433  * Parse the custom request.
1434  */
pop3_parse_custom_request(struct Curl_easy * data)1435 static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
1436 {
1437   CURLcode result = CURLE_OK;
1438   struct POP3 *pop3 = data->req.p.pop3;
1439   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1440 
1441   /* URL decode the custom request */
1442   if(custom)
1443     result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL);
1444 
1445   return result;
1446 }
1447 
1448 /***********************************************************************
1449  *
1450  * Curl_pop3_write()
1451  *
1452  * This function scans the body after the end-of-body and writes everything
1453  * until the end is found.
1454  */
Curl_pop3_write(struct Curl_easy * data,const char * str,size_t nread)1455 CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread)
1456 {
1457   /* This code could be made into a special function in the handler struct */
1458   CURLcode result = CURLE_OK;
1459   struct SingleRequest *k = &data->req;
1460   struct connectdata *conn = data->conn;
1461   struct pop3_conn *pop3c = &conn->proto.pop3c;
1462   bool strip_dot = FALSE;
1463   size_t last = 0;
1464   size_t i;
1465 
1466   /* Search through the buffer looking for the end-of-body marker which is
1467      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1468      the eob so the server will have prefixed it with an extra dot which we
1469      need to strip out. Additionally the marker could of course be spread out
1470      over 5 different data chunks. */
1471   for(i = 0; i < nread; i++) {
1472     size_t prev = pop3c->eob;
1473 
1474     switch(str[i]) {
1475     case 0x0d:
1476       if(pop3c->eob == 0) {
1477         pop3c->eob++;
1478 
1479         if(i) {
1480           /* Write out the body part that didn't match */
1481           result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
1482                                      i - last);
1483 
1484           if(result)
1485             return result;
1486 
1487           last = i;
1488         }
1489       }
1490       else if(pop3c->eob == 3)
1491         pop3c->eob++;
1492       else
1493         /* If the character match wasn't at position 0 or 3 then restart the
1494            pattern matching */
1495         pop3c->eob = 1;
1496       break;
1497 
1498     case 0x0a:
1499       if(pop3c->eob == 1 || pop3c->eob == 4)
1500         pop3c->eob++;
1501       else
1502         /* If the character match wasn't at position 1 or 4 then start the
1503            search again */
1504         pop3c->eob = 0;
1505       break;
1506 
1507     case 0x2e:
1508       if(pop3c->eob == 2)
1509         pop3c->eob++;
1510       else if(pop3c->eob == 3) {
1511         /* We have an extra dot after the CRLF which we need to strip off */
1512         strip_dot = TRUE;
1513         pop3c->eob = 0;
1514       }
1515       else
1516         /* If the character match wasn't at position 2 then start the search
1517            again */
1518         pop3c->eob = 0;
1519       break;
1520 
1521     default:
1522       pop3c->eob = 0;
1523       break;
1524     }
1525 
1526     /* Did we have a partial match which has subsequently failed? */
1527     if(prev && prev >= pop3c->eob) {
1528       /* Strip can only be non-zero for the very first mismatch after CRLF
1529          and then both prev and strip are equal and nothing will be output
1530          below */
1531       while(prev && pop3c->strip) {
1532         prev--;
1533         pop3c->strip--;
1534       }
1535 
1536       if(prev) {
1537         /* If the partial match was the CRLF and dot then only write the CRLF
1538            as the server would have inserted the dot */
1539         if(strip_dot && prev - 1 > 0) {
1540           result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
1541                                      prev - 1);
1542         }
1543         else if(!strip_dot) {
1544           result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
1545                                      prev);
1546         }
1547         else {
1548           result = CURLE_OK;
1549         }
1550 
1551         if(result)
1552           return result;
1553 
1554         last = i;
1555         strip_dot = FALSE;
1556       }
1557     }
1558   }
1559 
1560   if(pop3c->eob == POP3_EOB_LEN) {
1561     /* We have a full match so the transfer is done, however we must transfer
1562     the CRLF at the start of the EOB as this is considered to be part of the
1563     message as per RFC-1939, sect. 3 */
1564     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1565 
1566     k->keepon &= ~KEEP_RECV;
1567     pop3c->eob = 0;
1568 
1569     return result;
1570   }
1571 
1572   if(pop3c->eob)
1573     /* While EOB is matching nothing should be output */
1574     return CURLE_OK;
1575 
1576   if(nread - last) {
1577     result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
1578                                nread - last);
1579   }
1580 
1581   return result;
1582 }
1583 
1584 #endif /* CURL_DISABLE_POP3 */
1585