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