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