build: fix circular header inclusion with other packages
[platform/upstream/curl.git] / lib / pop3.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, 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 http://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  * RFC2222 Simple Authentication and Security Layer (SASL)
25  * RFC2384 POP URL Scheme
26  * RFC2449 POP3 Extension Mechanism
27  * RFC2595 Using TLS with IMAP, POP3 and ACAP
28  * RFC2831 DIGEST-MD5 authentication
29  * RFC4616 PLAIN authentication
30  *
31  ***************************************************************************/
32
33 #include "curl_setup.h"
34
35 #ifndef CURL_DISABLE_POP3
36
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
42 #endif
43 #ifdef HAVE_UTSNAME_H
44 #include <sys/utsname.h>
45 #endif
46 #ifdef HAVE_NETDB_H
47 #include <netdb.h>
48 #endif
49 #ifdef __VMS
50 #include <in.h>
51 #include <inet.h>
52 #endif
53
54 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
55 #undef in_addr_t
56 #define in_addr_t unsigned long
57 #endif
58
59 #include <curl/curl.h>
60 #include "urldata.h"
61 #include "sendf.h"
62 #include "if2ip.h"
63 #include "hostip.h"
64 #include "progress.h"
65 #include "transfer.h"
66 #include "escape.h"
67 #include "http.h" /* for HTTP proxy tunnel stuff */
68 #include "socks.h"
69 #include "pop3.h"
70
71 #include "strtoofft.h"
72 #include "strequal.h"
73 #include "sslgen.h"
74 #include "connect.h"
75 #include "strerror.h"
76 #include "select.h"
77 #include "multiif.h"
78 #include "url.h"
79 #include "rawstr.h"
80 #include "curl_sasl.h"
81 #include "curl_md5.h"
82 #include "warnless.h"
83
84 #define _MPRINTF_REPLACE /* use our functions only */
85 #include <curl/mprintf.h>
86
87 #include "curl_memory.h"
88 /* The last #include file should be: */
89 #include "memdebug.h"
90
91 /* Local API functions */
92 static CURLcode pop3_parse_url_path(struct connectdata *conn);
93 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
94 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
95 static CURLcode pop3_do(struct connectdata *conn, bool *done);
96 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
97                           bool premature);
98 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
99 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
100 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
101 static int pop3_getsock(struct connectdata *conn,
102                         curl_socket_t *socks,
103                         int numsocks);
104 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
105 static CURLcode pop3_setup_connection(struct connectdata *conn);
106 static CURLcode pop3_state_upgrade_tls(struct connectdata *conn);
107
108 /*
109  * POP3 protocol handler.
110  */
111
112 const struct Curl_handler Curl_handler_pop3 = {
113   "POP3",                           /* scheme */
114   pop3_setup_connection,            /* setup_connection */
115   pop3_do,                          /* do_it */
116   pop3_done,                        /* done */
117   ZERO_NULL,                        /* do_more */
118   pop3_connect,                     /* connect_it */
119   pop3_multi_statemach,             /* connecting */
120   pop3_doing,                       /* doing */
121   pop3_getsock,                     /* proto_getsock */
122   pop3_getsock,                     /* doing_getsock */
123   ZERO_NULL,                        /* domore_getsock */
124   ZERO_NULL,                        /* perform_getsock */
125   pop3_disconnect,                  /* disconnect */
126   ZERO_NULL,                        /* readwrite */
127   PORT_POP3,                        /* defport */
128   CURLPROTO_POP3,                   /* protocol */
129   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
130 };
131
132 #ifdef USE_SSL
133 /*
134  * POP3S protocol handler.
135  */
136
137 const struct Curl_handler Curl_handler_pop3s = {
138   "POP3S",                          /* scheme */
139   pop3_setup_connection,            /* setup_connection */
140   pop3_do,                          /* do_it */
141   pop3_done,                        /* done */
142   ZERO_NULL,                        /* do_more */
143   pop3_connect,                     /* connect_it */
144   pop3_multi_statemach,             /* connecting */
145   pop3_doing,                       /* doing */
146   pop3_getsock,                     /* proto_getsock */
147   pop3_getsock,                     /* doing_getsock */
148   ZERO_NULL,                        /* domore_getsock */
149   ZERO_NULL,                        /* perform_getsock */
150   pop3_disconnect,                  /* disconnect */
151   ZERO_NULL,                        /* readwrite */
152   PORT_POP3S,                       /* defport */
153   CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */
154   PROTOPT_CLOSEACTION | PROTOPT_SSL
155   | PROTOPT_NOURLQUERY              /* flags */
156 };
157 #endif
158
159 #ifndef CURL_DISABLE_HTTP
160 /*
161  * HTTP-proxyed POP3 protocol handler.
162  */
163
164 static const struct Curl_handler Curl_handler_pop3_proxy = {
165   "POP3",                               /* scheme */
166   ZERO_NULL,                            /* setup_connection */
167   Curl_http,                            /* do_it */
168   Curl_http_done,                       /* done */
169   ZERO_NULL,                            /* do_more */
170   ZERO_NULL,                            /* connect_it */
171   ZERO_NULL,                            /* connecting */
172   ZERO_NULL,                            /* doing */
173   ZERO_NULL,                            /* proto_getsock */
174   ZERO_NULL,                            /* doing_getsock */
175   ZERO_NULL,                            /* domore_getsock */
176   ZERO_NULL,                            /* perform_getsock */
177   ZERO_NULL,                            /* disconnect */
178   ZERO_NULL,                            /* readwrite */
179   PORT_POP3,                            /* defport */
180   CURLPROTO_HTTP,                       /* protocol */
181   PROTOPT_NONE                          /* flags */
182 };
183
184 #ifdef USE_SSL
185 /*
186  * HTTP-proxyed POP3S protocol handler.
187  */
188
189 static const struct Curl_handler Curl_handler_pop3s_proxy = {
190   "POP3S",                              /* scheme */
191   ZERO_NULL,                            /* setup_connection */
192   Curl_http,                            /* do_it */
193   Curl_http_done,                       /* done */
194   ZERO_NULL,                            /* do_more */
195   ZERO_NULL,                            /* connect_it */
196   ZERO_NULL,                            /* connecting */
197   ZERO_NULL,                            /* doing */
198   ZERO_NULL,                            /* proto_getsock */
199   ZERO_NULL,                            /* doing_getsock */
200   ZERO_NULL,                            /* domore_getsock */
201   ZERO_NULL,                            /* perform_getsock */
202   ZERO_NULL,                            /* disconnect */
203   ZERO_NULL,                            /* readwrite */
204   PORT_POP3S,                           /* defport */
205   CURLPROTO_HTTP,                       /* protocol */
206   PROTOPT_NONE                          /* flags */
207 };
208 #endif
209 #endif
210
211 /* Function that checks for an ending pop3 status code at the start of the
212    given string, but also detects the APOP timestamp from the server greeting
213    as well as the supported authentication types and allowed SASL mechanisms
214    from the CAPA response. */
215 static int pop3_endofresp(struct pingpong *pp, int *resp)
216 {
217   char *line = pp->linestart_resp;
218   size_t len = strlen(pp->linestart_resp);
219   struct connectdata *conn = pp->conn;
220   struct pop3_conn *pop3c = &conn->proto.pop3c;
221   size_t wordlen;
222   size_t i;
223
224   /* Do we have an error response? */
225   if(len >= 4 && !memcmp("-ERR", line, 4)) {
226     *resp = '-';
227
228     return FALSE;
229   }
230
231   /* Are we processing servergreet responses? */
232   if(pop3c->state == POP3_SERVERGREET) {
233     /* Look for the APOP timestamp */
234     if(len >= 3 && line[len - 3] == '>') {
235       for(i = 0; i < len - 3; ++i) {
236         if(line[i] == '<') {
237           /* Calculate the length of the timestamp */
238           size_t timestamplen = len - 2 - i;
239
240           /* Allocate some memory for the timestamp */
241           pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
242
243           if(!pop3c->apoptimestamp)
244             break;
245
246           /* Copy the timestamp */
247           memcpy(pop3c->apoptimestamp, line + i, timestamplen);
248           pop3c->apoptimestamp[timestamplen] = '\0';
249           break;
250         }
251       }
252     }
253   }
254   /* Are we processing CAPA command responses? */
255   else if(pop3c->state == POP3_CAPA) {
256
257     /* Do we have the terminating character? */
258     if(len >= 1 && !memcmp(line, ".", 1)) {
259       *resp = '+';
260
261       return TRUE;
262     }
263
264     /* Does the server support clear text? */
265     if(len >= 4 && !memcmp(line, "USER", 4)) {
266       pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
267       return FALSE;
268     }
269
270     /* Does the server support APOP? */
271     if(len >= 4 && !memcmp(line, "APOP", 4)) {
272       pop3c->authtypes |= POP3_TYPE_APOP;
273       return FALSE;
274     }
275
276     /* Does the server support SASL? */
277     if(len < 4 || memcmp(line, "SASL", 4))
278       return FALSE;
279
280     pop3c->authtypes |= POP3_TYPE_SASL;
281
282     /* Advance past the SASL keyword */
283     line += 4;
284     len -= 4;
285
286     /* Loop through the data line */
287     for(;;) {
288       while(len &&
289             (*line == ' ' || *line == '\t' ||
290              *line == '\r' || *line == '\n')) {
291
292         if(*line == '\n')
293           return FALSE;
294
295         line++;
296         len--;
297       }
298
299       if(!len)
300         break;
301
302       /* Extract the word */
303       for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
304             line[wordlen] != '\t' && line[wordlen] != '\r' &&
305             line[wordlen] != '\n';)
306         wordlen++;
307
308       /* Test the word for a matching authentication mechanism */
309       if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
310         pop3c->authmechs |= SASL_MECH_LOGIN;
311       else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
312         pop3c->authmechs |= SASL_MECH_PLAIN;
313       else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
314         pop3c->authmechs |= SASL_MECH_CRAM_MD5;
315       else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
316         pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
317       else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
318         pop3c->authmechs |= SASL_MECH_GSSAPI;
319       else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
320         pop3c->authmechs |= SASL_MECH_EXTERNAL;
321       else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
322         pop3c->authmechs |= SASL_MECH_NTLM;
323
324       line += wordlen;
325       len -= wordlen;
326     }
327   }
328
329   if((len < 1 || memcmp("+", line, 1)) &&
330      (len < 3 || memcmp("+OK", line, 3)))
331   return FALSE; /* Nothing for us */
332
333   /* Otherwise it's a positive response */
334   *resp = '+';
335
336   return TRUE;
337 }
338
339 /* This is the ONLY way to change POP3 state! */
340 static void state(struct connectdata *conn, pop3state newstate)
341 {
342   struct pop3_conn *pop3c = &conn->proto.pop3c;
343 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
344   /* for debug purposes */
345   static const char * const names[] = {
346     "STOP",
347     "SERVERGREET",
348     "STARTTLS",
349     "UPGRADETLS",
350     "CAPA",
351     "AUTH_PLAIN",
352     "AUTH_LOGIN",
353     "AUTH_LOGIN_PASSWD",
354     "AUTH_CRAMMD5",
355     "AUTH_DIGESTMD5",
356     "AUTH_DIGESTMD5_RESP",
357     "AUTH_NTLM",
358     "AUTH_NTLM_TYPE2MSG",
359     "AUTH",
360     "APOP",
361     "USER",
362     "PASS",
363     "COMMAND",
364     "QUIT",
365     /* LAST */
366   };
367
368   if(pop3c->state != newstate)
369     infof(conn->data, "POP3 %p state change from %s to %s\n",
370           pop3c, names[pop3c->state], names[newstate]);
371 #endif
372
373   pop3c->state = newstate;
374 }
375
376 static CURLcode pop3_state_capa(struct connectdata *conn)
377 {
378   CURLcode result = CURLE_OK;
379   struct pop3_conn *pop3c = &conn->proto.pop3c;
380
381   pop3c->authmechs = 0;         /* No known authentication mechanisms yet */
382   pop3c->authused = 0;          /* Clear the authentication mechanism used */
383
384   /* Check we have a username and password to authenticate with and end the
385      connect phase if we don't */
386   if(!conn->bits.user_passwd) {
387     state(conn, POP3_STOP);
388
389     return result;
390   }
391
392   /* Send the CAPA command */
393   result = Curl_pp_sendf(&pop3c->pp, "CAPA");
394
395   if(result)
396     return result;
397
398   state(conn, POP3_CAPA);
399
400   return CURLE_OK;
401 }
402
403 static CURLcode pop3_state_user(struct connectdata *conn)
404 {
405   CURLcode result;
406   struct FTP *pop3 = conn->data->state.proto.pop3;
407
408   /* Send the USER command */
409   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
410                          pop3->user ? pop3->user : "");
411   if(result)
412     return result;
413
414   state(conn, POP3_USER);
415
416   return CURLE_OK;
417 }
418
419 #ifndef CURL_DISABLE_CRYPTO_AUTH
420 static CURLcode pop3_state_apop(struct connectdata *conn)
421 {
422   CURLcode result = CURLE_OK;
423   struct pop3_conn *pop3c = &conn->proto.pop3c;
424   size_t i;
425   MD5_context *ctxt;
426   unsigned char digest[MD5_DIGEST_LEN];
427   char secret[2 * MD5_DIGEST_LEN + 1];
428
429   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
430   if(!ctxt)
431     return CURLE_OUT_OF_MEMORY;
432
433   Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
434                   curlx_uztoui(strlen(pop3c->apoptimestamp)));
435
436   Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
437                   curlx_uztoui(strlen(conn->passwd)));
438
439   /* Finalise the digest */
440   Curl_MD5_final(ctxt, digest);
441
442   /* Convert the calculated 16 octet digest into a 32 byte hex string */
443   for(i = 0; i < MD5_DIGEST_LEN; i++)
444     snprintf(&secret[2 * i], 3, "%02x", digest[i]);
445
446   result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
447
448   if(!result)
449     state(conn, POP3_APOP);
450
451   return result;
452 }
453 #endif
454
455 static CURLcode pop3_authenticate(struct connectdata *conn)
456 {
457   CURLcode result = CURLE_OK;
458   struct pop3_conn *pop3c = &conn->proto.pop3c;
459   const char *mech = NULL;
460   pop3state authstate = POP3_STOP;
461
462   /* Check supported authentication mechanisms by decreasing order of
463      security */
464 #ifndef CURL_DISABLE_CRYPTO_AUTH
465   if(pop3c->authmechs & SASL_MECH_DIGEST_MD5) {
466     mech = "DIGEST-MD5";
467     authstate = POP3_AUTH_DIGESTMD5;
468     pop3c->authused = SASL_MECH_DIGEST_MD5;
469   }
470   else if(pop3c->authmechs & SASL_MECH_CRAM_MD5) {
471     mech = "CRAM-MD5";
472     authstate = POP3_AUTH_CRAMMD5;
473     pop3c->authused = SASL_MECH_CRAM_MD5;
474   }
475   else
476 #endif
477 #ifdef USE_NTLM
478   if(pop3c->authmechs & SASL_MECH_NTLM) {
479     mech = "NTLM";
480     authstate = POP3_AUTH_NTLM;
481     pop3c->authused = SASL_MECH_NTLM;
482   }
483   else
484 #endif
485   if(pop3c->authmechs & SASL_MECH_LOGIN) {
486     mech = "LOGIN";
487     authstate = POP3_AUTH_LOGIN;
488     pop3c->authused = SASL_MECH_LOGIN;
489   }
490   else if(pop3c->authmechs & SASL_MECH_PLAIN) {
491     mech = "PLAIN";
492     authstate = POP3_AUTH_PLAIN;
493     pop3c->authused = SASL_MECH_PLAIN;
494   }
495   else {
496     infof(conn->data, "No known SASL authentication mechanisms supported!\n");
497     result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
498   }
499
500   if(!result) {
501     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
502
503     if(!result)
504       state(conn, authstate);
505   }
506
507   return result;
508 }
509
510 /* For the POP3 "protocol connect" and "doing" phases only */
511 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
512                         int numsocks)
513 {
514   return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
515 }
516
517 #ifdef USE_SSL
518 static void pop3_to_pop3s(struct connectdata *conn)
519 {
520   conn->handler = &Curl_handler_pop3s;
521 }
522 #else
523 #define pop3_to_pop3s(x) Curl_nop_stmt
524 #endif
525
526 /* For the initial server greeting */
527 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
528                                             int pop3code,
529                                             pop3state instate)
530 {
531   CURLcode result = CURLE_OK;
532   struct SessionHandle *data = conn->data;
533   struct pop3_conn *pop3c = &conn->proto.pop3c;
534
535   (void)instate; /* no use for this yet */
536
537   if(pop3code != '+') {
538     failf(data, "Got unexpected pop3-server response");
539     return CURLE_FTP_WEIRD_SERVER_REPLY;
540   }
541
542   if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
543     /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
544        to TLS connection now */
545     result = Curl_pp_sendf(&pop3c->pp, "STLS");
546     state(conn, POP3_STARTTLS);
547   }
548   else
549     result = pop3_state_capa(conn);
550
551   return result;
552 }
553
554 /* For STARTTLS responses */
555 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
556                                          int pop3code,
557                                          pop3state instate)
558 {
559   CURLcode result = CURLE_OK;
560   struct SessionHandle *data = conn->data;
561
562   (void)instate; /* no use for this yet */
563
564   if(pop3code != '+') {
565     if(data->set.use_ssl != CURLUSESSL_TRY) {
566       failf(data, "STARTTLS denied. %c", pop3code);
567       result = CURLE_USE_SSL_FAILED;
568     }
569     else
570       result = pop3_state_capa(conn);
571   }
572   else {
573     if(data->state.used_interface == Curl_if_multi) {
574       state(conn, POP3_UPGRADETLS);
575       result = pop3_state_upgrade_tls(conn);
576     }
577     else {
578       result = Curl_ssl_connect(conn, FIRSTSOCKET);
579       if(CURLE_OK == result) {
580         pop3_to_pop3s(conn);
581         result = pop3_state_capa(conn);
582       }
583     }
584   }
585
586   return result;
587 }
588
589 static CURLcode pop3_state_upgrade_tls(struct connectdata *conn)
590 {
591   struct pop3_conn *pop3c = &conn->proto.pop3c;
592   CURLcode result;
593
594   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
595
596   if(pop3c->ssldone) {
597     pop3_to_pop3s(conn);
598     result = pop3_state_capa(conn);
599   }
600
601   return result;
602 }
603
604 /* For CAPA responses */
605 static CURLcode pop3_state_capa_resp(struct connectdata *conn,
606                                      int pop3code,
607                                      pop3state instate)
608 {
609   CURLcode result = CURLE_OK;
610
611   (void)instate; /* no use for this yet */
612
613   if(pop3code == '+' && conn->proto.pop3c.authtypes) {
614     /* Check supported authentication types by decreasing order of security */
615     if(conn->proto.pop3c.authtypes & POP3_TYPE_SASL)
616       result = pop3_authenticate(conn);
617 #ifndef CURL_DISABLE_CRYPTO_AUTH
618     else if(conn->proto.pop3c.authtypes & POP3_TYPE_APOP)
619       result = pop3_state_apop(conn);
620 #endif
621     else if(conn->proto.pop3c.authtypes & POP3_TYPE_CLEARTEXT)
622       result = pop3_state_user(conn);
623     else {
624       infof(conn->data, "No known authentication types supported!\n");
625       result = CURLE_LOGIN_DENIED; /* Other types not supported */
626     }
627   }
628   else
629     result = pop3_state_user(conn);
630
631   return result;
632 }
633
634 /* For AUTH PLAIN responses */
635 static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
636                                            int pop3code,
637                                            pop3state instate)
638 {
639   CURLcode result = CURLE_OK;
640   struct SessionHandle *data = conn->data;
641   size_t len = 0;
642   char *plainauth = NULL;
643
644   (void)instate; /* no use for this yet */
645
646   if(pop3code != '+') {
647     failf(data, "Access denied. %c", pop3code);
648     result = CURLE_LOGIN_DENIED;
649   }
650   else {
651     /* Create the authorisation message */
652     result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
653                                             &plainauth, &len);
654
655     /* Send the message */
656     if(!result) {
657       if(plainauth) {
658         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
659
660         if(!result)
661           state(conn, POP3_AUTH);
662       }
663
664       Curl_safefree(plainauth);
665     }
666   }
667
668   return result;
669 }
670
671 /* For AUTH LOGIN responses */
672 static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
673                                            int pop3code,
674                                            pop3state instate)
675 {
676   CURLcode result = CURLE_OK;
677   struct SessionHandle *data = conn->data;
678   size_t len = 0;
679   char *authuser = NULL;
680
681   (void)instate; /* no use for this yet */
682
683   if(pop3code != '+') {
684     failf(data, "Access denied: %d", pop3code);
685     result = CURLE_LOGIN_DENIED;
686   }
687   else {
688     /* Create the user message */
689     result = Curl_sasl_create_login_message(data, conn->user,
690                                             &authuser, &len);
691
692     /* Send the user */
693     if(!result) {
694       if(authuser) {
695         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
696
697         if(!result)
698           state(conn, POP3_AUTH_LOGIN_PASSWD);
699       }
700
701       Curl_safefree(authuser);
702     }
703   }
704
705   return result;
706 }
707
708 /* For AUTH LOGIN user entry responses */
709 static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
710                                                     int pop3code,
711                                                     pop3state instate)
712 {
713   CURLcode result = CURLE_OK;
714   struct SessionHandle *data = conn->data;
715   size_t len = 0;
716   char *authpasswd = NULL;
717
718   (void)instate; /* no use for this yet */
719
720   if(pop3code != '+') {
721     failf(data, "Access denied: %d", pop3code);
722     result = CURLE_LOGIN_DENIED;
723   }
724   else {
725     /* Create the password message */
726     result = Curl_sasl_create_login_message(data, conn->passwd,
727                                             &authpasswd, &len);
728
729     /* Send the password */
730     if(!result) {
731       if(authpasswd) {
732         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
733
734         if(!result)
735           state(conn, POP3_AUTH);
736       }
737
738       Curl_safefree(authpasswd);
739     }
740   }
741
742   return result;
743 }
744
745 #ifndef CURL_DISABLE_CRYPTO_AUTH
746 /* For AUTH CRAM-MD5 responses */
747 static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
748                                           int pop3code,
749                                           pop3state instate)
750 {
751   CURLcode result = CURLE_OK;
752   struct SessionHandle *data = conn->data;
753   char *chlg64 = data->state.buffer;
754   size_t len = 0;
755   char *rplyb64 = NULL;
756
757   (void)instate; /* no use for this yet */
758
759   if(pop3code != '+') {
760     failf(data, "Access denied: %d", pop3code);
761     return CURLE_LOGIN_DENIED;
762   }
763
764   /* Get the challenge */
765   for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
766     ;
767
768   /* Terminate the challenge */
769   if(*chlg64 != '=') {
770     for(len = strlen(chlg64); len--;)
771       if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
772          chlg64[len] != '\t')
773         break;
774
775     if(++len) {
776       chlg64[len] = '\0';
777     }
778   }
779
780   /* Create the response message */
781   result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
782                                              conn->passwd, &rplyb64, &len);
783
784   /* Send the response */
785   if(!result) {
786     if(rplyb64) {
787       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
788
789       if(!result)
790         state(conn, POP3_AUTH);
791     }
792
793     Curl_safefree(rplyb64);
794   }
795
796   return result;
797 }
798
799 /* For AUTH DIGEST-MD5 challenge responses */
800 static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
801                                             int pop3code,
802                                             pop3state instate)
803 {
804   CURLcode result = CURLE_OK;
805   struct SessionHandle *data = conn->data;
806   char *chlg64 = data->state.buffer;
807   size_t len = 0;
808   char *rplyb64 = NULL;
809
810   (void)instate; /* no use for this yet */
811
812   if(pop3code != '+') {
813     failf(data, "Access denied: %d", pop3code);
814     return CURLE_LOGIN_DENIED;
815   }
816
817   /* Get the challenge */
818   for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
819     ;
820
821   /* Create the response message */
822   result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
823                                                conn->passwd, "pop",
824                                                &rplyb64, &len);
825
826   /* Send the response */
827   if(!result) {
828     if(rplyb64) {
829       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
830
831       if(!result)
832         state(conn, POP3_AUTH_DIGESTMD5_RESP);
833     }
834
835     Curl_safefree(rplyb64);
836   }
837
838   return result;
839 }
840
841 /* For AUTH DIGEST-MD5 challenge-response responses */
842 static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
843                                                  int pop3code,
844                                                  pop3state instate)
845 {
846   CURLcode result = CURLE_OK;
847   struct SessionHandle *data = conn->data;
848
849   (void)instate; /* no use for this yet */
850
851   if(pop3code != '+') {
852     failf(data, "Authentication failed: %d", pop3code);
853     result = CURLE_LOGIN_DENIED;
854   }
855   else {
856     /* Send an empty response */
857     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "");
858
859     if(!result)
860       state(conn, POP3_AUTH);
861   }
862
863   return result;
864 }
865 #endif
866
867 #ifdef USE_NTLM
868 /* For AUTH NTLM responses */
869 static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
870                                           int pop3code,
871                                           pop3state instate)
872 {
873   CURLcode result = CURLE_OK;
874   struct SessionHandle *data = conn->data;
875   size_t len = 0;
876   char *type1msg = NULL;
877
878   (void)instate; /* no use for this yet */
879
880   if(pop3code != '+') {
881     failf(data, "Access denied: %d", pop3code);
882     result = CURLE_LOGIN_DENIED;
883   }
884   else {
885     /* Create the type-1 message */
886     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
887                                                  &conn->ntlm,
888                                                  &type1msg, &len);
889
890     /* Send the message */
891     if(!result) {
892       if(type1msg) {
893         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
894
895         if(!result)
896           state(conn, POP3_AUTH_NTLM_TYPE2MSG);
897       }
898
899       Curl_safefree(type1msg);
900     }
901   }
902
903   return result;
904 }
905
906 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
907 static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
908                                                    int pop3code,
909                                                    pop3state instate)
910 {
911   CURLcode result = CURLE_OK;
912   struct SessionHandle *data = conn->data;
913   size_t len = 0;
914   char *type3msg = NULL;
915
916   (void)instate; /* no use for this yet */
917
918   if(pop3code != '+') {
919     failf(data, "Access denied: %d", pop3code);
920     result = CURLE_LOGIN_DENIED;
921   }
922   else {
923     /* Create the type-3 message */
924     result = Curl_sasl_create_ntlm_type3_message(data,
925                                                  data->state.buffer + 2,
926                                                  conn->user, conn->passwd,
927                                                  &conn->ntlm,
928                                                  &type3msg, &len);
929
930     /* Send the message */
931     if(!result) {
932       if(type3msg) {
933         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
934
935         if(!result)
936           state(conn, POP3_AUTH);
937       }
938
939       Curl_safefree(type3msg);
940     }
941   }
942
943   return result;
944 }
945 #endif
946
947 /* For final responses to the AUTH sequence */
948 static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
949                                            int pop3code,
950                                            pop3state instate)
951 {
952   CURLcode result = CURLE_OK;
953   struct SessionHandle *data = conn->data;
954
955   (void)instate; /* no use for this yet */
956
957   if(pop3code != '+') {
958     failf(data, "Authentication failed: %d", pop3code);
959     result = CURLE_LOGIN_DENIED;
960   }
961
962   /* End of connect phase */
963   state(conn, POP3_STOP);
964
965   return result;
966 }
967
968 static CURLcode pop3_state_apop_resp(struct connectdata *conn,
969                                      int pop3code,
970                                      pop3state instate)
971 {
972   CURLcode result = CURLE_OK;
973   struct SessionHandle *data = conn->data;
974
975   (void)instate; /* no use for this yet */
976
977   if(pop3code != '+') {
978     failf(data, "Authentication failed: %d", pop3code);
979     result = CURLE_LOGIN_DENIED;
980   }
981
982   /* End of connect phase */
983   state(conn, POP3_STOP);
984
985   return result;
986 }
987
988 /* For USER responses */
989 static CURLcode pop3_state_user_resp(struct connectdata *conn,
990                                      int pop3code,
991                                      pop3state instate)
992 {
993   CURLcode result = CURLE_OK;
994   struct SessionHandle *data = conn->data;
995   struct FTP *pop3 = data->state.proto.pop3;
996
997   (void)instate; /* no use for this yet */
998
999   if(pop3code != '+') {
1000     failf(data, "Access denied. %c", pop3code);
1001     result = CURLE_LOGIN_DENIED;
1002   }
1003   else
1004     /* Send the PASS command */
1005     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
1006                            pop3->passwd ? pop3->passwd : "");
1007   if(result)
1008     return result;
1009
1010   state(conn, POP3_PASS);
1011
1012   return result;
1013 }
1014
1015 /* For PASS responses */
1016 static CURLcode pop3_state_pass_resp(struct connectdata *conn,
1017                                      int pop3code,
1018                                      pop3state instate)
1019 {
1020   CURLcode result = CURLE_OK;
1021   struct SessionHandle *data = conn->data;
1022
1023   (void)instate; /* no use for this yet */
1024
1025   if(pop3code != '+') {
1026     failf(data, "Access denied. %c", pop3code);
1027     result = CURLE_LOGIN_DENIED;
1028   }
1029
1030   /* End of connect phase */
1031   state(conn, POP3_STOP);
1032
1033   return result;
1034 }
1035
1036 /* Start the DO phase for the command */
1037 static CURLcode pop3_command(struct connectdata *conn)
1038 {
1039   CURLcode result = CURLE_OK;
1040   struct pop3_conn *pop3c = &conn->proto.pop3c;
1041   const char *command = NULL;
1042
1043   /* Calculate the default command */
1044   if(pop3c->mailbox[0] == '\0' || conn->data->set.ftp_list_only) {
1045     command = "LIST";
1046
1047     if(pop3c->mailbox[0] != '\0') {
1048       /* Message specific LIST so skip the BODY transfer */
1049       struct FTP *pop3 = conn->data->state.proto.pop3;
1050       pop3->transfer = FTPTRANSFER_INFO;
1051     }
1052   }
1053   else
1054     command = "RETR";
1055
1056   /* Send the command */
1057   if(pop3c->mailbox[0] != '\0')
1058     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
1059                            (pop3c->custom && pop3c->custom[0] != '\0' ?
1060                             pop3c->custom : command), pop3c->mailbox);
1061   else
1062     result = Curl_pp_sendf(&conn->proto.pop3c.pp,
1063                            (pop3c->custom && pop3c->custom[0] != '\0' ?
1064                             pop3c->custom : command));
1065
1066   if(result)
1067     return result;
1068
1069   state(conn, POP3_COMMAND);
1070
1071   return result;
1072 }
1073
1074 /* For command responses */
1075 static CURLcode pop3_state_command_resp(struct connectdata *conn,
1076                                         int pop3code,
1077                                         pop3state instate)
1078 {
1079   CURLcode result = CURLE_OK;
1080   struct SessionHandle *data = conn->data;
1081   struct FTP *pop3 = data->state.proto.pop3;
1082   struct pop3_conn *pop3c = &conn->proto.pop3c;
1083   struct pingpong *pp = &pop3c->pp;
1084
1085   (void)instate; /* no use for this yet */
1086
1087   if(pop3code != '+') {
1088     state(conn, POP3_STOP);
1089     return CURLE_RECV_ERROR;
1090   }
1091
1092   /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1093      EOB string so count this is two matching bytes. This is necessary to make
1094      the code detect the EOB if the only data than comes now is %2e CR LF like
1095      when there is no body to return. */
1096   pop3c->eob = 2;
1097
1098   /* But since this initial CR LF pair is not part of the actual body, we set
1099      the strip counter here so that these bytes won't be delivered. */
1100   pop3c->strip = 2;
1101
1102   /* POP3 download */
1103   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp,
1104                       -1, NULL); /* no upload here */
1105
1106   if(pp->cache) {
1107     /* The header "cache" contains a bunch of data that is actually body
1108        content so send it as such. Note that there may even be additional
1109        "headers" after the body */
1110
1111     if(!data->set.opt_no_body) {
1112       result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
1113       if(result)
1114         return result;
1115     }
1116
1117     /* Free the cache */
1118     Curl_safefree(pp->cache);
1119
1120     /* Reset the cache size */
1121     pp->cache_size = 0;
1122   }
1123
1124   /* End of do phase */
1125   state(conn, POP3_STOP);
1126
1127   return result;
1128 }
1129
1130 static CURLcode pop3_statemach_act(struct connectdata *conn)
1131 {
1132   CURLcode result;
1133   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1134   int pop3code;
1135   struct pop3_conn *pop3c = &conn->proto.pop3c;
1136   struct pingpong *pp = &pop3c->pp;
1137   size_t nread = 0;
1138
1139   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1140   if(pop3c->state == POP3_UPGRADETLS)
1141     return pop3_state_upgrade_tls(conn);
1142
1143   /* Flush any data that needs to be sent */
1144   if(pp->sendleft)
1145     return Curl_pp_flushsend(pp);
1146
1147   /* Read the response from the server */
1148   result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1149   if(result)
1150     return result;
1151
1152   if(pop3code) {
1153     /* We have now received a full POP3 server response */
1154     switch(pop3c->state) {
1155     case POP3_SERVERGREET:
1156       result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1157       break;
1158
1159     case POP3_STARTTLS:
1160       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1161       break;
1162
1163     case POP3_CAPA:
1164       result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1165       break;
1166
1167     case POP3_AUTH_PLAIN:
1168       result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
1169       break;
1170
1171     case POP3_AUTH_LOGIN:
1172       result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
1173       break;
1174
1175     case POP3_AUTH_LOGIN_PASSWD:
1176       result = pop3_state_auth_login_password_resp(conn, pop3code,
1177                                                    pop3c->state);
1178       break;
1179
1180 #ifndef CURL_DISABLE_CRYPTO_AUTH
1181     case POP3_AUTH_CRAMMD5:
1182       result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
1183       break;
1184
1185     case POP3_AUTH_DIGESTMD5:
1186       result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
1187       break;
1188
1189     case POP3_AUTH_DIGESTMD5_RESP:
1190       result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
1191       break;
1192 #endif
1193
1194 #ifdef USE_NTLM
1195     case POP3_AUTH_NTLM:
1196       result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
1197       break;
1198
1199     case POP3_AUTH_NTLM_TYPE2MSG:
1200       result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
1201                                                   pop3c->state);
1202       break;
1203 #endif
1204
1205     case POP3_AUTH:
1206       result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
1207       break;
1208
1209     case POP3_APOP:
1210       result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1211       break;
1212
1213     case POP3_USER:
1214       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1215       break;
1216
1217     case POP3_PASS:
1218       result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1219       break;
1220
1221     case POP3_COMMAND:
1222       result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1223       break;
1224
1225     case POP3_QUIT:
1226       /* fallthrough, just stop! */
1227     default:
1228       /* internal error */
1229       state(conn, POP3_STOP);
1230       break;
1231     }
1232   }
1233
1234   return result;
1235 }
1236
1237 /* Called repeatedly until done from multi.c */
1238 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1239 {
1240   struct pop3_conn *pop3c = &conn->proto.pop3c;
1241   CURLcode result = Curl_pp_multi_statemach(&pop3c->pp);
1242
1243   *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1244
1245   return result;
1246 }
1247
1248 static CURLcode pop3_easy_statemach(struct connectdata *conn)
1249 {
1250   struct pop3_conn *pop3c = &conn->proto.pop3c;
1251   struct pingpong *pp = &pop3c->pp;
1252   CURLcode result = CURLE_OK;
1253
1254   while(pop3c->state != POP3_STOP) {
1255     result = Curl_pp_easy_statemach(pp);
1256     if(result)
1257       break;
1258   }
1259
1260   return result;
1261 }
1262
1263 /* Allocate and initialize the POP3 struct for the current SessionHandle if
1264    required */
1265 static CURLcode pop3_init(struct connectdata *conn)
1266 {
1267   struct SessionHandle *data = conn->data;
1268   struct FTP *pop3 = data->state.proto.pop3;
1269
1270   if(!pop3) {
1271     pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1);
1272     if(!pop3)
1273       return CURLE_OUT_OF_MEMORY;
1274   }
1275
1276   /* Get some initial data into the pop3 struct */
1277   pop3->bytecountp = &data->req.bytecount;
1278
1279   /* No need to duplicate user+password, the connectdata struct won't change
1280      during a session, but we re-init them here since on subsequent inits
1281      since the conn struct may have changed or been replaced.
1282   */
1283   pop3->user = conn->user;
1284   pop3->passwd = conn->passwd;
1285
1286   return CURLE_OK;
1287 }
1288
1289 /***********************************************************************
1290  *
1291  * pop3_connect()
1292  *
1293  * This function should do everything that is to be considered a part of the
1294  * connection phase.
1295  *
1296  * The variable 'done' points to will be TRUE if the protocol-layer connect
1297  * phase is done when this function returns, or FALSE is not. When called as
1298  * a part of the easy interface, it will always be TRUE.
1299  */
1300 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1301 {
1302   CURLcode result;
1303   struct pop3_conn *pop3c = &conn->proto.pop3c;
1304   struct SessionHandle *data = conn->data;
1305   struct pingpong *pp = &pop3c->pp;
1306
1307   *done = FALSE; /* default to not done yet */
1308
1309   /* If there already is a protocol-specific struct allocated for this
1310      sessionhandle, deal with it */
1311   Curl_reset_reqproto(conn);
1312
1313   result = pop3_init(conn);
1314   if(CURLE_OK != result)
1315     return result;
1316
1317   /* We always support persistent connections on pop3 */
1318   conn->bits.close = FALSE;
1319
1320   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1321   pp->statemach_act = pop3_statemach_act;
1322   pp->endofresp = pop3_endofresp;
1323   pp->conn = conn;
1324
1325   if(conn->handler->flags & PROTOPT_SSL) {
1326     /* POP3S is simply pop3 with SSL for the control channel */
1327     /* so perform the SSL initialization for this socket */
1328     result = Curl_ssl_connect(conn, FIRSTSOCKET);
1329     if(result)
1330       return result;
1331   }
1332
1333   /* Initialise the response reader stuff */
1334   Curl_pp_init(pp);
1335
1336   /* Start off waiting for the server greeting response */
1337   state(conn, POP3_SERVERGREET);
1338
1339   if(data->state.used_interface == Curl_if_multi)
1340     result = pop3_multi_statemach(conn, done);
1341   else {
1342     result = pop3_easy_statemach(conn);
1343     if(!result)
1344       *done = TRUE;
1345   }
1346
1347   return result;
1348 }
1349
1350 /***********************************************************************
1351  *
1352  * pop3_done()
1353  *
1354  * The DONE function. This does what needs to be done after a single DO has
1355  * performed.
1356  *
1357  * Input argument is already checked for validity.
1358  */
1359 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1360                           bool premature)
1361 {
1362   struct SessionHandle *data = conn->data;
1363   struct FTP *pop3 = data->state.proto.pop3;
1364   struct pop3_conn *pop3c = &conn->proto.pop3c;
1365   CURLcode result = CURLE_OK;
1366
1367   (void)premature;
1368
1369   if(!pop3)
1370     /* When the easy handle is removed from the multi while libcurl is still
1371      * trying to resolve the host name, it seems that the pop3 struct is not
1372      * yet initialized, but the removal action calls Curl_done() which calls
1373      * this function. So we simply return success if no pop3 pointer is set.
1374      */
1375     return CURLE_OK;
1376
1377   if(status) {
1378     conn->bits.close = TRUE; /* marked for closure */
1379     result = status;         /* use the already set error code */
1380   }
1381
1382   /* Cleanup our do based variables */
1383   Curl_safefree(pop3c->mailbox);
1384   Curl_safefree(pop3c->custom);
1385
1386   /* Clear the transfer mode for the next connection */
1387   pop3->transfer = FTPTRANSFER_BODY;
1388
1389   return result;
1390 }
1391
1392 /***********************************************************************
1393  *
1394  * pop3_perform()
1395  *
1396  * This is the actual DO function for POP3. Get a file/directory according to
1397  * the options previously setup.
1398  */
1399 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1400                              bool *dophase_done)
1401 {
1402   /* This is POP3 and no proxy */
1403   CURLcode result = CURLE_OK;
1404
1405   DEBUGF(infof(conn->data, "DO phase starts\n"));
1406
1407   if(conn->data->set.opt_no_body) {
1408     /* Requested no body means no transfer */
1409     struct FTP *pop3 = conn->data->state.proto.pop3;
1410     pop3->transfer = FTPTRANSFER_INFO;
1411   }
1412
1413   *dophase_done = FALSE; /* not done yet */
1414
1415   /* Start the first command in the DO phase */
1416   result = pop3_command(conn);
1417   if(result)
1418     return result;
1419
1420   /* Run the state-machine */
1421   if(conn->data->state.used_interface == Curl_if_multi)
1422     result = pop3_multi_statemach(conn, dophase_done);
1423   else {
1424     result = pop3_easy_statemach(conn);
1425     *dophase_done = TRUE; /* with the easy interface we are done here */
1426   }
1427   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1428
1429   if(*dophase_done)
1430     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1431
1432   return result;
1433 }
1434
1435 /***********************************************************************
1436  *
1437  * pop3_do()
1438  *
1439  * This function is registered as 'curl_do' function. It decodes the path
1440  * parts etc as a wrapper to the actual DO function (pop3_perform).
1441  *
1442  * The input argument is already checked for validity.
1443  */
1444 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1445 {
1446   CURLcode retcode = CURLE_OK;
1447
1448   *done = FALSE; /* default to false */
1449
1450   /*
1451     Since connections can be re-used between SessionHandles, this might be a
1452     connection already existing but on a fresh SessionHandle struct so we must
1453     make sure we have a good 'struct POP3' to play with. For new connections,
1454     the struct POP3 is allocated and setup in the pop3_connect() function.
1455   */
1456   Curl_reset_reqproto(conn);
1457   retcode = pop3_init(conn);
1458   if(retcode)
1459     return retcode;
1460
1461   /* Parse the URL path */
1462   retcode = pop3_parse_url_path(conn);
1463   if(retcode)
1464     return retcode;
1465
1466   /* Parse the custom request */
1467   retcode = pop3_parse_custom_request(conn);
1468   if(retcode)
1469     return retcode;
1470
1471   retcode = pop3_regular_transfer(conn, done);
1472
1473   return retcode;
1474 }
1475
1476 /***********************************************************************
1477  *
1478  * pop3_quit()
1479  *
1480  * This should be called before calling sclose().  We should then wait for the
1481  * response from the server before returning. The calling code should then try
1482  * to close the connection.
1483  */
1484 static CURLcode pop3_quit(struct connectdata *conn)
1485 {
1486   CURLcode result = CURLE_OK;
1487
1488   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL);
1489   if(result)
1490     return result;
1491
1492   state(conn, POP3_QUIT);
1493
1494   result = pop3_easy_statemach(conn);
1495
1496   return result;
1497 }
1498
1499 /***********************************************************************
1500  *
1501  * pop3_disconnect()
1502  *
1503  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1504  * resources. BLOCKING.
1505  */
1506 static CURLcode pop3_disconnect(struct connectdata *conn,
1507                                 bool dead_connection)
1508 {
1509   struct pop3_conn *pop3c = &conn->proto.pop3c;
1510
1511   /* We cannot send quit unconditionally. If this connection is stale or
1512      bad in any way, sending quit and waiting around here will make the
1513      disconnect wait in vain and cause more problems than we need to */
1514
1515   /* The POP3 session may or may not have been allocated/setup at this
1516      point! */
1517   if(!dead_connection && pop3c->pp.conn)
1518     (void)pop3_quit(conn); /* ignore errors on the LOGOUT */
1519
1520   /* Disconnect from the server */
1521   Curl_pp_disconnect(&pop3c->pp);
1522
1523   /* Cleanup the SASL module */
1524   Curl_sasl_cleanup(conn, pop3c->authused);
1525
1526   /* Cleanup our connection based variables */
1527   Curl_safefree(pop3c->apoptimestamp);
1528
1529   return CURLE_OK;
1530 }
1531
1532 /***********************************************************************
1533  *
1534  * pop3_parse_url_path()
1535  *
1536  * Parse the URL path into separate path components.
1537  */
1538 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1539 {
1540   /* The POP3 struct is already initialised in pop3_connect() */
1541   struct pop3_conn *pop3c = &conn->proto.pop3c;
1542   struct SessionHandle *data = conn->data;
1543   const char *path = data->state.path;
1544
1545   /* URL decode the path and use this mailbox */
1546   return Curl_urldecode(data, path, 0, &pop3c->mailbox, NULL, TRUE);
1547 }
1548
1549 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1550 {
1551   CURLcode result = CURLE_OK;
1552   struct pop3_conn *pop3c = &conn->proto.pop3c;
1553   struct SessionHandle *data = conn->data;
1554   const char *custom = conn->data->set.str[STRING_CUSTOMREQUEST];
1555
1556   /* URL decode the custom request */
1557   if(custom)
1558     result = Curl_urldecode(data, custom, 0, &pop3c->custom, NULL, TRUE);
1559
1560   return result;
1561 }
1562
1563 /* Call this when the DO phase has completed */
1564 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1565 {
1566   struct FTP *pop3 = conn->data->state.proto.pop3;
1567
1568   (void)connected;
1569
1570   if(pop3->transfer != FTPTRANSFER_BODY)
1571     /* no data to transfer */
1572     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1573
1574   return CURLE_OK;
1575 }
1576
1577 /* Called from multi.c while DOing */
1578 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1579 {
1580   CURLcode result = pop3_multi_statemach(conn, dophase_done);
1581
1582   if(result)
1583     DEBUGF(infof(conn->data, "DO phase failed\n"));
1584   else {
1585     if(*dophase_done) {
1586       result = pop3_dophase_done(conn, FALSE /* not connected */);
1587
1588       DEBUGF(infof(conn->data, "DO phase is complete\n"));
1589     }
1590   }
1591
1592   return result;
1593 }
1594
1595 /***********************************************************************
1596  *
1597  * pop3_regular_transfer()
1598  *
1599  * The input argument is already checked for validity.
1600  *
1601  * Performs all commands done before a regular transfer between a local and a
1602  * remote host.
1603  */
1604 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1605                                       bool *dophase_done)
1606 {
1607   CURLcode result = CURLE_OK;
1608   bool connected = FALSE;
1609   struct SessionHandle *data = conn->data;
1610
1611   /* Make sure size is unknown at this point */
1612   data->req.size = -1;
1613
1614   Curl_pgrsSetUploadCounter(data, 0);
1615   Curl_pgrsSetDownloadCounter(data, 0);
1616   Curl_pgrsSetUploadSize(data, 0);
1617   Curl_pgrsSetDownloadSize(data, 0);
1618
1619   result = pop3_perform(conn, &connected, dophase_done);
1620
1621   if(CURLE_OK == result) {
1622     if(!*dophase_done)
1623       /* The DO phase has not completed yet */
1624       return CURLE_OK;
1625
1626     result = pop3_dophase_done(conn, connected);
1627     if(result)
1628       return result;
1629   }
1630
1631   return result;
1632 }
1633
1634 static CURLcode pop3_setup_connection(struct connectdata * conn)
1635 {
1636   struct SessionHandle *data = conn->data;
1637
1638   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1639     /* Unless we have asked to tunnel pop3 operations through the proxy, we
1640        switch and use HTTP operations only */
1641 #ifndef CURL_DISABLE_HTTP
1642     if(conn->handler == &Curl_handler_pop3)
1643       conn->handler = &Curl_handler_pop3_proxy;
1644     else {
1645 #ifdef USE_SSL
1646       conn->handler = &Curl_handler_pop3s_proxy;
1647 #else
1648       failf(data, "POP3S not supported!");
1649       return CURLE_UNSUPPORTED_PROTOCOL;
1650 #endif
1651     }
1652
1653     /* We explicitly mark this connection as persistent here as we're doing
1654        POP3 over HTTP and thus we accidentally avoid setting this value
1655        otherwise */
1656     conn->bits.close = FALSE;
1657 #else
1658     failf(data, "POP3 over http proxy requires HTTP support built-in!");
1659     return CURLE_UNSUPPORTED_PROTOCOL;
1660 #endif
1661   }
1662
1663   data->state.path++;   /* don't include the initial slash */
1664
1665   return CURLE_OK;
1666 }
1667
1668 /* This function scans the body after the end-of-body and writes everything
1669    until the end is found */
1670 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1671 {
1672   /* This code could be made into a special function in the handler struct */
1673   CURLcode result = CURLE_OK;
1674   struct SessionHandle *data = conn->data;
1675   struct SingleRequest *k = &data->req;
1676
1677   struct pop3_conn *pop3c = &conn->proto.pop3c;
1678   bool strip_dot = FALSE;
1679   size_t last = 0;
1680   size_t i;
1681
1682   /* Search through the buffer looking for the end-of-body marker which is
1683      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1684      the eob so the server will have prefixed it with an extra dot which we
1685      need to strip out. Additionally the marker could of course be spread out
1686      over 5 different data chunks */
1687   for(i = 0; i < nread; i++) {
1688     size_t prev = pop3c->eob;
1689
1690     switch(str[i]) {
1691     case 0x0d:
1692       if(pop3c->eob == 0) {
1693         pop3c->eob++;
1694
1695         if(i) {
1696           /* Write out the body part that didn't match */
1697           result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1698                                      i - last);
1699
1700           if(result)
1701             return result;
1702
1703           last = i;
1704         }
1705       }
1706       else if(pop3c->eob == 3)
1707         pop3c->eob++;
1708       else
1709         /* If the character match wasn't at position 0 or 3 then restart the
1710            pattern matching */
1711         pop3c->eob = 1;
1712       break;
1713
1714     case 0x0a:
1715       if(pop3c->eob == 1 || pop3c->eob == 4)
1716         pop3c->eob++;
1717       else
1718         /* If the character match wasn't at position 1 or 4 then start the
1719            search again */
1720         pop3c->eob = 0;
1721       break;
1722
1723     case 0x2e:
1724       if(pop3c->eob == 2)
1725         pop3c->eob++;
1726       else if(pop3c->eob == 3) {
1727         /* We have an extra dot after the CRLF which we need to strip off */
1728         strip_dot = TRUE;
1729         pop3c->eob = 0;
1730       }
1731       else
1732         /* If the character match wasn't at position 2 then start the search
1733            again */
1734         pop3c->eob = 0;
1735       break;
1736
1737     default:
1738       pop3c->eob = 0;
1739       break;
1740     }
1741
1742     /* Did we have a partial match which has subsequently failed? */
1743     if(prev && prev >= pop3c->eob) {
1744       /* Strip can only be non-zero for the very first mismatch after CRLF
1745          and then both prev and strip are equal and nothing will be output
1746          below */
1747       while(prev && pop3c->strip) {
1748         prev--;
1749         pop3c->strip--;
1750       }
1751
1752       if(prev) {
1753         /* If the partial match was the CRLF and dot then only write the CRLF
1754            as the server would have inserted the dot */
1755         result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
1756                                    strip_dot ? prev - 1 : prev);
1757
1758         if(result)
1759           return result;
1760
1761         last = i;
1762         strip_dot = FALSE;
1763       }
1764     }
1765   }
1766
1767   if(pop3c->eob == POP3_EOB_LEN) {
1768     /* We have a full match so the transfer is done, however we must transfer
1769     the CRLF at the start of the EOB as this is considered to be part of the
1770     message as per RFC-1939, sect. 3 */
1771     result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1772
1773     k->keepon &= ~KEEP_RECV;
1774     pop3c->eob = 0;
1775
1776     return result;
1777   }
1778
1779   if(pop3c->eob)
1780     /* While EOB is matching nothing should be output */
1781     return CURLE_OK;
1782
1783   if(nread - last) {
1784     result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1785                                nread - last);
1786   }
1787
1788   return result;
1789 }
1790
1791 #endif /* CURL_DISABLE_POP3 */