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