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