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