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