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