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