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