]> rtime.felk.cvut.cz Git - hornmich/skoda-qr-demo.git/blob - QRScanner/mobile/jni/thirdparty/curl/lib/pop3.c
Add MuPDF native source codes
[hornmich/skoda-qr-demo.git] / QRScanner / mobile / jni / thirdparty / curl / lib / pop3.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * RFC1734 POP3 Authentication
22  * RFC1939 POP3 protocol
23  * RFC2195 CRAM-MD5 authentication
24  * RFC2384 POP URL Scheme
25  * RFC2449 POP3 Extension Mechanism
26  * RFC2595 Using TLS with IMAP, POP3 and ACAP
27  * RFC2831 DIGEST-MD5 authentication
28  * RFC4422 Simple Authentication and Security Layer (SASL)
29  * RFC4616 PLAIN authentication
30  * RFC5034 POP3 SASL Authentication Mechanism
31  *
32  ***************************************************************************/
33
34 #include "curl_setup.h"
35
36 #ifndef CURL_DISABLE_POP3
37
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 #include <arpa/inet.h>
43 #endif
44 #ifdef HAVE_UTSNAME_H
45 #include <sys/utsname.h>
46 #endif
47 #ifdef HAVE_NETDB_H
48 #include <netdb.h>
49 #endif
50 #ifdef __VMS
51 #include <in.h>
52 #include <inet.h>
53 #endif
54
55 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
56 #undef in_addr_t
57 #define in_addr_t unsigned long
58 #endif
59
60 #include <curl/curl.h>
61 #include "urldata.h"
62 #include "sendf.h"
63 #include "if2ip.h"
64 #include "hostip.h"
65 #include "progress.h"
66 #include "transfer.h"
67 #include "escape.h"
68 #include "http.h" /* for HTTP proxy tunnel stuff */
69 #include "socks.h"
70 #include "pop3.h"
71
72 #include "strtoofft.h"
73 #include "strequal.h"
74 #include "sslgen.h"
75 #include "connect.h"
76 #include "strerror.h"
77 #include "select.h"
78 #include "multiif.h"
79 #include "url.h"
80 #include "rawstr.h"
81 #include "curl_sasl.h"
82 #include "curl_md5.h"
83 #include "warnless.h"
84
85 #define _MPRINTF_REPLACE /* use our functions only */
86 #include <curl/mprintf.h>
87
88 #include "curl_memory.h"
89 /* The last #include file should be: */
90 #include "memdebug.h"
91
92 /* Local API functions */
93 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
94 static CURLcode pop3_do(struct connectdata *conn, bool *done);
95 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
96                           bool premature);
97 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
98 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
99 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
100 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
101                         int numsocks);
102 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
103 static CURLcode pop3_setup_connection(struct connectdata *conn);
104 static CURLcode pop3_parse_url_options(struct connectdata *conn);
105 static CURLcode pop3_parse_url_path(struct connectdata *conn);
106 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
107
108 /*
109  * POP3 protocol handler.
110  */
111
112 const struct Curl_handler Curl_handler_pop3 = {
113   "POP3",                           /* scheme */
114   pop3_setup_connection,            /* setup_connection */
115   pop3_do,                          /* do_it */
116   pop3_done,                        /* done */
117   ZERO_NULL,                        /* do_more */
118   pop3_connect,                     /* connect_it */
119   pop3_multi_statemach,             /* connecting */
120   pop3_doing,                       /* doing */
121   pop3_getsock,                     /* proto_getsock */
122   pop3_getsock,                     /* doing_getsock */
123   ZERO_NULL,                        /* domore_getsock */
124   ZERO_NULL,                        /* perform_getsock */
125   pop3_disconnect,                  /* disconnect */
126   ZERO_NULL,                        /* readwrite */
127   PORT_POP3,                        /* defport */
128   CURLPROTO_POP3,                   /* protocol */
129   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
130 };
131
132 #ifdef USE_SSL
133 /*
134  * POP3S protocol handler.
135  */
136
137 const struct Curl_handler Curl_handler_pop3s = {
138   "POP3S",                          /* scheme */
139   pop3_setup_connection,            /* setup_connection */
140   pop3_do,                          /* do_it */
141   pop3_done,                        /* done */
142   ZERO_NULL,                        /* do_more */
143   pop3_connect,                     /* connect_it */
144   pop3_multi_statemach,             /* connecting */
145   pop3_doing,                       /* doing */
146   pop3_getsock,                     /* proto_getsock */
147   pop3_getsock,                     /* doing_getsock */
148   ZERO_NULL,                        /* domore_getsock */
149   ZERO_NULL,                        /* perform_getsock */
150   pop3_disconnect,                  /* disconnect */
151   ZERO_NULL,                        /* readwrite */
152   PORT_POP3S,                       /* defport */
153   CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */
154   PROTOPT_CLOSEACTION | PROTOPT_SSL
155   | PROTOPT_NOURLQUERY              /* flags */
156 };
157 #endif
158
159 #ifndef CURL_DISABLE_HTTP
160 /*
161  * HTTP-proxyed POP3 protocol handler.
162  */
163
164 static const struct Curl_handler Curl_handler_pop3_proxy = {
165   "POP3",                               /* scheme */
166   ZERO_NULL,                            /* setup_connection */
167   Curl_http,                            /* do_it */
168   Curl_http_done,                       /* done */
169   ZERO_NULL,                            /* do_more */
170   ZERO_NULL,                            /* connect_it */
171   ZERO_NULL,                            /* connecting */
172   ZERO_NULL,                            /* doing */
173   ZERO_NULL,                            /* proto_getsock */
174   ZERO_NULL,                            /* doing_getsock */
175   ZERO_NULL,                            /* domore_getsock */
176   ZERO_NULL,                            /* perform_getsock */
177   ZERO_NULL,                            /* disconnect */
178   ZERO_NULL,                            /* readwrite */
179   PORT_POP3,                            /* defport */
180   CURLPROTO_HTTP,                       /* protocol */
181   PROTOPT_NONE                          /* flags */
182 };
183
184 #ifdef USE_SSL
185 /*
186  * HTTP-proxyed POP3S protocol handler.
187  */
188
189 static const struct Curl_handler Curl_handler_pop3s_proxy = {
190   "POP3S",                              /* scheme */
191   ZERO_NULL,                            /* setup_connection */
192   Curl_http,                            /* do_it */
193   Curl_http_done,                       /* done */
194   ZERO_NULL,                            /* do_more */
195   ZERO_NULL,                            /* connect_it */
196   ZERO_NULL,                            /* connecting */
197   ZERO_NULL,                            /* doing */
198   ZERO_NULL,                            /* proto_getsock */
199   ZERO_NULL,                            /* doing_getsock */
200   ZERO_NULL,                            /* domore_getsock */
201   ZERO_NULL,                            /* perform_getsock */
202   ZERO_NULL,                            /* disconnect */
203   ZERO_NULL,                            /* readwrite */
204   PORT_POP3S,                           /* defport */
205   CURLPROTO_HTTP,                       /* protocol */
206   PROTOPT_NONE                          /* flags */
207 };
208 #endif
209 #endif
210
211 #ifdef USE_SSL
212 static void pop3_to_pop3s(struct connectdata *conn)
213 {
214   conn->handler = &Curl_handler_pop3s;
215 }
216 #else
217 #define pop3_to_pop3s(x) Curl_nop_stmt
218 #endif
219
220 /***********************************************************************
221  *
222  * pop3_endofresp()
223  *
224  * Checks for an ending POP3 status code at the start of the given string, but
225  * also detects the APOP timestamp from the server greeting and various
226  * capabilities from the CAPA response including the supported authentication
227  * types and allowed SASL mechanisms.
228  */
229 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
230                            int *resp)
231 {
232   struct pop3_conn *pop3c = &conn->proto.pop3c;
233   size_t wordlen;
234   size_t i;
235
236   /* Do we have an error response? */
237   if(len >= 4 && !memcmp("-ERR", line, 4)) {
238     *resp = '-';
239
240     return TRUE;
241   }
242
243   /* Are we processing servergreet responses? */
244   if(pop3c->state == POP3_SERVERGREET) {
245     /* Look for the APOP timestamp */
246     if(len >= 3 && line[len - 3] == '>') {
247       for(i = 0; i < len - 3; ++i) {
248         if(line[i] == '<') {
249           /* Calculate the length of the timestamp */
250           size_t timestamplen = len - 2 - i;
251
252           /* Allocate some memory for the timestamp */
253           pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
254
255           if(!pop3c->apoptimestamp)
256             break;
257
258           /* Copy the timestamp */
259           memcpy(pop3c->apoptimestamp, line + i, timestamplen);
260           pop3c->apoptimestamp[timestamplen] = '\0';
261           break;
262         }
263       }
264     }
265   }
266   /* Are we processing CAPA command responses? */
267   else if(pop3c->state == POP3_CAPA) {
268     /* Do we have the terminating line? */
269     if(len >= 1 && !memcmp(line, ".", 1)) {
270       *resp = '+';
271
272       return TRUE;
273     }
274
275     /* Does the server support the STLS capability? */
276     if(len >= 4 && !memcmp(line, "STLS", 4))
277       pop3c->tls_supported = TRUE;
278
279     /* Does the server support clear text authentication? */
280     else if(len >= 4 && !memcmp(line, "USER", 4))
281       pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
282
283     /* Does the server support APOP authentication? */
284     else if(len >= 4 && !memcmp(line, "APOP", 4))
285       pop3c->authtypes |= POP3_TYPE_APOP;
286
287     /* Does the server support SASL based authentication? */
288     else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
289       pop3c->authtypes |= POP3_TYPE_SASL;
290
291       /* Advance past the SASL keyword */
292       line += 5;
293       len -= 5;
294
295       /* Loop through the data line */
296       for(;;) {
297         while(len &&
298               (*line == ' ' || *line == '\t' ||
299                *line == '\r' || *line == '\n')) {
300
301           line++;
302           len--;
303         }
304
305         if(!len)
306           break;
307
308         /* Extract the word */
309         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
310               line[wordlen] != '\t' && line[wordlen] != '\r' &&
311               line[wordlen] != '\n';)
312           wordlen++;
313
314         /* Test the word for a matching authentication mechanism */
315         if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
316           pop3c->authmechs |= SASL_MECH_LOGIN;
317         else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
318           pop3c->authmechs |= SASL_MECH_PLAIN;
319         else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
320           pop3c->authmechs |= SASL_MECH_CRAM_MD5;
321         else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
322           pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
323         else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
324           pop3c->authmechs |= SASL_MECH_GSSAPI;
325         else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
326           pop3c->authmechs |= SASL_MECH_EXTERNAL;
327         else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
328           pop3c->authmechs |= SASL_MECH_NTLM;
329
330         line += wordlen;
331         len -= wordlen;
332       }
333     }
334
335     return FALSE;
336   }
337
338   /* Do we have a command or continuation response? */
339   if((len >= 3 && !memcmp("+OK", line, 3)) ||
340      (len >= 1 && !memcmp("+", line, 1))) {
341     *resp = '+';
342
343     return TRUE;
344   }
345
346   return FALSE; /* Nothing for us */
347 }
348
349 /***********************************************************************
350  *
351  * state()
352  *
353  * This is the ONLY way to change POP3 state!
354  */
355 static void state(struct connectdata *conn, pop3state newstate)
356 {
357   struct pop3_conn *pop3c = &conn->proto.pop3c;
358 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
359   /* for debug purposes */
360   static const char * const names[] = {
361     "STOP",
362     "SERVERGREET",
363     "CAPA",
364     "STARTTLS",
365     "UPGRADETLS",
366     "AUTH_PLAIN",
367     "AUTH_LOGIN",
368     "AUTH_LOGIN_PASSWD",
369     "AUTH_CRAMMD5",
370     "AUTH_DIGESTMD5",
371     "AUTH_DIGESTMD5_RESP",
372     "AUTH_NTLM",
373     "AUTH_NTLM_TYPE2MSG",
374     "AUTH_FINAL",
375     "APOP",
376     "USER",
377     "PASS",
378     "COMMAND",
379     "QUIT",
380     /* LAST */
381   };
382
383   if(pop3c->state != newstate)
384     infof(conn->data, "POP3 %p state change from %s to %s\n",
385           pop3c, names[pop3c->state], names[newstate]);
386 #endif
387
388   pop3c->state = newstate;
389 }
390
391 /***********************************************************************
392  *
393  * pop3_perform_capa()
394  *
395  * Sends the CAPA command in order to obtain a list of server side supported
396  * capabilities.
397  */
398 static CURLcode pop3_perform_capa(struct connectdata *conn)
399 {
400   CURLcode result = CURLE_OK;
401   struct pop3_conn *pop3c = &conn->proto.pop3c;
402
403   pop3c->authmechs = 0;         /* No known authentication mechanisms yet */
404   pop3c->authused = 0;          /* Clear the authentication mechanism used */
405   pop3c->tls_supported = FALSE; /* Clear the TLS capability */
406
407   /* Send the CAPA command */
408   result = Curl_pp_sendf(&pop3c->pp, "CAPA");
409
410   if(!result)
411     state(conn, POP3_CAPA);
412
413   return result;
414 }
415
416 /***********************************************************************
417  *
418  * pop3_perform_starttls()
419  *
420  * Sends the STLS command to start the upgrade to TLS.
421  */
422 static CURLcode pop3_perform_starttls(struct connectdata *conn)
423 {
424   CURLcode result = CURLE_OK;
425
426   /* Send the STLS command */
427   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "STLS");
428
429   if(!result)
430     state(conn, POP3_STARTTLS);
431
432   return result;
433 }
434
435 /***********************************************************************
436  *
437  * pop3_perform_upgrade_tls()
438  *
439  * Performs the upgrade to TLS.
440  */
441 static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
442 {
443   CURLcode result = CURLE_OK;
444   struct pop3_conn *pop3c = &conn->proto.pop3c;
445
446   /* Start the SSL connection */
447   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
448
449   if(!result) {
450     if(pop3c->state != POP3_UPGRADETLS)
451       state(conn, POP3_UPGRADETLS);
452
453     if(pop3c->ssldone) {
454       pop3_to_pop3s(conn);
455       result = pop3_perform_capa(conn);
456     }
457   }
458
459   return result;
460 }
461
462 /***********************************************************************
463  *
464  * pop3_perform_user()
465  *
466  * Sends a clear text USER command to authenticate with.
467  */
468 static CURLcode pop3_perform_user(struct connectdata *conn)
469 {
470   CURLcode result = CURLE_OK;
471
472   /* Check we have a username and password to authenticate with and end the
473      connect phase if we don't */
474   if(!conn->bits.user_passwd) {
475     state(conn, POP3_STOP);
476
477     return result;
478   }
479
480   /* Send the USER command */
481   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
482                          conn->user ? conn->user : "");
483   if(!result)
484     state(conn, POP3_USER);
485
486   return result;
487 }
488
489 #ifndef CURL_DISABLE_CRYPTO_AUTH
490 /***********************************************************************
491  *
492  * pop3_perform_apop()
493  *
494  * Sends an APOP command to authenticate with.
495  */
496 static CURLcode pop3_perform_apop(struct connectdata *conn)
497 {
498   CURLcode result = CURLE_OK;
499   struct pop3_conn *pop3c = &conn->proto.pop3c;
500   size_t i;
501   MD5_context *ctxt;
502   unsigned char digest[MD5_DIGEST_LEN];
503   char secret[2 * MD5_DIGEST_LEN + 1];
504
505   /* Check we have a username and password to authenticate with and end the
506      connect phase if we don't */
507   if(!conn->bits.user_passwd) {
508     state(conn, POP3_STOP);
509
510     return result;
511   }
512
513   /* Create the digest */
514   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
515   if(!ctxt)
516     return CURLE_OUT_OF_MEMORY;
517
518   Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
519                   curlx_uztoui(strlen(pop3c->apoptimestamp)));
520
521   Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
522                   curlx_uztoui(strlen(conn->passwd)));
523
524   /* Finalise the digest */
525   Curl_MD5_final(ctxt, digest);
526
527   /* Convert the calculated 16 octet digest into a 32 byte hex string */
528   for(i = 0; i < MD5_DIGEST_LEN; i++)
529     snprintf(&secret[2 * i], 3, "%02x", digest[i]);
530
531   result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
532
533   if(!result)
534     state(conn, POP3_APOP);
535
536   return result;
537 }
538 #endif
539
540 /***********************************************************************
541  *
542  * pop3_perform_authenticate()
543  *
544  * Sends an AUTH command allowing the client to login with the appropriate
545  * SASL authentication mechanism.
546  *
547  * Additionally, the function will perform fallback to APOP and USER commands
548  * should a common mechanism not be available between the client and server.
549  */
550 static CURLcode pop3_perform_authenticate(struct connectdata *conn)
551 {
552   CURLcode result = CURLE_OK;
553   struct SessionHandle *data = conn->data;
554   struct pop3_conn *pop3c = &conn->proto.pop3c;
555   const char *mech = NULL;
556   char *initresp = NULL;
557   size_t len = 0;
558   pop3state state1 = POP3_STOP;
559   pop3state state2 = POP3_STOP;
560
561   /* Check we have a username and password to authenticate with and end the
562      connect phase if we don't */
563   if(!conn->bits.user_passwd) {
564     state(conn, POP3_STOP);
565
566     return result;
567   }
568
569   /* Calculate the supported authentication mechanism by decreasing order of
570      security */
571   if(pop3c->authtypes & POP3_TYPE_SASL) {
572 #ifndef CURL_DISABLE_CRYPTO_AUTH
573     if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) &&
574        (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) {
575       mech = "DIGEST-MD5";
576       state1 = POP3_AUTH_DIGESTMD5;
577       pop3c->authused = SASL_MECH_DIGEST_MD5;
578     }
579     else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) &&
580             (pop3c->prefmech & SASL_MECH_CRAM_MD5)) {
581       mech = "CRAM-MD5";
582       state1 = POP3_AUTH_CRAMMD5;
583       pop3c->authused = SASL_MECH_CRAM_MD5;
584     }
585     else
586 #endif
587 #ifdef USE_NTLM
588     if((pop3c->authmechs & SASL_MECH_NTLM) &&
589        (pop3c->prefmech & SASL_MECH_NTLM)) {
590       mech = "NTLM";
591       state1 = POP3_AUTH_NTLM;
592       state2 = POP3_AUTH_NTLM_TYPE2MSG;
593       pop3c->authused = SASL_MECH_NTLM;
594
595       if(data->set.sasl_ir)
596         result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
597                                                      &conn->ntlm,
598                                                      &initresp, &len);
599     }
600     else
601 #endif
602     if((pop3c->authmechs & SASL_MECH_LOGIN) &&
603        (pop3c->prefmech & SASL_MECH_LOGIN)) {
604       mech = "LOGIN";
605       state1 = POP3_AUTH_LOGIN;
606       state2 = POP3_AUTH_LOGIN_PASSWD;
607       pop3c->authused = SASL_MECH_LOGIN;
608
609       if(data->set.sasl_ir)
610         result = Curl_sasl_create_login_message(conn->data, conn->user,
611                                                 &initresp, &len);
612     }
613     else if((pop3c->authmechs & SASL_MECH_PLAIN) &&
614             (pop3c->prefmech & SASL_MECH_PLAIN)) {
615       mech = "PLAIN";
616       state1 = POP3_AUTH_PLAIN;
617       state2 = POP3_AUTH_FINAL;
618       pop3c->authused = SASL_MECH_PLAIN;
619
620       if(data->set.sasl_ir)
621         result = Curl_sasl_create_plain_message(conn->data, conn->user,
622                                                 conn->passwd, &initresp,
623                                                 &len);
624     }
625   }
626
627   if(!result) {
628     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,
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, "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, "");
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 is not. When called as
1425  * a part of the easy interface, it will always be TRUE.
1426  */
1427 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1428 {
1429   CURLcode result = CURLE_OK;
1430   struct pop3_conn *pop3c = &conn->proto.pop3c;
1431   struct pingpong *pp = &pop3c->pp;
1432
1433   *done = FALSE; /* default to not done yet */
1434
1435   /* If there already is a protocol-specific struct allocated for this
1436      sessionhandle, deal with it */
1437   Curl_reset_reqproto(conn);
1438
1439   /* Initialise the POP3 layer */
1440   result = pop3_init(conn);
1441   if(result)
1442     return result;
1443
1444   /* We always support persistent connections in POP3 */
1445   conn->bits.close = FALSE;
1446
1447   /* Set the default response time-out */
1448   pp->response_time = RESP_TIMEOUT;
1449   pp->statemach_act = pop3_statemach_act;
1450   pp->endofresp = pop3_endofresp;
1451   pp->conn = conn;
1452
1453   /* Set the default preferred authentication type and mechanism */
1454   pop3c->preftype = POP3_TYPE_ANY;
1455   pop3c->prefmech = SASL_AUTH_ANY;
1456
1457   /* Initialise the pingpong layer */
1458   Curl_pp_init(pp);
1459
1460   /* Parse the URL options */
1461   result = pop3_parse_url_options(conn);
1462   if(result)
1463     return result;
1464
1465   /* Start off waiting for the server greeting response */
1466   state(conn, POP3_SERVERGREET);
1467
1468   result = pop3_multi_statemach(conn, done);
1469
1470   return result;
1471 }
1472
1473 /***********************************************************************
1474  *
1475  * pop3_done()
1476  *
1477  * The DONE function. This does what needs to be done after a single DO has
1478  * performed.
1479  *
1480  * Input argument is already checked for validity.
1481  */
1482 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1483                           bool premature)
1484 {
1485   CURLcode result = CURLE_OK;
1486   struct SessionHandle *data = conn->data;
1487   struct POP3 *pop3 = data->state.proto.pop3;
1488
1489   (void)premature;
1490
1491   if(!pop3)
1492     /* When the easy handle is removed from the multi interface while libcurl
1493        is still trying to resolve the host name, the POP3 struct is not yet
1494        initialized. However, the removal action calls Curl_done() which in
1495        turn calls this function, so we simply return success. */
1496     return CURLE_OK;
1497
1498   if(status) {
1499     conn->bits.close = TRUE; /* marked for closure */
1500     result = status;         /* use the already set error code */
1501   }
1502
1503   /* Cleanup our per-request based variables */
1504   Curl_safefree(pop3->id);
1505   Curl_safefree(pop3->custom);
1506
1507   /* Clear the transfer mode for the next request */
1508   pop3->transfer = FTPTRANSFER_BODY;
1509
1510   return result;
1511 }
1512
1513 /***********************************************************************
1514  *
1515  * pop3_perform()
1516  *
1517  * This is the actual DO function for POP3. Get a message/listing according to
1518  * the options previously setup.
1519  */
1520 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1521                              bool *dophase_done)
1522 {
1523   /* This is POP3 and no proxy */
1524   CURLcode result = CURLE_OK;
1525
1526   DEBUGF(infof(conn->data, "DO phase starts\n"));
1527
1528   if(conn->data->set.opt_no_body) {
1529     /* Requested no body means no transfer */
1530     struct POP3 *pop3 = conn->data->state.proto.pop3;
1531     pop3->transfer = FTPTRANSFER_INFO;
1532   }
1533
1534   *dophase_done = FALSE; /* not done yet */
1535
1536   /* Start the first command in the DO phase */
1537   result = pop3_perform_command(conn);
1538   if(result)
1539     return result;
1540
1541   /* Run the state-machine */
1542   result = pop3_multi_statemach(conn, dophase_done);
1543
1544   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1545
1546   if(*dophase_done)
1547     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1548
1549   return result;
1550 }
1551
1552 /***********************************************************************
1553  *
1554  * pop3_do()
1555  *
1556  * This function is registered as 'curl_do' function. It decodes the path
1557  * parts etc as a wrapper to the actual DO function (pop3_perform).
1558  *
1559  * The input argument is already checked for validity.
1560  */
1561 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1562 {
1563   CURLcode result = CURLE_OK;
1564
1565   *done = FALSE; /* default to false */
1566
1567   /* Since connections can be re-used between SessionHandles, there might be a
1568      connection already existing but on a fresh SessionHandle struct. As such
1569      we make sure we have a good POP3 struct to play with. For new connections
1570      the POP3 struct is allocated and setup in the pop3_connect() function. */
1571   Curl_reset_reqproto(conn);
1572   result = pop3_init(conn);
1573   if(result)
1574     return result;
1575
1576   /* Parse the URL path */
1577   result = pop3_parse_url_path(conn);
1578   if(result)
1579     return result;
1580
1581   /* Parse the custom request */
1582   result = pop3_parse_custom_request(conn);
1583   if(result)
1584     return result;
1585
1586   result = pop3_regular_transfer(conn, done);
1587
1588   return result;
1589 }
1590
1591 /***********************************************************************
1592  *
1593  * pop3_disconnect()
1594  *
1595  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1596  * resources. BLOCKING.
1597  */
1598 static CURLcode pop3_disconnect(struct connectdata *conn,
1599                                 bool dead_connection)
1600 {
1601   struct pop3_conn *pop3c = &conn->proto.pop3c;
1602
1603   /* We cannot send quit unconditionally. If this connection is stale or
1604      bad in any way, sending quit and waiting around here will make the
1605      disconnect wait in vain and cause more problems than we need to. */
1606
1607   /* The POP3 session may or may not have been allocated/setup at this
1608      point! */
1609   if(!dead_connection && pop3c->pp.conn)
1610     if(!pop3_perform_quit(conn))
1611       (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1612
1613   /* Disconnect from the server */
1614   Curl_pp_disconnect(&pop3c->pp);
1615
1616   /* Cleanup the SASL module */
1617   Curl_sasl_cleanup(conn, pop3c->authused);
1618
1619   /* Cleanup our connection based variables */
1620   Curl_safefree(pop3c->apoptimestamp);
1621
1622   return CURLE_OK;
1623 }
1624
1625 /* Call this when the DO phase has completed */
1626 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1627 {
1628   (void)conn;
1629   (void)connected;
1630
1631   return CURLE_OK;
1632 }
1633
1634 /* Called from multi.c while DOing */
1635 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1636 {
1637   CURLcode result = pop3_multi_statemach(conn, dophase_done);
1638
1639   if(result)
1640     DEBUGF(infof(conn->data, "DO phase failed\n"));
1641   else if(*dophase_done) {
1642     result = pop3_dophase_done(conn, FALSE /* not connected */);
1643
1644     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1645   }
1646
1647   return result;
1648 }
1649
1650 /***********************************************************************
1651  *
1652  * pop3_regular_transfer()
1653  *
1654  * The input argument is already checked for validity.
1655  *
1656  * Performs all commands done before a regular transfer between a local and a
1657  * remote host.
1658  */
1659 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1660                                       bool *dophase_done)
1661 {
1662   CURLcode result = CURLE_OK;
1663   bool connected = FALSE;
1664   struct SessionHandle *data = conn->data;
1665
1666   /* Make sure size is unknown at this point */
1667   data->req.size = -1;
1668
1669   /* Set the progress data */
1670   Curl_pgrsSetUploadCounter(data, 0);
1671   Curl_pgrsSetDownloadCounter(data, 0);
1672   Curl_pgrsSetUploadSize(data, 0);
1673   Curl_pgrsSetDownloadSize(data, 0);
1674
1675   /* Carry out the perform */
1676   result = pop3_perform(conn, &connected, dophase_done);
1677
1678   /* Perform post DO phase operations if necessary */
1679   if(!result && *dophase_done)
1680     result = pop3_dophase_done(conn, connected);
1681
1682   return result;
1683 }
1684
1685 static CURLcode pop3_setup_connection(struct connectdata *conn)
1686 {
1687   struct SessionHandle *data = conn->data;
1688
1689   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1690     /* Unless we have asked to tunnel POP3 operations through the proxy, we
1691        switch and use HTTP operations only */
1692 #ifndef CURL_DISABLE_HTTP
1693     if(conn->handler == &Curl_handler_pop3)
1694       conn->handler = &Curl_handler_pop3_proxy;
1695     else {
1696 #ifdef USE_SSL
1697       conn->handler = &Curl_handler_pop3s_proxy;
1698 #else
1699       failf(data, "POP3S not supported!");
1700       return CURLE_UNSUPPORTED_PROTOCOL;
1701 #endif
1702     }
1703
1704     /* We explicitly mark this connection as persistent here as we're doing
1705        POP3 over HTTP and thus we accidentally avoid setting this value
1706        otherwise */
1707     conn->bits.close = FALSE;
1708 #else
1709     failf(data, "POP3 over http proxy requires HTTP support built-in!");
1710     return CURLE_UNSUPPORTED_PROTOCOL;
1711 #endif
1712   }
1713
1714   data->state.path++;   /* don't include the initial slash */
1715
1716   return CURLE_OK;
1717 }
1718
1719 /***********************************************************************
1720  *
1721  * pop3_parse_url_options()
1722  *
1723  * Parse the URL login options.
1724  */
1725 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1726 {
1727   CURLcode result = CURLE_OK;
1728   struct pop3_conn *pop3c = &conn->proto.pop3c;
1729   const char *options = conn->options;
1730   const char *ptr = options;
1731
1732   if(options) {
1733     const char *key = ptr;
1734
1735     while(*ptr && *ptr != '=')
1736         ptr++;
1737
1738     if(strnequal(key, "AUTH", 4)) {
1739       const char *value = ptr + 1;
1740
1741       if(strequal(value, "*")) {
1742         pop3c->preftype = POP3_TYPE_ANY;
1743         pop3c->prefmech = SASL_AUTH_ANY;
1744       }
1745       else if(strequal(value, "+APOP")) {
1746         pop3c->preftype = POP3_TYPE_APOP;
1747         pop3c->prefmech = SASL_AUTH_NONE;
1748       }
1749       else if(strequal(value, "LOGIN")) {
1750         pop3c->preftype = POP3_TYPE_SASL;
1751         pop3c->prefmech = SASL_MECH_LOGIN;
1752       }
1753       else if(strequal(value, "PLAIN")) {
1754         pop3c->preftype = POP3_TYPE_SASL;
1755         pop3c->prefmech = SASL_MECH_PLAIN;
1756       }
1757       else if(strequal(value, "CRAM-MD5")) {
1758         pop3c->preftype = POP3_TYPE_SASL;
1759         pop3c->prefmech = SASL_MECH_CRAM_MD5;
1760       }
1761       else if(strequal(value, "DIGEST-MD5")) {
1762         pop3c->preftype = POP3_TYPE_SASL;
1763         pop3c->prefmech = SASL_MECH_DIGEST_MD5;
1764       }
1765       else if(strequal(value, "GSSAPI")) {
1766         pop3c->preftype = POP3_TYPE_SASL;
1767         pop3c->prefmech = SASL_MECH_GSSAPI;
1768       }
1769       else if(strequal(value, "NTLM")) {
1770         pop3c->preftype = POP3_TYPE_SASL;
1771         pop3c->prefmech = SASL_MECH_NTLM;
1772       }
1773       else {
1774         pop3c->preftype = POP3_TYPE_NONE;
1775         pop3c->prefmech = SASL_AUTH_NONE;
1776       }
1777     }
1778     else
1779       result = CURLE_URL_MALFORMAT;
1780   }
1781
1782   return result;
1783 }
1784
1785 /***********************************************************************
1786  *
1787  * pop3_parse_url_path()
1788  *
1789  * Parse the URL path into separate path components.
1790  */
1791 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1792 {
1793   /* The POP3 struct is already initialised in pop3_connect() */
1794   struct SessionHandle *data = conn->data;
1795   struct POP3 *pop3 = data->state.proto.pop3;
1796   const char *path = data->state.path;
1797
1798   /* URL decode the path for the message ID */
1799   return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1800 }
1801
1802 /***********************************************************************
1803  *
1804  * pop3_parse_custom_request()
1805  *
1806  * Parse the custom request.
1807  */
1808 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1809 {
1810   CURLcode result = CURLE_OK;
1811   struct SessionHandle *data = conn->data;
1812   struct POP3 *pop3 = data->state.proto.pop3;
1813   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1814
1815   /* URL decode the custom request */
1816   if(custom)
1817     result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1818
1819   return result;
1820 }
1821
1822 /***********************************************************************
1823  *
1824  * Curl_pop3_write()
1825  *
1826  * This function scans the body after the end-of-body and writes everything
1827  * until the end is found.
1828  */
1829 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1830 {
1831   /* This code could be made into a special function in the handler struct */
1832   CURLcode result = CURLE_OK;
1833   struct SessionHandle *data = conn->data;
1834   struct SingleRequest *k = &data->req;
1835
1836   struct pop3_conn *pop3c = &conn->proto.pop3c;
1837   bool strip_dot = FALSE;
1838   size_t last = 0;
1839   size_t i;
1840
1841   /* Search through the buffer looking for the end-of-body marker which is
1842      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1843      the eob so the server will have prefixed it with an extra dot which we
1844      need to strip out. Additionally the marker could of course be spread out
1845      over 5 different data chunks. */
1846   for(i = 0; i < nread; i++) {
1847     size_t prev = pop3c->eob;
1848
1849     switch(str[i]) {
1850     case 0x0d:
1851       if(pop3c->eob == 0) {
1852         pop3c->eob++;
1853
1854         if(i) {
1855           /* Write out the body part that didn't match */
1856           result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1857                                      i - last);
1858
1859           if(result)
1860             return result;
1861
1862           last = i;
1863         }
1864       }
1865       else if(pop3c->eob == 3)
1866         pop3c->eob++;
1867       else
1868         /* If the character match wasn't at position 0 or 3 then restart the
1869            pattern matching */
1870         pop3c->eob = 1;
1871       break;
1872
1873     case 0x0a:
1874       if(pop3c->eob == 1 || pop3c->eob == 4)
1875         pop3c->eob++;
1876       else
1877         /* If the character match wasn't at position 1 or 4 then start the
1878            search again */
1879         pop3c->eob = 0;
1880       break;
1881
1882     case 0x2e:
1883       if(pop3c->eob == 2)
1884         pop3c->eob++;
1885       else if(pop3c->eob == 3) {
1886         /* We have an extra dot after the CRLF which we need to strip off */
1887         strip_dot = TRUE;
1888         pop3c->eob = 0;
1889       }
1890       else
1891         /* If the character match wasn't at position 2 then start the search
1892            again */
1893         pop3c->eob = 0;
1894       break;
1895
1896     default:
1897       pop3c->eob = 0;
1898       break;
1899     }
1900
1901     /* Did we have a partial match which has subsequently failed? */
1902     if(prev && prev >= pop3c->eob) {
1903       /* Strip can only be non-zero for the very first mismatch after CRLF
1904          and then both prev and strip are equal and nothing will be output
1905          below */
1906       while(prev && pop3c->strip) {
1907         prev--;
1908         pop3c->strip--;
1909       }
1910
1911       if(prev) {
1912         /* If the partial match was the CRLF and dot then only write the CRLF
1913            as the server would have inserted the dot */
1914         result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
1915                                    strip_dot ? prev - 1 : prev);
1916
1917         if(result)
1918           return result;
1919
1920         last = i;
1921         strip_dot = FALSE;
1922       }
1923     }
1924   }
1925
1926   if(pop3c->eob == POP3_EOB_LEN) {
1927     /* We have a full match so the transfer is done, however we must transfer
1928     the CRLF at the start of the EOB as this is considered to be part of the
1929     message as per RFC-1939, sect. 3 */
1930     result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1931
1932     k->keepon &= ~KEEP_RECV;
1933     pop3c->eob = 0;
1934
1935     return result;
1936   }
1937
1938   if(pop3c->eob)
1939     /* While EOB is matching nothing should be output */
1940     return CURLE_OK;
1941
1942   if(nread - last) {
1943     result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1944                                nread - last);
1945   }
1946
1947   return result;
1948 }
1949
1950 #endif /* CURL_DISABLE_POP3 */