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