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