876987f11aacb0c0b7187a2c6ba2e39cb85d6077
[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           (void *)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, "%s", "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, "%s", "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     if(mech && (pop3c->preftype & POP3_TYPE_SASL)) {
629       /* Perform SASL based authentication */
630       if(initresp &&
631          8 + strlen(mech) + len <= 255) { /* AUTH <mech> ...<crlf> */
632         result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
633
634         if(!result)
635           state(conn, state2);
636       }
637       else {
638         result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
639
640         if(!result)
641           state(conn, state1);
642       }
643
644       Curl_safefree(initresp);
645     }
646 #ifndef CURL_DISABLE_CRYPTO_AUTH
647     else if((pop3c->authtypes & POP3_TYPE_APOP) &&
648             (pop3c->preftype & POP3_TYPE_APOP))
649       /* Perform APOP authentication */
650       result = pop3_perform_apop(conn);
651 #endif
652     else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
653             (pop3c->preftype & POP3_TYPE_CLEARTEXT))
654       /* Perform clear text authentication */
655       result = pop3_perform_user(conn);
656     else {
657       /* Other mechanisms not supported */
658       infof(conn->data, "No known authentication mechanisms supported!\n");
659       result = CURLE_LOGIN_DENIED;
660     }
661   }
662
663   return result;
664 }
665
666 /***********************************************************************
667  *
668  * pop3_perform_command()
669  *
670  * Sends a POP3 based command.
671  */
672 static CURLcode pop3_perform_command(struct connectdata *conn)
673 {
674   CURLcode result = CURLE_OK;
675   struct SessionHandle *data = conn->data;
676   struct POP3 *pop3 = data->state.proto.pop3;
677   const char *command = NULL;
678
679   /* Calculate the default command */
680   if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
681     command = "LIST";
682
683     if(pop3->id[0] != '\0')
684       /* Message specific LIST so skip the BODY transfer */
685       pop3->transfer = FTPTRANSFER_INFO;
686   }
687   else
688     command = "RETR";
689
690   /* Send the command */
691   if(pop3->id[0] != '\0')
692     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
693                            (pop3->custom && pop3->custom[0] != '\0' ?
694                             pop3->custom : command), pop3->id);
695   else
696     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s",
697                            (pop3->custom && pop3->custom[0] != '\0' ?
698                             pop3->custom : command));
699
700   if(!result)
701     state(conn, POP3_COMMAND);
702
703   return result;
704 }
705
706 /***********************************************************************
707  *
708  * pop3_perform_quit()
709  *
710  * Performs the quit action prior to sclose() be called.
711  */
712 static CURLcode pop3_perform_quit(struct connectdata *conn)
713 {
714   CURLcode result = CURLE_OK;
715
716   /* Send the QUIT command */
717   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT");
718
719   if(!result)
720     state(conn, POP3_QUIT);
721
722   return result;
723 }
724
725 /* For the initial server greeting */
726 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
727                                             int pop3code,
728                                             pop3state instate)
729 {
730   CURLcode result = CURLE_OK;
731   struct SessionHandle *data = conn->data;
732
733   (void)instate; /* no use for this yet */
734
735   if(pop3code != '+') {
736     failf(data, "Got unexpected pop3-server response");
737     result = CURLE_FTP_WEIRD_SERVER_REPLY;
738   }
739   else
740     result = pop3_perform_capa(conn);
741
742   return result;
743 }
744
745 /* For CAPA responses */
746 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
747                                      pop3state instate)
748 {
749   CURLcode result = CURLE_OK;
750   struct SessionHandle *data = conn->data;
751   struct pop3_conn *pop3c = &conn->proto.pop3c;
752
753   (void)instate; /* no use for this yet */
754
755   if(pop3code != '+')
756     result = pop3_perform_user(conn);
757   else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
758     /* We don't have a SSL/TLS connection yet, but SSL is requested */
759     if(pop3c->tls_supported)
760       /* Switch to TLS connection now */
761       result = pop3_perform_starttls(conn);
762     else if(data->set.use_ssl == CURLUSESSL_TRY)
763       /* Fallback and carry on with authentication */
764       result = pop3_perform_authenticate(conn);
765     else {
766       failf(data, "STLS not supported.");
767       result = CURLE_USE_SSL_FAILED;
768     }
769   }
770   else
771     result = pop3_perform_authenticate(conn);
772
773   return result;
774 }
775
776 /* For STARTTLS responses */
777 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
778                                          int pop3code,
779                                          pop3state instate)
780 {
781   CURLcode result = CURLE_OK;
782   struct SessionHandle *data = conn->data;
783
784   (void)instate; /* no use for this yet */
785
786   if(pop3code != '+') {
787     if(data->set.use_ssl != CURLUSESSL_TRY) {
788       failf(data, "STARTTLS denied. %c", pop3code);
789       result = CURLE_USE_SSL_FAILED;
790     }
791     else
792       result = pop3_perform_authenticate(conn);
793   }
794   else
795     result = pop3_perform_upgrade_tls(conn);
796
797   return result;
798 }
799
800 /* For AUTH PLAIN (without initial response) responses */
801 static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
802                                            int pop3code,
803                                            pop3state instate)
804 {
805   CURLcode result = CURLE_OK;
806   struct SessionHandle *data = conn->data;
807   size_t len = 0;
808   char *plainauth = NULL;
809
810   (void)instate; /* no use for this yet */
811
812   if(pop3code != '+') {
813     failf(data, "Access denied. %c", pop3code);
814     result = CURLE_LOGIN_DENIED;
815   }
816   else {
817     /* Create the authorisation message */
818     result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
819                                             &plainauth, &len);
820
821     /* Send the message */
822     if(!result) {
823       if(plainauth) {
824         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
825
826         if(!result)
827           state(conn, POP3_AUTH_FINAL);
828       }
829
830       Curl_safefree(plainauth);
831     }
832   }
833
834   return result;
835 }
836
837 /* For AUTH LOGIN (without initial response) responses */
838 static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
839                                            int pop3code,
840                                            pop3state instate)
841 {
842   CURLcode result = CURLE_OK;
843   struct SessionHandle *data = conn->data;
844   size_t len = 0;
845   char *authuser = NULL;
846
847   (void)instate; /* no use for this yet */
848
849   if(pop3code != '+') {
850     failf(data, "Access denied: %d", pop3code);
851     result = CURLE_LOGIN_DENIED;
852   }
853   else {
854     /* Create the user message */
855     result = Curl_sasl_create_login_message(data, conn->user,
856                                             &authuser, &len);
857
858     /* Send the user */
859     if(!result) {
860       if(authuser) {
861         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
862
863         if(!result)
864           state(conn, POP3_AUTH_LOGIN_PASSWD);
865       }
866
867       Curl_safefree(authuser);
868     }
869   }
870
871   return result;
872 }
873
874 /* For AUTH LOGIN user entry responses */
875 static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
876                                                     int pop3code,
877                                                     pop3state instate)
878 {
879   CURLcode result = CURLE_OK;
880   struct SessionHandle *data = conn->data;
881   size_t len = 0;
882   char *authpasswd = NULL;
883
884   (void)instate; /* no use for this yet */
885
886   if(pop3code != '+') {
887     failf(data, "Access denied: %d", pop3code);
888     result = CURLE_LOGIN_DENIED;
889   }
890   else {
891     /* Create the password message */
892     result = Curl_sasl_create_login_message(data, conn->passwd,
893                                             &authpasswd, &len);
894
895     /* Send the password */
896     if(!result) {
897       if(authpasswd) {
898         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
899
900         if(!result)
901           state(conn, POP3_AUTH_FINAL);
902       }
903
904       Curl_safefree(authpasswd);
905     }
906   }
907
908   return result;
909 }
910
911 #ifndef CURL_DISABLE_CRYPTO_AUTH
912 /* For AUTH CRAM-MD5 responses */
913 static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
914                                           int pop3code,
915                                           pop3state instate)
916 {
917   CURLcode result = CURLE_OK;
918   struct SessionHandle *data = conn->data;
919   char *chlg64 = data->state.buffer;
920   size_t len = 0;
921   char *rplyb64 = NULL;
922
923   (void)instate; /* no use for this yet */
924
925   if(pop3code != '+') {
926     failf(data, "Access denied: %d", pop3code);
927     return CURLE_LOGIN_DENIED;
928   }
929
930   /* Get the challenge */
931   for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
932     ;
933
934   /* Terminate the challenge */
935   if(*chlg64 != '=') {
936     for(len = strlen(chlg64); len--;)
937       if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
938          chlg64[len] != '\t')
939         break;
940
941     if(++len) {
942       chlg64[len] = '\0';
943     }
944   }
945
946   /* Create the response message */
947   result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
948                                              conn->passwd, &rplyb64, &len);
949
950   /* Send the response */
951   if(!result) {
952     if(rplyb64) {
953       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
954
955       if(!result)
956         state(conn, POP3_AUTH_FINAL);
957     }
958
959     Curl_safefree(rplyb64);
960   }
961
962   return result;
963 }
964
965 /* For AUTH DIGEST-MD5 challenge responses */
966 static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
967                                             int pop3code,
968                                             pop3state instate)
969 {
970   CURLcode result = CURLE_OK;
971   struct SessionHandle *data = conn->data;
972   char *chlg64 = data->state.buffer;
973   size_t len = 0;
974   char *rplyb64 = NULL;
975
976   (void)instate; /* no use for this yet */
977
978   if(pop3code != '+') {
979     failf(data, "Access denied: %d", pop3code);
980     return CURLE_LOGIN_DENIED;
981   }
982
983   /* Get the challenge */
984   for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
985     ;
986
987   /* Create the response message */
988   result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
989                                                conn->passwd, "pop",
990                                                &rplyb64, &len);
991
992   /* Send the response */
993   if(!result) {
994     if(rplyb64) {
995       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
996
997       if(!result)
998         state(conn, POP3_AUTH_DIGESTMD5_RESP);
999     }
1000
1001     Curl_safefree(rplyb64);
1002   }
1003
1004   return result;
1005 }
1006
1007 /* For AUTH DIGEST-MD5 challenge-response responses */
1008 static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
1009                                                  int pop3code,
1010                                                  pop3state instate)
1011 {
1012   CURLcode result = CURLE_OK;
1013   struct SessionHandle *data = conn->data;
1014
1015   (void)instate; /* no use for this yet */
1016
1017   if(pop3code != '+') {
1018     failf(data, "Authentication failed: %d", pop3code);
1019     result = CURLE_LOGIN_DENIED;
1020   }
1021   else {
1022     /* Send an empty response */
1023     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "");
1024
1025     if(!result)
1026       state(conn, POP3_AUTH_FINAL);
1027   }
1028
1029   return result;
1030 }
1031 #endif
1032
1033 #ifdef USE_NTLM
1034 /* For AUTH NTLM (without initial response) responses */
1035 static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
1036                                           int pop3code,
1037                                           pop3state instate)
1038 {
1039   CURLcode result = CURLE_OK;
1040   struct SessionHandle *data = conn->data;
1041   size_t len = 0;
1042   char *type1msg = NULL;
1043
1044   (void)instate; /* no use for this yet */
1045
1046   if(pop3code != '+') {
1047     failf(data, "Access denied: %d", pop3code);
1048     result = CURLE_LOGIN_DENIED;
1049   }
1050   else {
1051     /* Create the type-1 message */
1052     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1053                                                  &conn->ntlm,
1054                                                  &type1msg, &len);
1055
1056     /* Send the message */
1057     if(!result) {
1058       if(type1msg) {
1059         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
1060
1061         if(!result)
1062           state(conn, POP3_AUTH_NTLM_TYPE2MSG);
1063       }
1064
1065       Curl_safefree(type1msg);
1066     }
1067   }
1068
1069   return result;
1070 }
1071
1072 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1073 static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1074                                                    int pop3code,
1075                                                    pop3state instate)
1076 {
1077   CURLcode result = CURLE_OK;
1078   struct SessionHandle *data = conn->data;
1079   size_t len = 0;
1080   char *type3msg = NULL;
1081
1082   (void)instate; /* no use for this yet */
1083
1084   if(pop3code != '+') {
1085     failf(data, "Access denied: %d", pop3code);
1086     result = CURLE_LOGIN_DENIED;
1087   }
1088   else {
1089     /* Create the type-3 message */
1090     result = Curl_sasl_create_ntlm_type3_message(data,
1091                                                  data->state.buffer + 2,
1092                                                  conn->user, conn->passwd,
1093                                                  &conn->ntlm,
1094                                                  &type3msg, &len);
1095
1096     /* Send the message */
1097     if(!result) {
1098       if(type3msg) {
1099         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
1100
1101         if(!result)
1102           state(conn, POP3_AUTH_FINAL);
1103       }
1104
1105       Curl_safefree(type3msg);
1106     }
1107   }
1108
1109   return result;
1110 }
1111 #endif
1112
1113 /* For final responses to the AUTH sequence */
1114 static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
1115                                            int pop3code,
1116                                            pop3state instate)
1117 {
1118   CURLcode result = CURLE_OK;
1119   struct SessionHandle *data = conn->data;
1120
1121   (void)instate; /* no use for this yet */
1122
1123   if(pop3code != '+') {
1124     failf(data, "Authentication failed: %d", pop3code);
1125     result = CURLE_LOGIN_DENIED;
1126   }
1127   else
1128     /* End of connect phase */
1129     state(conn, POP3_STOP);
1130
1131   return result;
1132 }
1133
1134 #ifndef CURL_DISABLE_CRYPTO_AUTH
1135 /* For APOP responses */
1136 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
1137                                      pop3state instate)
1138 {
1139   CURLcode result = CURLE_OK;
1140   struct SessionHandle *data = conn->data;
1141
1142   (void)instate; /* no use for this yet */
1143
1144   if(pop3code != '+') {
1145     failf(data, "Authentication failed: %d", pop3code);
1146     result = CURLE_LOGIN_DENIED;
1147   }
1148   else
1149     /* End of connect phase */
1150     state(conn, POP3_STOP);
1151
1152   return result;
1153 }
1154 #endif
1155
1156 /* For USER responses */
1157 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
1158                                      pop3state instate)
1159 {
1160   CURLcode result = CURLE_OK;
1161   struct SessionHandle *data = conn->data;
1162
1163   (void)instate; /* no use for this yet */
1164
1165   if(pop3code != '+') {
1166     failf(data, "Access denied. %c", pop3code);
1167     result = CURLE_LOGIN_DENIED;
1168   }
1169   else
1170     /* Send the PASS command */
1171     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
1172                            conn->passwd ? conn->passwd : "");
1173   if(!result)
1174     state(conn, POP3_PASS);
1175
1176   return result;
1177 }
1178
1179 /* For PASS responses */
1180 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
1181                                      pop3state instate)
1182 {
1183   CURLcode result = CURLE_OK;
1184   struct SessionHandle *data = conn->data;
1185
1186   (void)instate; /* no use for this yet */
1187
1188   if(pop3code != '+') {
1189     failf(data, "Access denied. %c", pop3code);
1190     result = CURLE_LOGIN_DENIED;
1191   }
1192   else
1193     /* End of connect phase */
1194     state(conn, POP3_STOP);
1195
1196   return result;
1197 }
1198
1199 /* For command responses */
1200 static CURLcode pop3_state_command_resp(struct connectdata *conn,
1201                                         int pop3code,
1202                                         pop3state instate)
1203 {
1204   CURLcode result = CURLE_OK;
1205   struct SessionHandle *data = conn->data;
1206   struct POP3 *pop3 = data->state.proto.pop3;
1207   struct pop3_conn *pop3c = &conn->proto.pop3c;
1208   struct pingpong *pp = &pop3c->pp;
1209
1210   (void)instate; /* no use for this yet */
1211
1212   if(pop3code != '+') {
1213     state(conn, POP3_STOP);
1214     return CURLE_RECV_ERROR;
1215   }
1216
1217   /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1218      EOB string so count this is two matching bytes. This is necessary to make
1219      the code detect the EOB if the only data than comes now is %2e CR LF like
1220      when there is no body to return. */
1221   pop3c->eob = 2;
1222
1223   /* But since this initial CR LF pair is not part of the actual body, we set
1224      the strip counter here so that these bytes won't be delivered. */
1225   pop3c->strip = 2;
1226
1227   if(pop3->transfer == FTPTRANSFER_BODY) {
1228     /* POP3 download */
1229     Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
1230
1231     if(pp->cache) {
1232       /* The header "cache" contains a bunch of data that is actually body
1233          content so send it as such. Note that there may even be additional
1234          "headers" after the body */
1235
1236       if(!data->set.opt_no_body) {
1237         result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
1238         if(result)
1239           return result;
1240       }
1241
1242       /* Free the cache */
1243       Curl_safefree(pp->cache);
1244
1245       /* Reset the cache size */
1246       pp->cache_size = 0;
1247     }
1248   }
1249
1250   /* End of DO phase */
1251   state(conn, POP3_STOP);
1252
1253   return result;
1254 }
1255
1256 static CURLcode pop3_statemach_act(struct connectdata *conn)
1257 {
1258   CURLcode result = CURLE_OK;
1259   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1260   int pop3code;
1261   struct pop3_conn *pop3c = &conn->proto.pop3c;
1262   struct pingpong *pp = &pop3c->pp;
1263   size_t nread = 0;
1264
1265   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1266   if(pop3c->state == POP3_UPGRADETLS)
1267     return pop3_perform_upgrade_tls(conn);
1268
1269   /* Flush any data that needs to be sent */
1270   if(pp->sendleft)
1271     return Curl_pp_flushsend(pp);
1272
1273   /* Read the response from the server */
1274   result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1275   if(result)
1276     return result;
1277
1278   if(pop3code) {
1279     /* We have now received a full POP3 server response */
1280     switch(pop3c->state) {
1281     case POP3_SERVERGREET:
1282       result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1283       break;
1284
1285     case POP3_CAPA:
1286       result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1287       break;
1288
1289     case POP3_STARTTLS:
1290       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1291       break;
1292
1293     case POP3_AUTH_PLAIN:
1294       result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
1295       break;
1296
1297     case POP3_AUTH_LOGIN:
1298       result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
1299       break;
1300
1301     case POP3_AUTH_LOGIN_PASSWD:
1302       result = pop3_state_auth_login_password_resp(conn, pop3code,
1303                                                    pop3c->state);
1304       break;
1305
1306 #ifndef CURL_DISABLE_CRYPTO_AUTH
1307     case POP3_AUTH_CRAMMD5:
1308       result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
1309       break;
1310
1311     case POP3_AUTH_DIGESTMD5:
1312       result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
1313       break;
1314
1315     case POP3_AUTH_DIGESTMD5_RESP:
1316       result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
1317       break;
1318 #endif
1319
1320 #ifdef USE_NTLM
1321     case POP3_AUTH_NTLM:
1322       result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
1323       break;
1324
1325     case POP3_AUTH_NTLM_TYPE2MSG:
1326       result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
1327                                                   pop3c->state);
1328       break;
1329 #endif
1330
1331     case POP3_AUTH_FINAL:
1332       result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
1333       break;
1334
1335 #ifndef CURL_DISABLE_CRYPTO_AUTH
1336     case POP3_APOP:
1337       result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1338       break;
1339 #endif
1340
1341     case POP3_USER:
1342       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1343       break;
1344
1345     case POP3_PASS:
1346       result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1347       break;
1348
1349     case POP3_COMMAND:
1350       result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1351       break;
1352
1353     case POP3_QUIT:
1354       /* fallthrough, just stop! */
1355     default:
1356       /* internal error */
1357       state(conn, POP3_STOP);
1358       break;
1359     }
1360   }
1361
1362   return result;
1363 }
1364
1365 /* Called repeatedly until done from multi.c */
1366 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1367 {
1368   CURLcode result = CURLE_OK;
1369   struct pop3_conn *pop3c = &conn->proto.pop3c;
1370
1371   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone)
1372     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1373   else
1374     result = Curl_pp_statemach(&pop3c->pp, FALSE);
1375
1376   *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1377
1378   return result;
1379 }
1380
1381 static CURLcode pop3_block_statemach(struct connectdata *conn)
1382 {
1383   CURLcode result = CURLE_OK;
1384   struct pop3_conn *pop3c = &conn->proto.pop3c;
1385
1386   while(pop3c->state != POP3_STOP && !result)
1387     result = Curl_pp_statemach(&pop3c->pp, TRUE);
1388
1389   return result;
1390 }
1391
1392 /* Allocate and initialize the POP3 struct for the current SessionHandle if
1393    required */
1394 static CURLcode pop3_init(struct connectdata *conn)
1395 {
1396   CURLcode result = CURLE_OK;
1397   struct SessionHandle *data = conn->data;
1398   struct POP3 *pop3 = data->state.proto.pop3;
1399
1400   if(!pop3) {
1401     pop3 = data->state.proto.pop3 = calloc(sizeof(struct POP3), 1);
1402     if(!pop3)
1403       result = CURLE_OUT_OF_MEMORY;
1404   }
1405
1406   return result;
1407 }
1408
1409 /* For the POP3 "protocol connect" and "doing" phases only */
1410 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1411                         int numsocks)
1412 {
1413   return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1414 }
1415
1416 /***********************************************************************
1417  *
1418  * pop3_connect()
1419  *
1420  * This function should do everything that is to be considered a part of the
1421  * connection phase.
1422  *
1423  * The variable 'done' points to will be TRUE if the protocol-layer connect
1424  * phase is done when this function returns, or FALSE if not.
1425  */
1426 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1427 {
1428   CURLcode result = CURLE_OK;
1429   struct pop3_conn *pop3c = &conn->proto.pop3c;
1430   struct pingpong *pp = &pop3c->pp;
1431
1432   *done = FALSE; /* default to not done yet */
1433
1434   /* If there already is a protocol-specific struct allocated for this
1435      sessionhandle, deal with it */
1436   Curl_reset_reqproto(conn);
1437
1438   /* Initialise the POP3 layer */
1439   result = pop3_init(conn);
1440   if(result)
1441     return result;
1442
1443   /* We always support persistent connections in POP3 */
1444   conn->bits.close = FALSE;
1445
1446   /* Set the default response time-out */
1447   pp->response_time = RESP_TIMEOUT;
1448   pp->statemach_act = pop3_statemach_act;
1449   pp->endofresp = pop3_endofresp;
1450   pp->conn = conn;
1451
1452   /* Set the default preferred authentication type and mechanism */
1453   pop3c->preftype = POP3_TYPE_ANY;
1454   pop3c->prefmech = SASL_AUTH_ANY;
1455
1456   /* Initialise the pingpong layer */
1457   Curl_pp_init(pp);
1458
1459   /* Parse the URL options */
1460   result = pop3_parse_url_options(conn);
1461   if(result)
1462     return result;
1463
1464   /* Start off waiting for the server greeting response */
1465   state(conn, POP3_SERVERGREET);
1466
1467   result = pop3_multi_statemach(conn, done);
1468
1469   return result;
1470 }
1471
1472 /***********************************************************************
1473  *
1474  * pop3_done()
1475  *
1476  * The DONE function. This does what needs to be done after a single DO has
1477  * performed.
1478  *
1479  * Input argument is already checked for validity.
1480  */
1481 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1482                           bool premature)
1483 {
1484   CURLcode result = CURLE_OK;
1485   struct SessionHandle *data = conn->data;
1486   struct POP3 *pop3 = data->state.proto.pop3;
1487
1488   (void)premature;
1489
1490   if(!pop3)
1491     /* When the easy handle is removed from the multi interface while libcurl
1492        is still trying to resolve the host name, the POP3 struct is not yet
1493        initialized. However, the removal action calls Curl_done() which in
1494        turn calls this function, so we simply return success. */
1495     return CURLE_OK;
1496
1497   if(status) {
1498     conn->bits.close = TRUE; /* marked for closure */
1499     result = status;         /* use the already set error code */
1500   }
1501
1502   /* Cleanup our per-request based variables */
1503   Curl_safefree(pop3->id);
1504   Curl_safefree(pop3->custom);
1505
1506   /* Clear the transfer mode for the next request */
1507   pop3->transfer = FTPTRANSFER_BODY;
1508
1509   return result;
1510 }
1511
1512 /***********************************************************************
1513  *
1514  * pop3_perform()
1515  *
1516  * This is the actual DO function for POP3. Get a message/listing according to
1517  * the options previously setup.
1518  */
1519 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1520                              bool *dophase_done)
1521 {
1522   /* This is POP3 and no proxy */
1523   CURLcode result = CURLE_OK;
1524
1525   DEBUGF(infof(conn->data, "DO phase starts\n"));
1526
1527   if(conn->data->set.opt_no_body) {
1528     /* Requested no body means no transfer */
1529     struct POP3 *pop3 = conn->data->state.proto.pop3;
1530     pop3->transfer = FTPTRANSFER_INFO;
1531   }
1532
1533   *dophase_done = FALSE; /* not done yet */
1534
1535   /* Start the first command in the DO phase */
1536   result = pop3_perform_command(conn);
1537   if(result)
1538     return result;
1539
1540   /* Run the state-machine */
1541   result = pop3_multi_statemach(conn, dophase_done);
1542
1543   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1544
1545   if(*dophase_done)
1546     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1547
1548   return result;
1549 }
1550
1551 /***********************************************************************
1552  *
1553  * pop3_do()
1554  *
1555  * This function is registered as 'curl_do' function. It decodes the path
1556  * parts etc as a wrapper to the actual DO function (pop3_perform).
1557  *
1558  * The input argument is already checked for validity.
1559  */
1560 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1561 {
1562   CURLcode result = CURLE_OK;
1563
1564   *done = FALSE; /* default to false */
1565
1566   /* Since connections can be re-used between SessionHandles, there might be a
1567      connection already existing but on a fresh SessionHandle struct. As such
1568      we make sure we have a good POP3 struct to play with. For new connections
1569      the POP3 struct is allocated and setup in the pop3_connect() function. */
1570   Curl_reset_reqproto(conn);
1571   result = pop3_init(conn);
1572   if(result)
1573     return result;
1574
1575   /* Parse the URL path */
1576   result = pop3_parse_url_path(conn);
1577   if(result)
1578     return result;
1579
1580   /* Parse the custom request */
1581   result = pop3_parse_custom_request(conn);
1582   if(result)
1583     return result;
1584
1585   result = pop3_regular_transfer(conn, done);
1586
1587   return result;
1588 }
1589
1590 /***********************************************************************
1591  *
1592  * pop3_disconnect()
1593  *
1594  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1595  * resources. BLOCKING.
1596  */
1597 static CURLcode pop3_disconnect(struct connectdata *conn,
1598                                 bool dead_connection)
1599 {
1600   struct pop3_conn *pop3c = &conn->proto.pop3c;
1601
1602   /* We cannot send quit unconditionally. If this connection is stale or
1603      bad in any way, sending quit and waiting around here will make the
1604      disconnect wait in vain and cause more problems than we need to. */
1605
1606   /* The POP3 session may or may not have been allocated/setup at this
1607      point! */
1608   if(!dead_connection && pop3c->pp.conn)
1609     if(!pop3_perform_quit(conn))
1610       (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1611
1612   /* Disconnect from the server */
1613   Curl_pp_disconnect(&pop3c->pp);
1614
1615   /* Cleanup the SASL module */
1616   Curl_sasl_cleanup(conn, pop3c->authused);
1617
1618   /* Cleanup our connection based variables */
1619   Curl_safefree(pop3c->apoptimestamp);
1620
1621   return CURLE_OK;
1622 }
1623
1624 /* Call this when the DO phase has completed */
1625 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1626 {
1627   (void)conn;
1628   (void)connected;
1629
1630   return CURLE_OK;
1631 }
1632
1633 /* Called from multi.c while DOing */
1634 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1635 {
1636   CURLcode result = pop3_multi_statemach(conn, dophase_done);
1637
1638   if(result)
1639     DEBUGF(infof(conn->data, "DO phase failed\n"));
1640   else if(*dophase_done) {
1641     result = pop3_dophase_done(conn, FALSE /* not connected */);
1642
1643     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1644   }
1645
1646   return result;
1647 }
1648
1649 /***********************************************************************
1650  *
1651  * pop3_regular_transfer()
1652  *
1653  * The input argument is already checked for validity.
1654  *
1655  * Performs all commands done before a regular transfer between a local and a
1656  * remote host.
1657  */
1658 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1659                                       bool *dophase_done)
1660 {
1661   CURLcode result = CURLE_OK;
1662   bool connected = FALSE;
1663   struct SessionHandle *data = conn->data;
1664
1665   /* Make sure size is unknown at this point */
1666   data->req.size = -1;
1667
1668   /* Set the progress data */
1669   Curl_pgrsSetUploadCounter(data, 0);
1670   Curl_pgrsSetDownloadCounter(data, 0);
1671   Curl_pgrsSetUploadSize(data, 0);
1672   Curl_pgrsSetDownloadSize(data, 0);
1673
1674   /* Carry out the perform */
1675   result = pop3_perform(conn, &connected, dophase_done);
1676
1677   /* Perform post DO phase operations if necessary */
1678   if(!result && *dophase_done)
1679     result = pop3_dophase_done(conn, connected);
1680
1681   return result;
1682 }
1683
1684 static CURLcode pop3_setup_connection(struct connectdata *conn)
1685 {
1686   struct SessionHandle *data = conn->data;
1687
1688   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1689     /* Unless we have asked to tunnel POP3 operations through the proxy, we
1690        switch and use HTTP operations only */
1691 #ifndef CURL_DISABLE_HTTP
1692     if(conn->handler == &Curl_handler_pop3)
1693       conn->handler = &Curl_handler_pop3_proxy;
1694     else {
1695 #ifdef USE_SSL
1696       conn->handler = &Curl_handler_pop3s_proxy;
1697 #else
1698       failf(data, "POP3S not supported!");
1699       return CURLE_UNSUPPORTED_PROTOCOL;
1700 #endif
1701     }
1702
1703     /* We explicitly mark this connection as persistent here as we're doing
1704        POP3 over HTTP and thus we accidentally avoid setting this value
1705        otherwise */
1706     conn->bits.close = FALSE;
1707 #else
1708     failf(data, "POP3 over http proxy requires HTTP support built-in!");
1709     return CURLE_UNSUPPORTED_PROTOCOL;
1710 #endif
1711   }
1712
1713   data->state.path++;   /* don't include the initial slash */
1714
1715   return CURLE_OK;
1716 }
1717
1718 /***********************************************************************
1719  *
1720  * pop3_parse_url_options()
1721  *
1722  * Parse the URL login options.
1723  */
1724 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1725 {
1726   CURLcode result = CURLE_OK;
1727   struct pop3_conn *pop3c = &conn->proto.pop3c;
1728   const char *options = conn->options;
1729   const char *ptr = options;
1730
1731   if(options) {
1732     const char *key = ptr;
1733
1734     while(*ptr && *ptr != '=')
1735         ptr++;
1736
1737     if(strnequal(key, "AUTH", 4)) {
1738       const char *value = ptr + 1;
1739
1740       if(strequal(value, "*")) {
1741         pop3c->preftype = POP3_TYPE_ANY;
1742         pop3c->prefmech = SASL_AUTH_ANY;
1743       }
1744       else if(strequal(value, "+APOP")) {
1745         pop3c->preftype = POP3_TYPE_APOP;
1746         pop3c->prefmech = SASL_AUTH_NONE;
1747       }
1748       else if(strequal(value, "LOGIN")) {
1749         pop3c->preftype = POP3_TYPE_SASL;
1750         pop3c->prefmech = SASL_MECH_LOGIN;
1751       }
1752       else if(strequal(value, "PLAIN")) {
1753         pop3c->preftype = POP3_TYPE_SASL;
1754         pop3c->prefmech = SASL_MECH_PLAIN;
1755       }
1756       else if(strequal(value, "CRAM-MD5")) {
1757         pop3c->preftype = POP3_TYPE_SASL;
1758         pop3c->prefmech = SASL_MECH_CRAM_MD5;
1759       }
1760       else if(strequal(value, "DIGEST-MD5")) {
1761         pop3c->preftype = POP3_TYPE_SASL;
1762         pop3c->prefmech = SASL_MECH_DIGEST_MD5;
1763       }
1764       else if(strequal(value, "GSSAPI")) {
1765         pop3c->preftype = POP3_TYPE_SASL;
1766         pop3c->prefmech = SASL_MECH_GSSAPI;
1767       }
1768       else if(strequal(value, "NTLM")) {
1769         pop3c->preftype = POP3_TYPE_SASL;
1770         pop3c->prefmech = SASL_MECH_NTLM;
1771       }
1772       else {
1773         pop3c->preftype = POP3_TYPE_NONE;
1774         pop3c->prefmech = SASL_AUTH_NONE;
1775       }
1776     }
1777     else
1778       result = CURLE_URL_MALFORMAT;
1779   }
1780
1781   return result;
1782 }
1783
1784 /***********************************************************************
1785  *
1786  * pop3_parse_url_path()
1787  *
1788  * Parse the URL path into separate path components.
1789  */
1790 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1791 {
1792   /* The POP3 struct is already initialised in pop3_connect() */
1793   struct SessionHandle *data = conn->data;
1794   struct POP3 *pop3 = data->state.proto.pop3;
1795   const char *path = data->state.path;
1796
1797   /* URL decode the path for the message ID */
1798   return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1799 }
1800
1801 /***********************************************************************
1802  *
1803  * pop3_parse_custom_request()
1804  *
1805  * Parse the custom request.
1806  */
1807 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1808 {
1809   CURLcode result = CURLE_OK;
1810   struct SessionHandle *data = conn->data;
1811   struct POP3 *pop3 = data->state.proto.pop3;
1812   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1813
1814   /* URL decode the custom request */
1815   if(custom)
1816     result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1817
1818   return result;
1819 }
1820
1821 /***********************************************************************
1822  *
1823  * Curl_pop3_write()
1824  *
1825  * This function scans the body after the end-of-body and writes everything
1826  * until the end is found.
1827  */
1828 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1829 {
1830   /* This code could be made into a special function in the handler struct */
1831   CURLcode result = CURLE_OK;
1832   struct SessionHandle *data = conn->data;
1833   struct SingleRequest *k = &data->req;
1834
1835   struct pop3_conn *pop3c = &conn->proto.pop3c;
1836   bool strip_dot = FALSE;
1837   size_t last = 0;
1838   size_t i;
1839
1840   /* Search through the buffer looking for the end-of-body marker which is
1841      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1842      the eob so the server will have prefixed it with an extra dot which we
1843      need to strip out. Additionally the marker could of course be spread out
1844      over 5 different data chunks. */
1845   for(i = 0; i < nread; i++) {
1846     size_t prev = pop3c->eob;
1847
1848     switch(str[i]) {
1849     case 0x0d:
1850       if(pop3c->eob == 0) {
1851         pop3c->eob++;
1852
1853         if(i) {
1854           /* Write out the body part that didn't match */
1855           result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1856                                      i - last);
1857
1858           if(result)
1859             return result;
1860
1861           last = i;
1862         }
1863       }
1864       else if(pop3c->eob == 3)
1865         pop3c->eob++;
1866       else
1867         /* If the character match wasn't at position 0 or 3 then restart the
1868            pattern matching */
1869         pop3c->eob = 1;
1870       break;
1871
1872     case 0x0a:
1873       if(pop3c->eob == 1 || pop3c->eob == 4)
1874         pop3c->eob++;
1875       else
1876         /* If the character match wasn't at position 1 or 4 then start the
1877            search again */
1878         pop3c->eob = 0;
1879       break;
1880
1881     case 0x2e:
1882       if(pop3c->eob == 2)
1883         pop3c->eob++;
1884       else if(pop3c->eob == 3) {
1885         /* We have an extra dot after the CRLF which we need to strip off */
1886         strip_dot = TRUE;
1887         pop3c->eob = 0;
1888       }
1889       else
1890         /* If the character match wasn't at position 2 then start the search
1891            again */
1892         pop3c->eob = 0;
1893       break;
1894
1895     default:
1896       pop3c->eob = 0;
1897       break;
1898     }
1899
1900     /* Did we have a partial match which has subsequently failed? */
1901     if(prev && prev >= pop3c->eob) {
1902       /* Strip can only be non-zero for the very first mismatch after CRLF
1903          and then both prev and strip are equal and nothing will be output
1904          below */
1905       while(prev && pop3c->strip) {
1906         prev--;
1907         pop3c->strip--;
1908       }
1909
1910       if(prev) {
1911         /* If the partial match was the CRLF and dot then only write the CRLF
1912            as the server would have inserted the dot */
1913         result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
1914                                    strip_dot ? prev - 1 : prev);
1915
1916         if(result)
1917           return result;
1918
1919         last = i;
1920         strip_dot = FALSE;
1921       }
1922     }
1923   }
1924
1925   if(pop3c->eob == POP3_EOB_LEN) {
1926     /* We have a full match so the transfer is done, however we must transfer
1927     the CRLF at the start of the EOB as this is considered to be part of the
1928     message as per RFC-1939, sect. 3 */
1929     result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1930
1931     k->keepon &= ~KEEP_RECV;
1932     pop3c->eob = 0;
1933
1934     return result;
1935   }
1936
1937   if(pop3c->eob)
1938     /* While EOB is matching nothing should be output */
1939     return CURLE_OK;
1940
1941   if(nread - last) {
1942     result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1943                                nread - last);
1944   }
1945
1946   return result;
1947 }
1948
1949 #endif /* CURL_DISABLE_POP3 */