Base code merged to SPIN 2.4
[platform/upstream/curl.git] / lib / pop3.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, 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  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
31  * RFC5034 POP3 SASL Authentication Mechanism
32  * RFC6749 OAuth 2.0 Authorization Framework
33  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
34  *
35  ***************************************************************************/
36
37 #include "curl_setup.h"
38
39 #ifndef CURL_DISABLE_POP3
40
41 #ifdef HAVE_NETINET_IN_H
42 #include <netinet/in.h>
43 #endif
44 #ifdef HAVE_ARPA_INET_H
45 #include <arpa/inet.h>
46 #endif
47 #ifdef HAVE_UTSNAME_H
48 #include <sys/utsname.h>
49 #endif
50 #ifdef HAVE_NETDB_H
51 #include <netdb.h>
52 #endif
53 #ifdef __VMS
54 #include <in.h>
55 #include <inet.h>
56 #endif
57
58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59 #undef in_addr_t
60 #define in_addr_t unsigned long
61 #endif
62
63 #include <curl/curl.h>
64 #include "urldata.h"
65 #include "sendf.h"
66 #include "hostip.h"
67 #include "progress.h"
68 #include "transfer.h"
69 #include "escape.h"
70 #include "http.h" /* for HTTP proxy tunnel stuff */
71 #include "socks.h"
72 #include "pop3.h"
73
74 #include "strtoofft.h"
75 #include "strequal.h"
76 #include "vtls/vtls.h"
77 #include "connect.h"
78 #include "strerror.h"
79 #include "select.h"
80 #include "multiif.h"
81 #include "url.h"
82 #include "rawstr.h"
83 #include "curl_sasl.h"
84 #include "curl_md5.h"
85 #include "warnless.h"
86
87 #define _MPRINTF_REPLACE /* use our functions only */
88 #include <curl/mprintf.h>
89
90 #include "curl_memory.h"
91 /* The last #include file should be: */
92 #include "memdebug.h"
93
94 /* Local API functions */
95 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
96 static CURLcode pop3_do(struct connectdata *conn, bool *done);
97 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
98                           bool premature);
99 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
100 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
101 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
102 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
103                         int numsocks);
104 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
105 static CURLcode pop3_setup_connection(struct connectdata *conn);
106 static CURLcode pop3_parse_url_options(struct connectdata *conn);
107 static CURLcode pop3_parse_url_path(struct connectdata *conn);
108 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
109 static CURLcode pop3_calc_sasl_details(struct connectdata *conn,
110                                        const char **mech,
111                                        char **initresp, size_t *len,
112                                        pop3state *state1, pop3state *state2);
113
114 /*
115  * POP3 protocol handler.
116  */
117
118 const struct Curl_handler Curl_handler_pop3 = {
119   "POP3",                           /* scheme */
120   pop3_setup_connection,            /* setup_connection */
121   pop3_do,                          /* do_it */
122   pop3_done,                        /* done */
123   ZERO_NULL,                        /* do_more */
124   pop3_connect,                     /* connect_it */
125   pop3_multi_statemach,             /* connecting */
126   pop3_doing,                       /* doing */
127   pop3_getsock,                     /* proto_getsock */
128   pop3_getsock,                     /* doing_getsock */
129   ZERO_NULL,                        /* domore_getsock */
130   ZERO_NULL,                        /* perform_getsock */
131   pop3_disconnect,                  /* disconnect */
132   ZERO_NULL,                        /* readwrite */
133   PORT_POP3,                        /* defport */
134   CURLPROTO_POP3,                   /* protocol */
135   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
136 };
137
138 #ifdef USE_SSL
139 /*
140  * POP3S protocol handler.
141  */
142
143 const struct Curl_handler Curl_handler_pop3s = {
144   "POP3S",                          /* scheme */
145   pop3_setup_connection,            /* setup_connection */
146   pop3_do,                          /* do_it */
147   pop3_done,                        /* done */
148   ZERO_NULL,                        /* do_more */
149   pop3_connect,                     /* connect_it */
150   pop3_multi_statemach,             /* connecting */
151   pop3_doing,                       /* doing */
152   pop3_getsock,                     /* proto_getsock */
153   pop3_getsock,                     /* doing_getsock */
154   ZERO_NULL,                        /* domore_getsock */
155   ZERO_NULL,                        /* perform_getsock */
156   pop3_disconnect,                  /* disconnect */
157   ZERO_NULL,                        /* readwrite */
158   PORT_POP3S,                       /* defport */
159   CURLPROTO_POP3S,                  /* protocol */
160   PROTOPT_CLOSEACTION | PROTOPT_SSL
161   | PROTOPT_NOURLQUERY              /* flags */
162 };
163 #endif
164
165 #ifndef CURL_DISABLE_HTTP
166 /*
167  * HTTP-proxyed POP3 protocol handler.
168  */
169
170 static const struct Curl_handler Curl_handler_pop3_proxy = {
171   "POP3",                               /* scheme */
172   Curl_http_setup_conn,                 /* setup_connection */
173   Curl_http,                            /* do_it */
174   Curl_http_done,                       /* done */
175   ZERO_NULL,                            /* do_more */
176   ZERO_NULL,                            /* connect_it */
177   ZERO_NULL,                            /* connecting */
178   ZERO_NULL,                            /* doing */
179   ZERO_NULL,                            /* proto_getsock */
180   ZERO_NULL,                            /* doing_getsock */
181   ZERO_NULL,                            /* domore_getsock */
182   ZERO_NULL,                            /* perform_getsock */
183   ZERO_NULL,                            /* disconnect */
184   ZERO_NULL,                            /* readwrite */
185   PORT_POP3,                            /* defport */
186   CURLPROTO_HTTP,                       /* protocol */
187   PROTOPT_NONE                          /* flags */
188 };
189
190 #ifdef USE_SSL
191 /*
192  * HTTP-proxyed POP3S protocol handler.
193  */
194
195 static const struct Curl_handler Curl_handler_pop3s_proxy = {
196   "POP3S",                              /* scheme */
197   Curl_http_setup_conn,                 /* setup_connection */
198   Curl_http,                            /* do_it */
199   Curl_http_done,                       /* done */
200   ZERO_NULL,                            /* do_more */
201   ZERO_NULL,                            /* connect_it */
202   ZERO_NULL,                            /* connecting */
203   ZERO_NULL,                            /* doing */
204   ZERO_NULL,                            /* proto_getsock */
205   ZERO_NULL,                            /* doing_getsock */
206   ZERO_NULL,                            /* domore_getsock */
207   ZERO_NULL,                            /* perform_getsock */
208   ZERO_NULL,                            /* disconnect */
209   ZERO_NULL,                            /* readwrite */
210   PORT_POP3S,                           /* defport */
211   CURLPROTO_HTTP,                       /* protocol */
212   PROTOPT_NONE                          /* flags */
213 };
214 #endif
215 #endif
216
217 #ifdef USE_SSL
218 static void pop3_to_pop3s(struct connectdata *conn)
219 {
220   conn->handler = &Curl_handler_pop3s;
221 }
222 #else
223 #define pop3_to_pop3s(x) Curl_nop_stmt
224 #endif
225
226 /***********************************************************************
227  *
228  * pop3_endofresp()
229  *
230  * Checks for an ending POP3 status code at the start of the given string, but
231  * also detects the APOP timestamp from the server greeting and various
232  * capabilities from the CAPA response including the supported authentication
233  * types and allowed SASL mechanisms.
234  */
235 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
236                            int *resp)
237 {
238   struct pop3_conn *pop3c = &conn->proto.pop3c;
239
240   /* Do we have an error response? */
241   if(len >= 4 && !memcmp("-ERR", line, 4)) {
242     *resp = '-';
243
244     return TRUE;
245   }
246
247   /* Are we processing CAPA command responses? */
248   if(pop3c->state == POP3_CAPA) {
249     /* Do we have the terminating line? */
250     if(len >= 1 && !memcmp(line, ".", 1))
251       *resp = '+';
252     else
253       *resp = '*';
254
255     return TRUE;
256   }
257
258   /* Do we have a command or continuation response? */
259   if((len >= 3 && !memcmp("+OK", line, 3)) ||
260      (len >= 1 && !memcmp("+", line, 1))) {
261     *resp = '+';
262
263     return TRUE;
264   }
265
266   return FALSE; /* Nothing for us */
267 }
268
269 /***********************************************************************
270  *
271  * pop3_get_message()
272  *
273  * Gets the authentication message from the response buffer.
274  */
275 static void pop3_get_message(char *buffer, char** outptr)
276 {
277   size_t len = 0;
278   char* message = NULL;
279
280   /* Find the start of the message */
281   for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
282     ;
283
284   /* Find the end of the message */
285   for(len = strlen(message); len--;)
286     if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
287         message[len] != '\t')
288       break;
289
290   /* Terminate the message */
291   if(++len) {
292     message[len] = '\0';
293   }
294
295   *outptr = message;
296 }
297
298 /***********************************************************************
299  *
300  * state()
301  *
302  * This is the ONLY way to change POP3 state!
303  */
304 static void state(struct connectdata *conn, pop3state newstate)
305 {
306   struct pop3_conn *pop3c = &conn->proto.pop3c;
307 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
308   /* for debug purposes */
309   static const char * const names[] = {
310     "STOP",
311     "SERVERGREET",
312     "CAPA",
313     "STARTTLS",
314     "UPGRADETLS",
315     "AUTH_PLAIN",
316     "AUTH_LOGIN",
317     "AUTH_LOGIN_PASSWD",
318     "AUTH_CRAMMD5",
319     "AUTH_DIGESTMD5",
320     "AUTH_DIGESTMD5_RESP",
321     "AUTH_NTLM",
322     "AUTH_NTLM_TYPE2MSG",
323     "AUTH_GSSAPI",
324     "AUTH_GSSAPI_TOKEN",
325     "AUTH_GSSAPI_NO_DATA",
326     "AUTH_XOAUTH2",
327     "AUTH_CANCEL",
328     "AUTH_FINAL",
329     "APOP",
330     "USER",
331     "PASS",
332     "COMMAND",
333     "QUIT",
334     /* LAST */
335   };
336
337   if(pop3c->state != newstate)
338     infof(conn->data, "POP3 %p state change from %s to %s\n",
339           (void *)pop3c, names[pop3c->state], names[newstate]);
340 #endif
341
342   pop3c->state = newstate;
343 }
344
345 /***********************************************************************
346  *
347  * pop3_perform_capa()
348  *
349  * Sends the CAPA command in order to obtain a list of server side supported
350  * capabilities.
351  */
352 static CURLcode pop3_perform_capa(struct connectdata *conn)
353 {
354   CURLcode result = CURLE_OK;
355   struct pop3_conn *pop3c = &conn->proto.pop3c;
356
357   pop3c->authmechs = 0;         /* No known authentication mechanisms yet */
358   pop3c->authused = 0;          /* Clear the authentication mechanism used */
359   pop3c->tls_supported = FALSE; /* Clear the TLS capability */
360
361   /* Send the CAPA command */
362   result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA");
363
364   if(!result)
365     state(conn, POP3_CAPA);
366
367   return result;
368 }
369
370 /***********************************************************************
371  *
372  * pop3_perform_starttls()
373  *
374  * Sends the STLS command to start the upgrade to TLS.
375  */
376 static CURLcode pop3_perform_starttls(struct connectdata *conn)
377 {
378   CURLcode result = CURLE_OK;
379
380   /* Send the STLS command */
381   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS");
382
383   if(!result)
384     state(conn, POP3_STARTTLS);
385
386   return result;
387 }
388
389 /***********************************************************************
390  *
391  * pop3_perform_upgrade_tls()
392  *
393  * Performs the upgrade to TLS.
394  */
395 static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
396 {
397   CURLcode result = CURLE_OK;
398   struct pop3_conn *pop3c = &conn->proto.pop3c;
399
400   /* Start the SSL connection */
401   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
402
403   if(!result) {
404     if(pop3c->state != POP3_UPGRADETLS)
405       state(conn, POP3_UPGRADETLS);
406
407     if(pop3c->ssldone) {
408       pop3_to_pop3s(conn);
409       result = pop3_perform_capa(conn);
410     }
411   }
412
413   return result;
414 }
415
416 /***********************************************************************
417  *
418  * pop3_perform_user()
419  *
420  * Sends a clear text USER command to authenticate with.
421  */
422 static CURLcode pop3_perform_user(struct connectdata *conn)
423 {
424   CURLcode result = CURLE_OK;
425
426   /* Check we have a username and password to authenticate with and end the
427      connect phase if we don't */
428   if(!conn->bits.user_passwd) {
429     state(conn, POP3_STOP);
430
431     return result;
432   }
433
434   /* Send the USER command */
435   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
436                          conn->user ? conn->user : "");
437   if(!result)
438     state(conn, POP3_USER);
439
440   return result;
441 }
442
443 #ifndef CURL_DISABLE_CRYPTO_AUTH
444 /***********************************************************************
445  *
446  * pop3_perform_apop()
447  *
448  * Sends an APOP command to authenticate with.
449  */
450 static CURLcode pop3_perform_apop(struct connectdata *conn)
451 {
452   CURLcode result = CURLE_OK;
453   struct pop3_conn *pop3c = &conn->proto.pop3c;
454   size_t i;
455   MD5_context *ctxt;
456   unsigned char digest[MD5_DIGEST_LEN];
457   char secret[2 * MD5_DIGEST_LEN + 1];
458
459   /* Check we have a username and password to authenticate with and end the
460      connect phase if we don't */
461   if(!conn->bits.user_passwd) {
462     state(conn, POP3_STOP);
463
464     return result;
465   }
466
467   /* Create the digest */
468   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
469   if(!ctxt)
470     return CURLE_OUT_OF_MEMORY;
471
472   Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
473                   curlx_uztoui(strlen(pop3c->apoptimestamp)));
474
475   Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
476                   curlx_uztoui(strlen(conn->passwd)));
477
478   /* Finalise the digest */
479   Curl_MD5_final(ctxt, digest);
480
481   /* Convert the calculated 16 octet digest into a 32 byte hex string */
482   for(i = 0; i < MD5_DIGEST_LEN; i++)
483     snprintf(&secret[2 * i], 3, "%02x", digest[i]);
484
485   result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
486
487   if(!result)
488     state(conn, POP3_APOP);
489
490   return result;
491 }
492 #endif
493
494 /***********************************************************************
495  *
496  * pop3_perform_auth()
497  *
498  * Sends an AUTH command allowing the client to login with the given SASL
499  * authentication mechanism.
500  */
501 static CURLcode pop3_perform_auth(struct connectdata *conn,
502                                   const char *mech,
503                                   const char *initresp, size_t len,
504                                   pop3state state1, pop3state state2)
505 {
506   CURLcode result = CURLE_OK;
507   struct pop3_conn *pop3c = &conn->proto.pop3c;
508
509   if(initresp && 8 + strlen(mech) + len <= 255) { /* AUTH <mech> ...<crlf> */
510     /* Send the AUTH command with the initial response */
511     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
512
513     if(!result)
514       state(conn, state2);
515   }
516   else {
517     /* Send the AUTH command */
518     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
519
520     if(!result)
521       state(conn, state1);
522   }
523
524   return result;
525 }
526
527 /***********************************************************************
528  *
529  * pop3_perform_authentication()
530  *
531  * Initiates the authentication sequence, with the appropriate SASL
532  * authentication mechanism, falling back to APOP and clear text should a
533  * common mechanism not be available between the client and server.
534  */
535 static CURLcode pop3_perform_authentication(struct connectdata *conn)
536 {
537   CURLcode result = CURLE_OK;
538   struct pop3_conn *pop3c = &conn->proto.pop3c;
539   const char *mech = NULL;
540   char *initresp = NULL;
541   size_t len = 0;
542   pop3state state1 = POP3_STOP;
543   pop3state state2 = POP3_STOP;
544
545   /* Check we have a username and password to authenticate with and end the
546      connect phase if we don't */
547   if(!conn->bits.user_passwd) {
548     state(conn, POP3_STOP);
549
550     return result;
551   }
552
553   /* Calculate the SASL login details */
554   if(pop3c->authtypes & POP3_TYPE_SASL)
555     result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
556                                     &state2);
557
558   if(!result) {
559     if(mech && (pop3c->preftype & POP3_TYPE_SASL)) {
560       /* Perform SASL based authentication */
561       result = pop3_perform_auth(conn, mech, initresp, len, state1, state2);
562     }
563 #ifndef CURL_DISABLE_CRYPTO_AUTH
564     else if((pop3c->authtypes & POP3_TYPE_APOP) &&
565             (pop3c->preftype & POP3_TYPE_APOP))
566       /* Perform APOP authentication */
567       result = pop3_perform_apop(conn);
568 #endif
569     else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
570             (pop3c->preftype & POP3_TYPE_CLEARTEXT))
571       /* Perform clear text authentication */
572       result = pop3_perform_user(conn);
573     else {
574       /* Other mechanisms not supported */
575       infof(conn->data, "No known authentication mechanisms supported!\n");
576       result = CURLE_LOGIN_DENIED;
577     }
578   }
579
580   Curl_safefree(initresp);
581
582   return result;
583 }
584
585 /***********************************************************************
586  *
587  * pop3_perform_command()
588  *
589  * Sends a POP3 based command.
590  */
591 static CURLcode pop3_perform_command(struct connectdata *conn)
592 {
593   CURLcode result = CURLE_OK;
594   struct SessionHandle *data = conn->data;
595   struct POP3 *pop3 = data->req.protop;
596   const char *command = NULL;
597
598   /* Calculate the default command */
599   if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
600     command = "LIST";
601
602     if(pop3->id[0] != '\0')
603       /* Message specific LIST so skip the BODY transfer */
604       pop3->transfer = FTPTRANSFER_INFO;
605   }
606   else
607     command = "RETR";
608
609   /* Send the command */
610   if(pop3->id[0] != '\0')
611     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
612                            (pop3->custom && pop3->custom[0] != '\0' ?
613                             pop3->custom : command), pop3->id);
614   else
615     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s",
616                            (pop3->custom && pop3->custom[0] != '\0' ?
617                             pop3->custom : command));
618
619   if(!result)
620     state(conn, POP3_COMMAND);
621
622   return result;
623 }
624
625 /***********************************************************************
626  *
627  * pop3_perform_quit()
628  *
629  * Performs the quit action prior to sclose() be called.
630  */
631 static CURLcode pop3_perform_quit(struct connectdata *conn)
632 {
633   CURLcode result = CURLE_OK;
634
635   /* Send the QUIT command */
636   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT");
637
638   if(!result)
639     state(conn, POP3_QUIT);
640
641   return result;
642 }
643
644 /* For the initial server greeting */
645 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
646                                             int pop3code,
647                                             pop3state instate)
648 {
649   CURLcode result = CURLE_OK;
650   struct SessionHandle *data = conn->data;
651   struct pop3_conn *pop3c = &conn->proto.pop3c;
652   const char *line = data->state.buffer;
653   size_t len = strlen(line);
654   size_t i;
655
656   (void)instate; /* no use for this yet */
657
658   if(pop3code != '+') {
659     failf(data, "Got unexpected pop3-server response");
660     result = CURLE_FTP_WEIRD_SERVER_REPLY;
661   }
662   else {
663     /* Does the server support APOP authentication? */
664     if(len >= 4 && line[len - 2] == '>') {
665       /* Look for the APOP timestamp */
666       for(i = 3; i < len - 2; ++i) {
667         if(line[i] == '<') {
668           /* Calculate the length of the timestamp */
669           size_t timestamplen = len - 1 - i;
670           if(!timestamplen)
671             break;
672
673           /* Allocate some memory for the timestamp */
674           pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
675
676           if(!pop3c->apoptimestamp)
677             break;
678
679           /* Copy the timestamp */
680           memcpy(pop3c->apoptimestamp, line + i, timestamplen);
681           pop3c->apoptimestamp[timestamplen] = '\0';
682
683           /* Store the APOP capability */
684           pop3c->authtypes |= POP3_TYPE_APOP;
685           break;
686         }
687       }
688     }
689
690     result = pop3_perform_capa(conn);
691   }
692
693   return result;
694 }
695
696 /* For CAPA responses */
697 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
698                                      pop3state instate)
699 {
700   CURLcode result = CURLE_OK;
701   struct SessionHandle *data = conn->data;
702   struct pop3_conn *pop3c = &conn->proto.pop3c;
703   const char *line = data->state.buffer;
704   size_t len = strlen(line);
705   size_t wordlen;
706
707   (void)instate; /* no use for this yet */
708
709   /* Do we have a untagged response? */
710   if(pop3code == '*') {
711     /* Does the server support the STLS capability? */
712     if(len >= 4 && !memcmp(line, "STLS", 4))
713       pop3c->tls_supported = TRUE;
714
715     /* Does the server support clear text authentication? */
716     else if(len >= 4 && !memcmp(line, "USER", 4))
717       pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
718
719     /* Does the server support SASL based authentication? */
720     else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
721       pop3c->authtypes |= POP3_TYPE_SASL;
722
723       /* Advance past the SASL keyword */
724       line += 5;
725       len -= 5;
726
727       /* Loop through the data line */
728       for(;;) {
729         while(len &&
730               (*line == ' ' || *line == '\t' ||
731                *line == '\r' || *line == '\n')) {
732
733           line++;
734           len--;
735         }
736
737         if(!len)
738           break;
739
740         /* Extract the word */
741         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
742               line[wordlen] != '\t' && line[wordlen] != '\r' &&
743               line[wordlen] != '\n';)
744           wordlen++;
745
746         /* Test the word for a matching authentication mechanism */
747         if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
748           pop3c->authmechs |= SASL_MECH_LOGIN;
749         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
750           pop3c->authmechs |= SASL_MECH_PLAIN;
751         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
752           pop3c->authmechs |= SASL_MECH_CRAM_MD5;
753         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
754           pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
755         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
756           pop3c->authmechs |= SASL_MECH_GSSAPI;
757         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
758           pop3c->authmechs |= SASL_MECH_EXTERNAL;
759         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
760           pop3c->authmechs |= SASL_MECH_NTLM;
761         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
762           pop3c->authmechs |= SASL_MECH_XOAUTH2;
763
764         line += wordlen;
765         len -= wordlen;
766       }
767     }
768   }
769   else if(pop3code == '+') {
770     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
771       /* We don't have a SSL/TLS connection yet, but SSL is requested */
772       if(pop3c->tls_supported)
773         /* Switch to TLS connection now */
774         result = pop3_perform_starttls(conn);
775       else if(data->set.use_ssl == CURLUSESSL_TRY)
776         /* Fallback and carry on with authentication */
777         result = pop3_perform_authentication(conn);
778       else {
779         failf(data, "STLS not supported.");
780         result = CURLE_USE_SSL_FAILED;
781       }
782     }
783     else
784       result = pop3_perform_authentication(conn);
785   }
786   else {
787     /* Clear text is supported when CAPA isn't recognised */
788     pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
789
790     result = pop3_perform_authentication(conn);
791   }
792
793   return result;
794 }
795
796 /* For STARTTLS responses */
797 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
798                                          int pop3code,
799                                          pop3state instate)
800 {
801   CURLcode result = CURLE_OK;
802   struct SessionHandle *data = conn->data;
803
804   (void)instate; /* no use for this yet */
805
806   if(pop3code != '+') {
807     if(data->set.use_ssl != CURLUSESSL_TRY) {
808       failf(data, "STARTTLS denied. %c", pop3code);
809       result = CURLE_USE_SSL_FAILED;
810     }
811     else
812       result = pop3_perform_authentication(conn);
813   }
814   else
815     result = pop3_perform_upgrade_tls(conn);
816
817   return result;
818 }
819
820 /* For AUTH PLAIN (without initial response) responses */
821 static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
822                                            int pop3code,
823                                            pop3state instate)
824 {
825   CURLcode result = CURLE_OK;
826   struct SessionHandle *data = conn->data;
827   size_t len = 0;
828   char *plainauth = NULL;
829
830   (void)instate; /* no use for this yet */
831
832   if(pop3code != '+') {
833     failf(data, "Access denied. %c", pop3code);
834     result = CURLE_LOGIN_DENIED;
835   }
836   else {
837     /* Create the authorisation message */
838     result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
839                                             &plainauth, &len);
840     if(!result && plainauth) {
841       /* Send the message */
842       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
843
844       if(!result)
845         state(conn, POP3_AUTH_FINAL);
846     }
847   }
848
849   Curl_safefree(plainauth);
850
851   return result;
852 }
853
854 /* For AUTH LOGIN (without initial response) responses */
855 static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
856                                            int pop3code,
857                                            pop3state instate)
858 {
859   CURLcode result = CURLE_OK;
860   struct SessionHandle *data = conn->data;
861   size_t len = 0;
862   char *authuser = NULL;
863
864   (void)instate; /* no use for this yet */
865
866   if(pop3code != '+') {
867     failf(data, "Access denied: %d", pop3code);
868     result = CURLE_LOGIN_DENIED;
869   }
870   else {
871     /* Create the user message */
872     result = Curl_sasl_create_login_message(data, conn->user,
873                                             &authuser, &len);
874     if(!result && authuser) {
875       /* Send the user */
876       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
877
878       if(!result)
879         state(conn, POP3_AUTH_LOGIN_PASSWD);
880     }
881   }
882
883   Curl_safefree(authuser);
884
885   return result;
886 }
887
888 /* For AUTH LOGIN user entry responses */
889 static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
890                                                     int pop3code,
891                                                     pop3state instate)
892 {
893   CURLcode result = CURLE_OK;
894   struct SessionHandle *data = conn->data;
895   size_t len = 0;
896   char *authpasswd = NULL;
897
898   (void)instate; /* no use for this yet */
899
900   if(pop3code != '+') {
901     failf(data, "Access denied: %d", pop3code);
902     result = CURLE_LOGIN_DENIED;
903   }
904   else {
905     /* Create the password message */
906     result = Curl_sasl_create_login_message(data, conn->passwd,
907                                             &authpasswd, &len);
908     if(!result && authpasswd) {
909       /* Send the password */
910       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
911
912       if(!result)
913         state(conn, POP3_AUTH_FINAL);
914     }
915   }
916
917   Curl_safefree(authpasswd);
918
919   return result;
920 }
921
922 #ifndef CURL_DISABLE_CRYPTO_AUTH
923 /* For AUTH CRAM-MD5 responses */
924 static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
925                                           int pop3code,
926                                           pop3state instate)
927 {
928   CURLcode result = CURLE_OK;
929   struct SessionHandle *data = conn->data;
930   char *chlg = NULL;
931   char *chlg64 = NULL;
932   char *rplyb64 = NULL;
933   size_t len = 0;
934
935   (void)instate; /* no use for this yet */
936
937   if(pop3code != '+') {
938     failf(data, "Access denied: %d", pop3code);
939     return CURLE_LOGIN_DENIED;
940   }
941
942   /* Get the challenge message */
943   pop3_get_message(data->state.buffer, &chlg64);
944
945   /* Decode the challenge message */
946   result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
947   if(result) {
948     /* Send the cancellation */
949     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
950
951     if(!result)
952       state(conn, POP3_AUTH_CANCEL);
953   }
954   else {
955     /* Create the response message */
956     result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
957                                                conn->passwd, &rplyb64, &len);
958     if(!result && rplyb64) {
959       /* Send the response */
960       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
961
962       if(!result)
963         state(conn, POP3_AUTH_FINAL);
964     }
965   }
966
967   Curl_safefree(chlg);
968   Curl_safefree(rplyb64);
969
970   return result;
971 }
972
973 /* For AUTH DIGEST-MD5 challenge responses */
974 static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
975                                             int pop3code,
976                                             pop3state instate)
977 {
978   CURLcode result = CURLE_OK;
979   struct SessionHandle *data = conn->data;
980   char *chlg64 = NULL;
981   char *rplyb64 = NULL;
982   size_t len = 0;
983
984   (void)instate; /* no use for this yet */
985
986   if(pop3code != '+') {
987     failf(data, "Access denied: %d", pop3code);
988     return CURLE_LOGIN_DENIED;
989   }
990
991   /* Get the challenge message */
992   pop3_get_message(data->state.buffer, &chlg64);
993
994   /* Create the response message */
995   result = Curl_sasl_create_digest_md5_message(data, chlg64,
996                                                conn->user, conn->passwd,
997                                                "pop", &rplyb64, &len);
998   if(result) {
999     if(result == CURLE_BAD_CONTENT_ENCODING) {
1000       /* Send the cancellation */
1001       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
1002
1003       if(!result)
1004         state(conn, POP3_AUTH_CANCEL);
1005     }
1006   }
1007   else {
1008     /* Send the response */
1009     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
1010
1011     if(!result)
1012       state(conn, POP3_AUTH_DIGESTMD5_RESP);
1013   }
1014
1015   Curl_safefree(rplyb64);
1016
1017   return result;
1018 }
1019
1020 /* For AUTH DIGEST-MD5 challenge-response responses */
1021 static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
1022                                                  int pop3code,
1023                                                  pop3state instate)
1024 {
1025   CURLcode result = CURLE_OK;
1026   struct SessionHandle *data = conn->data;
1027
1028   (void)instate; /* no use for this yet */
1029
1030   if(pop3code != '+') {
1031     failf(data, "Authentication failed: %d", pop3code);
1032     result = CURLE_LOGIN_DENIED;
1033   }
1034   else {
1035     /* Send an empty response */
1036     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "");
1037
1038     if(!result)
1039       state(conn, POP3_AUTH_FINAL);
1040   }
1041
1042   return result;
1043 }
1044 #endif
1045
1046 #ifdef USE_NTLM
1047 /* For AUTH NTLM (without initial response) responses */
1048 static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
1049                                           int pop3code,
1050                                           pop3state instate)
1051 {
1052   CURLcode result = CURLE_OK;
1053   struct SessionHandle *data = conn->data;
1054   size_t len = 0;
1055   char *type1msg = NULL;
1056
1057   (void)instate; /* no use for this yet */
1058
1059   if(pop3code != '+') {
1060     failf(data, "Access denied: %d", pop3code);
1061     result = CURLE_LOGIN_DENIED;
1062   }
1063   else {
1064     /* Create the type-1 message */
1065     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1066                                                  &conn->ntlm,
1067                                                  &type1msg, &len);
1068     if(!result && type1msg) {
1069       /* Send the message */
1070       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
1071
1072       if(!result)
1073         state(conn, POP3_AUTH_NTLM_TYPE2MSG);
1074     }
1075   }
1076
1077   Curl_safefree(type1msg);
1078
1079   return result;
1080 }
1081
1082 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1083 static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1084                                                    int pop3code,
1085                                                    pop3state instate)
1086 {
1087   CURLcode result = CURLE_OK;
1088   struct SessionHandle *data = conn->data;
1089   char *type2msg = NULL;
1090   char *type3msg = NULL;
1091   size_t len = 0;
1092
1093   (void)instate; /* no use for this yet */
1094
1095   if(pop3code != '+') {
1096     failf(data, "Access denied: %d", pop3code);
1097     result = CURLE_LOGIN_DENIED;
1098   }
1099   else {
1100     /* Get the type-2 message */
1101     pop3_get_message(data->state.buffer, &type2msg);
1102
1103     /* Decode the type-2 message */
1104     result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1105     if(result) {
1106       /* Send the cancellation */
1107       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
1108
1109       if(!result)
1110         state(conn, POP3_AUTH_CANCEL);
1111     }
1112     else {
1113       /* Create the type-3 message */
1114       result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1115                                                    conn->passwd, &conn->ntlm,
1116                                                    &type3msg, &len);
1117       if(!result && type3msg) {
1118         /* Send the message */
1119         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
1120
1121         if(!result)
1122           state(conn, POP3_AUTH_FINAL);
1123       }
1124     }
1125   }
1126
1127   Curl_safefree(type3msg);
1128
1129   return result;
1130 }
1131 #endif
1132
1133 #if defined(USE_KERBEROS5)
1134 /* For AUTH GSSAPI (without initial response) responses */
1135 static CURLcode pop3_state_auth_gssapi_resp(struct connectdata *conn,
1136                                             int pop3code,
1137                                             pop3state instate)
1138 {
1139   CURLcode result = CURLE_OK;
1140   struct SessionHandle *data = conn->data;
1141   struct pop3_conn *pop3c = &conn->proto.pop3c;
1142   size_t len = 0;
1143   char *respmsg = NULL;
1144
1145   (void)instate; /* no use for this yet */
1146
1147   if(pop3code != '+') {
1148     failf(data, "Access denied: %d", pop3code);
1149     result = CURLE_LOGIN_DENIED;
1150   }
1151   else {
1152     /* Create the initial response message */
1153     result = Curl_sasl_create_gssapi_user_message(data, conn->user,
1154                                                   conn->passwd, "pop",
1155                                                   pop3c->mutual_auth,
1156                                                   NULL, &conn->krb5,
1157                                                   &respmsg, &len);
1158     if(!result && respmsg) {
1159       /* Send the message */
1160       result = Curl_pp_sendf(&pop3c->pp, "%s", respmsg);
1161
1162       if(!result)
1163         state(conn, POP3_AUTH_GSSAPI_TOKEN);
1164     }
1165   }
1166
1167   Curl_safefree(respmsg);
1168
1169   return result;
1170 }
1171
1172 /* For AUTH GSSAPI user token responses */
1173 static CURLcode pop3_state_auth_gssapi_token_resp(struct connectdata *conn,
1174                                                   int pop3code,
1175                                                   pop3state instate)
1176 {
1177   CURLcode result = CURLE_OK;
1178   struct SessionHandle *data = conn->data;
1179   struct pop3_conn *pop3c = &conn->proto.pop3c;
1180   char *chlgmsg = NULL;
1181   char *respmsg = NULL;
1182   size_t len = 0;
1183
1184   (void)instate; /* no use for this yet */
1185
1186   if(pop3code != '+') {
1187     failf(data, "Access denied: %d", pop3code);
1188     result = CURLE_LOGIN_DENIED;
1189   }
1190   else {
1191     /* Get the challenge message */
1192     pop3_get_message(data->state.buffer, &chlgmsg);
1193
1194     if(pop3c->mutual_auth)
1195       /* Decode the user token challenge and create the optional response
1196          message */
1197       result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
1198                                                     pop3c->mutual_auth,
1199                                                     chlgmsg, &conn->krb5,
1200                                                     &respmsg, &len);
1201     else
1202       /* Decode the security challenge and create the response message */
1203       result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
1204                                                         &conn->krb5,
1205                                                         &respmsg, &len);
1206
1207     if(result) {
1208       if(result == CURLE_BAD_CONTENT_ENCODING) {
1209         /* Send the cancellation */
1210         result = Curl_pp_sendf(&pop3c->pp, "%s", "*");
1211
1212         if(!result)
1213           state(conn, POP3_AUTH_CANCEL);
1214       }
1215     }
1216     else {
1217       /* Send the response */
1218       if(respmsg)
1219         result = Curl_pp_sendf(&pop3c->pp, "%s", respmsg);
1220       else
1221         result = Curl_pp_sendf(&pop3c->pp, "%s", "");
1222
1223       if(!result)
1224         state(conn, (pop3c->mutual_auth ? POP3_AUTH_GSSAPI_NO_DATA :
1225                                           POP3_AUTH_FINAL));
1226     }
1227   }
1228
1229   Curl_safefree(respmsg);
1230
1231   return result;
1232 }
1233
1234 /* For AUTH GSSAPI no data responses */
1235 static CURLcode pop3_state_auth_gssapi_no_data_resp(struct connectdata *conn,
1236                                                     int pop3code,
1237                                                     pop3state instate)
1238 {
1239   CURLcode result = CURLE_OK;
1240   struct SessionHandle *data = conn->data;
1241   char *chlgmsg = NULL;
1242   char *respmsg = NULL;
1243   size_t len = 0;
1244
1245   (void)instate; /* no use for this yet */
1246
1247   if(pop3code != '+') {
1248     failf(data, "Access denied: %d", pop3code);
1249     result = CURLE_LOGIN_DENIED;
1250   }
1251   else {
1252     /* Get the challenge message */
1253     pop3_get_message(data->state.buffer, &chlgmsg);
1254
1255     /* Decode the security challenge and create the security message */
1256     result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
1257                                                       &conn->krb5,
1258                                                       &respmsg, &len);
1259     if(result) {
1260       if(result == CURLE_BAD_CONTENT_ENCODING) {
1261         /* Send the cancellation */
1262         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
1263
1264         if(!result)
1265           state(conn, POP3_AUTH_CANCEL);
1266       }
1267     }
1268     else {
1269       /* Send the response */
1270       if(respmsg) {
1271         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", respmsg);
1272
1273         if(!result)
1274           state(conn, POP3_AUTH_FINAL);
1275       }
1276     }
1277   }
1278
1279   Curl_safefree(respmsg);
1280
1281   return result;
1282 }
1283 #endif
1284
1285 /* For AUTH XOAUTH2 (without initial response) responses */
1286 static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn,
1287                                              int pop3code, pop3state instate)
1288 {
1289   CURLcode result = CURLE_OK;
1290   struct SessionHandle *data = conn->data;
1291   size_t len = 0;
1292   char *xoauth = NULL;
1293
1294   (void)instate; /* no use for this yet */
1295
1296   if(pop3code != '+') {
1297     failf(data, "Access denied: %d", pop3code);
1298     result = CURLE_LOGIN_DENIED;
1299   }
1300   else {
1301     /* Create the authorisation message */
1302     result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1303                                               conn->xoauth2_bearer,
1304                                               &xoauth, &len);
1305     if(!result && xoauth) {
1306       /* Send the message */
1307       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth);
1308
1309       if(!result)
1310         state(conn, POP3_AUTH_FINAL);
1311     }
1312   }
1313
1314   Curl_safefree(xoauth);
1315
1316   return result;
1317 }
1318
1319 /* For AUTH cancellation responses */
1320 static CURLcode pop3_state_auth_cancel_resp(struct connectdata *conn,
1321                                             int pop3code,
1322                                             pop3state instate)
1323 {
1324   CURLcode result = CURLE_OK;
1325   struct SessionHandle *data = conn->data;
1326   struct pop3_conn *pop3c = &conn->proto.pop3c;
1327   const char *mech = NULL;
1328   char *initresp = NULL;
1329   size_t len = 0;
1330   pop3state state1 = POP3_STOP;
1331   pop3state state2 = POP3_STOP;
1332
1333   (void)pop3code;
1334   (void)instate; /* no use for this yet */
1335
1336   /* Remove the offending mechanism from the supported list */
1337   pop3c->authmechs ^= pop3c->authused;
1338
1339   /* Calculate alternative SASL login details */
1340   result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1341                                   &state2);
1342
1343   if(!result) {
1344     /* Do we have any mechanisms left or can we fallback to another
1345        authentication type? */
1346     if(mech) {
1347       /* Retry SASL based authentication */
1348       result = pop3_perform_auth(conn, mech, initresp, len, state1, state2);
1349
1350       Curl_safefree(initresp);
1351     }
1352 #ifndef CURL_DISABLE_CRYPTO_AUTH
1353     else if((pop3c->authtypes & POP3_TYPE_APOP) &&
1354             (pop3c->preftype & POP3_TYPE_APOP))
1355       /* Perform APOP authentication */
1356       result = pop3_perform_apop(conn);
1357 #endif
1358     else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
1359             (pop3c->preftype & POP3_TYPE_CLEARTEXT))
1360       /* Perform clear text authentication */
1361       result = pop3_perform_user(conn);
1362     else {
1363       failf(data, "Authentication cancelled");
1364
1365       result = CURLE_LOGIN_DENIED;
1366     }
1367   }
1368
1369   return result;
1370 }
1371
1372 /* For final responses in the AUTH sequence */
1373 static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
1374                                            int pop3code,
1375                                            pop3state instate)
1376 {
1377   CURLcode result = CURLE_OK;
1378   struct SessionHandle *data = conn->data;
1379
1380   (void)instate; /* no use for this yet */
1381
1382   if(pop3code != '+') {
1383     failf(data, "Authentication failed: %d", pop3code);
1384     result = CURLE_LOGIN_DENIED;
1385   }
1386   else
1387     /* End of connect phase */
1388     state(conn, POP3_STOP);
1389
1390   return result;
1391 }
1392
1393 #ifndef CURL_DISABLE_CRYPTO_AUTH
1394 /* For APOP responses */
1395 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
1396                                      pop3state instate)
1397 {
1398   CURLcode result = CURLE_OK;
1399   struct SessionHandle *data = conn->data;
1400
1401   (void)instate; /* no use for this yet */
1402
1403   if(pop3code != '+') {
1404     failf(data, "Authentication failed: %d", pop3code);
1405     result = CURLE_LOGIN_DENIED;
1406   }
1407   else
1408     /* End of connect phase */
1409     state(conn, POP3_STOP);
1410
1411   return result;
1412 }
1413 #endif
1414
1415 /* For USER responses */
1416 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
1417                                      pop3state instate)
1418 {
1419   CURLcode result = CURLE_OK;
1420   struct SessionHandle *data = conn->data;
1421
1422   (void)instate; /* no use for this yet */
1423
1424   if(pop3code != '+') {
1425     failf(data, "Access denied. %c", pop3code);
1426     result = CURLE_LOGIN_DENIED;
1427   }
1428   else
1429     /* Send the PASS command */
1430     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
1431                            conn->passwd ? conn->passwd : "");
1432   if(!result)
1433     state(conn, POP3_PASS);
1434
1435   return result;
1436 }
1437
1438 /* For PASS responses */
1439 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
1440                                      pop3state instate)
1441 {
1442   CURLcode result = CURLE_OK;
1443   struct SessionHandle *data = conn->data;
1444
1445   (void)instate; /* no use for this yet */
1446
1447   if(pop3code != '+') {
1448     failf(data, "Access denied. %c", pop3code);
1449     result = CURLE_LOGIN_DENIED;
1450   }
1451   else
1452     /* End of connect phase */
1453     state(conn, POP3_STOP);
1454
1455   return result;
1456 }
1457
1458 /* For command responses */
1459 static CURLcode pop3_state_command_resp(struct connectdata *conn,
1460                                         int pop3code,
1461                                         pop3state instate)
1462 {
1463   CURLcode result = CURLE_OK;
1464   struct SessionHandle *data = conn->data;
1465   struct POP3 *pop3 = data->req.protop;
1466   struct pop3_conn *pop3c = &conn->proto.pop3c;
1467   struct pingpong *pp = &pop3c->pp;
1468
1469   (void)instate; /* no use for this yet */
1470
1471   if(pop3code != '+') {
1472     state(conn, POP3_STOP);
1473     return CURLE_RECV_ERROR;
1474   }
1475
1476   /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1477      EOB string so count this is two matching bytes. This is necessary to make
1478      the code detect the EOB if the only data than comes now is %2e CR LF like
1479      when there is no body to return. */
1480   pop3c->eob = 2;
1481
1482   /* But since this initial CR LF pair is not part of the actual body, we set
1483      the strip counter here so that these bytes won't be delivered. */
1484   pop3c->strip = 2;
1485
1486   if(pop3->transfer == FTPTRANSFER_BODY) {
1487     /* POP3 download */
1488     Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
1489
1490     if(pp->cache) {
1491       /* The header "cache" contains a bunch of data that is actually body
1492          content so send it as such. Note that there may even be additional
1493          "headers" after the body */
1494
1495       if(!data->set.opt_no_body) {
1496         result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
1497         if(result)
1498           return result;
1499       }
1500
1501       /* Free the cache */
1502       Curl_safefree(pp->cache);
1503
1504       /* Reset the cache size */
1505       pp->cache_size = 0;
1506     }
1507   }
1508
1509   /* End of DO phase */
1510   state(conn, POP3_STOP);
1511
1512   return result;
1513 }
1514
1515 static CURLcode pop3_statemach_act(struct connectdata *conn)
1516 {
1517   CURLcode result = CURLE_OK;
1518   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1519   int pop3code;
1520   struct pop3_conn *pop3c = &conn->proto.pop3c;
1521   struct pingpong *pp = &pop3c->pp;
1522   size_t nread = 0;
1523
1524   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1525   if(pop3c->state == POP3_UPGRADETLS)
1526     return pop3_perform_upgrade_tls(conn);
1527
1528   /* Flush any data that needs to be sent */
1529   if(pp->sendleft)
1530     return Curl_pp_flushsend(pp);
1531
1532  do {
1533     /* Read the response from the server */
1534     result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1535     if(result)
1536       return result;
1537
1538     if(!pop3code)
1539       break;
1540
1541     /* We have now received a full POP3 server response */
1542     switch(pop3c->state) {
1543     case POP3_SERVERGREET:
1544       result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1545       break;
1546
1547     case POP3_CAPA:
1548       result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1549       break;
1550
1551     case POP3_STARTTLS:
1552       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1553       break;
1554
1555     case POP3_AUTH_PLAIN:
1556       result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
1557       break;
1558
1559     case POP3_AUTH_LOGIN:
1560       result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
1561       break;
1562
1563     case POP3_AUTH_LOGIN_PASSWD:
1564       result = pop3_state_auth_login_password_resp(conn, pop3code,
1565                                                    pop3c->state);
1566       break;
1567
1568 #ifndef CURL_DISABLE_CRYPTO_AUTH
1569     case POP3_AUTH_CRAMMD5:
1570       result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
1571       break;
1572
1573     case POP3_AUTH_DIGESTMD5:
1574       result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
1575       break;
1576
1577     case POP3_AUTH_DIGESTMD5_RESP:
1578       result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
1579       break;
1580 #endif
1581
1582 #ifdef USE_NTLM
1583     case POP3_AUTH_NTLM:
1584       result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
1585       break;
1586
1587     case POP3_AUTH_NTLM_TYPE2MSG:
1588       result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
1589                                                   pop3c->state);
1590       break;
1591 #endif
1592
1593 #if defined(USE_KERBEROS5)
1594     case POP3_AUTH_GSSAPI:
1595       result = pop3_state_auth_gssapi_resp(conn, pop3code, pop3c->state);
1596       break;
1597
1598     case POP3_AUTH_GSSAPI_TOKEN:
1599       result = pop3_state_auth_gssapi_token_resp(conn, pop3code, pop3c->state);
1600       break;
1601
1602     case POP3_AUTH_GSSAPI_NO_DATA:
1603       result = pop3_state_auth_gssapi_no_data_resp(conn, pop3code,
1604                                                    pop3c->state);
1605       break;
1606 #endif
1607
1608     case POP3_AUTH_XOAUTH2:
1609       result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state);
1610       break;
1611
1612     case POP3_AUTH_CANCEL:
1613       result = pop3_state_auth_cancel_resp(conn, pop3code, pop3c->state);
1614       break;
1615
1616     case POP3_AUTH_FINAL:
1617       result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
1618       break;
1619
1620 #ifndef CURL_DISABLE_CRYPTO_AUTH
1621     case POP3_APOP:
1622       result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1623       break;
1624 #endif
1625
1626     case POP3_USER:
1627       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1628       break;
1629
1630     case POP3_PASS:
1631       result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1632       break;
1633
1634     case POP3_COMMAND:
1635       result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1636       break;
1637
1638     case POP3_QUIT:
1639       /* fallthrough, just stop! */
1640     default:
1641       /* internal error */
1642       state(conn, POP3_STOP);
1643       break;
1644     }
1645   } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1646
1647   return result;
1648 }
1649
1650 /* Called repeatedly until done from multi.c */
1651 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1652 {
1653   CURLcode result = CURLE_OK;
1654   struct pop3_conn *pop3c = &conn->proto.pop3c;
1655
1656   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1657     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1658     if(result || !pop3c->ssldone)
1659       return result;
1660   }
1661
1662   result = Curl_pp_statemach(&pop3c->pp, FALSE);
1663   *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1664
1665   return result;
1666 }
1667
1668 static CURLcode pop3_block_statemach(struct connectdata *conn)
1669 {
1670   CURLcode result = CURLE_OK;
1671   struct pop3_conn *pop3c = &conn->proto.pop3c;
1672
1673   while(pop3c->state != POP3_STOP && !result)
1674     result = Curl_pp_statemach(&pop3c->pp, TRUE);
1675
1676   return result;
1677 }
1678
1679 /* Allocate and initialize the POP3 struct for the current SessionHandle if
1680    required */
1681 static CURLcode pop3_init(struct connectdata *conn)
1682 {
1683   CURLcode result = CURLE_OK;
1684   struct SessionHandle *data = conn->data;
1685   struct POP3 *pop3;
1686
1687   pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
1688   if(!pop3)
1689     result = CURLE_OUT_OF_MEMORY;
1690
1691   return result;
1692 }
1693
1694 /* For the POP3 "protocol connect" and "doing" phases only */
1695 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1696                         int numsocks)
1697 {
1698   return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1699 }
1700
1701 /***********************************************************************
1702  *
1703  * pop3_connect()
1704  *
1705  * This function should do everything that is to be considered a part of the
1706  * connection phase.
1707  *
1708  * The variable 'done' points to will be TRUE if the protocol-layer connect
1709  * phase is done when this function returns, or FALSE if not.
1710  */
1711 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1712 {
1713   CURLcode result = CURLE_OK;
1714   struct pop3_conn *pop3c = &conn->proto.pop3c;
1715   struct pingpong *pp = &pop3c->pp;
1716
1717   *done = FALSE; /* default to not done yet */
1718
1719   /* We always support persistent connections in POP3 */
1720   connkeep(conn, "POP3 default");
1721
1722   /* Set the default response time-out */
1723   pp->response_time = RESP_TIMEOUT;
1724   pp->statemach_act = pop3_statemach_act;
1725   pp->endofresp = pop3_endofresp;
1726   pp->conn = conn;
1727
1728   /* Set the default preferred authentication type and mechanism */
1729   pop3c->preftype = POP3_TYPE_ANY;
1730   pop3c->prefmech = SASL_AUTH_ANY;
1731
1732   /* Initialise the pingpong layer */
1733   Curl_pp_init(pp);
1734
1735   /* Parse the URL options */
1736   result = pop3_parse_url_options(conn);
1737   if(result)
1738     return result;
1739
1740   /* Start off waiting for the server greeting response */
1741   state(conn, POP3_SERVERGREET);
1742
1743   result = pop3_multi_statemach(conn, done);
1744
1745   return result;
1746 }
1747
1748 /***********************************************************************
1749  *
1750  * pop3_done()
1751  *
1752  * The DONE function. This does what needs to be done after a single DO has
1753  * performed.
1754  *
1755  * Input argument is already checked for validity.
1756  */
1757 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1758                           bool premature)
1759 {
1760   CURLcode result = CURLE_OK;
1761   struct SessionHandle *data = conn->data;
1762   struct POP3 *pop3 = data->req.protop;
1763
1764   (void)premature;
1765
1766   if(!pop3)
1767     /* When the easy handle is removed from the multi interface while libcurl
1768        is still trying to resolve the host name, the POP3 struct is not yet
1769        initialized. However, the removal action calls Curl_done() which in
1770        turn calls this function, so we simply return success. */
1771     return CURLE_OK;
1772
1773   if(status) {
1774     connclose(conn, "POP3 done with bad status");
1775     result = status;         /* use the already set error code */
1776   }
1777
1778   /* Cleanup our per-request based variables */
1779   Curl_safefree(pop3->id);
1780   Curl_safefree(pop3->custom);
1781
1782   /* Clear the transfer mode for the next request */
1783   pop3->transfer = FTPTRANSFER_BODY;
1784
1785   return result;
1786 }
1787
1788 /***********************************************************************
1789  *
1790  * pop3_perform()
1791  *
1792  * This is the actual DO function for POP3. Get a message/listing according to
1793  * the options previously setup.
1794  */
1795 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1796                              bool *dophase_done)
1797 {
1798   /* This is POP3 and no proxy */
1799   CURLcode result = CURLE_OK;
1800   struct POP3 *pop3 = conn->data->req.protop;
1801
1802   DEBUGF(infof(conn->data, "DO phase starts\n"));
1803
1804   if(conn->data->set.opt_no_body) {
1805     /* Requested no body means no transfer */
1806     pop3->transfer = FTPTRANSFER_INFO;
1807   }
1808
1809   *dophase_done = FALSE; /* not done yet */
1810
1811   /* Start the first command in the DO phase */
1812   result = pop3_perform_command(conn);
1813   if(result)
1814     return result;
1815
1816   /* Run the state-machine */
1817   result = pop3_multi_statemach(conn, dophase_done);
1818
1819   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1820
1821   if(*dophase_done)
1822     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1823
1824   return result;
1825 }
1826
1827 /***********************************************************************
1828  *
1829  * pop3_do()
1830  *
1831  * This function is registered as 'curl_do' function. It decodes the path
1832  * parts etc as a wrapper to the actual DO function (pop3_perform).
1833  *
1834  * The input argument is already checked for validity.
1835  */
1836 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1837 {
1838   CURLcode result = CURLE_OK;
1839
1840   *done = FALSE; /* default to false */
1841
1842   /* Parse the URL path */
1843   result = pop3_parse_url_path(conn);
1844   if(result)
1845     return result;
1846
1847   /* Parse the custom request */
1848   result = pop3_parse_custom_request(conn);
1849   if(result)
1850     return result;
1851
1852   result = pop3_regular_transfer(conn, done);
1853
1854   return result;
1855 }
1856
1857 /***********************************************************************
1858  *
1859  * pop3_disconnect()
1860  *
1861  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1862  * resources. BLOCKING.
1863  */
1864 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
1865 {
1866   struct pop3_conn *pop3c = &conn->proto.pop3c;
1867
1868   /* We cannot send quit unconditionally. If this connection is stale or
1869      bad in any way, sending quit and waiting around here will make the
1870      disconnect wait in vain and cause more problems than we need to. */
1871
1872   /* The POP3 session may or may not have been allocated/setup at this
1873      point! */
1874   if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
1875     if(!pop3_perform_quit(conn))
1876       (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1877
1878   /* Disconnect from the server */
1879   Curl_pp_disconnect(&pop3c->pp);
1880
1881   /* Cleanup the SASL module */
1882   Curl_sasl_cleanup(conn, pop3c->authused);
1883
1884   /* Cleanup our connection based variables */
1885   Curl_safefree(pop3c->apoptimestamp);
1886
1887   return CURLE_OK;
1888 }
1889
1890 /* Call this when the DO phase has completed */
1891 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1892 {
1893   (void)conn;
1894   (void)connected;
1895
1896   return CURLE_OK;
1897 }
1898
1899 /* Called from multi.c while DOing */
1900 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1901 {
1902   CURLcode result = pop3_multi_statemach(conn, dophase_done);
1903
1904   if(result)
1905     DEBUGF(infof(conn->data, "DO phase failed\n"));
1906   else if(*dophase_done) {
1907     result = pop3_dophase_done(conn, FALSE /* not connected */);
1908
1909     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1910   }
1911
1912   return result;
1913 }
1914
1915 /***********************************************************************
1916  *
1917  * pop3_regular_transfer()
1918  *
1919  * The input argument is already checked for validity.
1920  *
1921  * Performs all commands done before a regular transfer between a local and a
1922  * remote host.
1923  */
1924 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1925                                       bool *dophase_done)
1926 {
1927   CURLcode result = CURLE_OK;
1928   bool connected = FALSE;
1929   struct SessionHandle *data = conn->data;
1930
1931   /* Make sure size is unknown at this point */
1932   data->req.size = -1;
1933
1934   /* Set the progress data */
1935   Curl_pgrsSetUploadCounter(data, 0);
1936   Curl_pgrsSetDownloadCounter(data, 0);
1937   Curl_pgrsSetUploadSize(data, -1);
1938   Curl_pgrsSetDownloadSize(data, -1);
1939
1940   /* Carry out the perform */
1941   result = pop3_perform(conn, &connected, dophase_done);
1942
1943   /* Perform post DO phase operations if necessary */
1944   if(!result && *dophase_done)
1945     result = pop3_dophase_done(conn, connected);
1946
1947   return result;
1948 }
1949
1950 static CURLcode pop3_setup_connection(struct connectdata *conn)
1951 {
1952   struct SessionHandle *data = conn->data;
1953
1954   /* Initialise the POP3 layer */
1955   CURLcode result = pop3_init(conn);
1956   if(result)
1957     return result;
1958
1959   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1960     /* Unless we have asked to tunnel POP3 operations through the proxy, we
1961        switch and use HTTP operations only */
1962 #ifndef CURL_DISABLE_HTTP
1963     if(conn->handler == &Curl_handler_pop3)
1964       conn->handler = &Curl_handler_pop3_proxy;
1965     else {
1966 #ifdef USE_SSL
1967       conn->handler = &Curl_handler_pop3s_proxy;
1968 #else
1969       failf(data, "POP3S not supported!");
1970       return CURLE_UNSUPPORTED_PROTOCOL;
1971 #endif
1972     }
1973
1974     /* set it up as an HTTP connection instead */
1975     return conn->handler->setup_connection(conn);
1976 #else
1977     failf(data, "POP3 over http proxy requires HTTP support built-in!");
1978     return CURLE_UNSUPPORTED_PROTOCOL;
1979 #endif
1980   }
1981
1982   data->state.path++;   /* don't include the initial slash */
1983
1984   return CURLE_OK;
1985 }
1986
1987 /***********************************************************************
1988  *
1989  * pop3_parse_url_options()
1990  *
1991  * Parse the URL login options.
1992  */
1993 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1994 {
1995   CURLcode result = CURLE_OK;
1996   struct pop3_conn *pop3c = &conn->proto.pop3c;
1997   const char *options = conn->options;
1998   const char *ptr = options;
1999   bool reset = TRUE;
2000
2001   while(ptr && *ptr) {
2002     const char *key = ptr;
2003
2004     while(*ptr && *ptr != '=')
2005         ptr++;
2006
2007     if(strnequal(key, "AUTH", 4)) {
2008       size_t len = 0;
2009       const char *value = ++ptr;
2010
2011       if(reset) {
2012         reset = FALSE;
2013         pop3c->preftype = POP3_TYPE_NONE;
2014         pop3c->prefmech = SASL_AUTH_NONE;
2015       }
2016
2017       while(*ptr && *ptr != ';') {
2018         ptr++;
2019         len++;
2020       }
2021
2022       if(strnequal(value, "*", len)) {
2023         pop3c->preftype = POP3_TYPE_ANY;
2024         pop3c->prefmech = SASL_AUTH_ANY;
2025       }
2026       else if(strnequal(value, "+APOP", len)) {
2027         pop3c->preftype = POP3_TYPE_APOP;
2028         pop3c->prefmech = SASL_AUTH_NONE;
2029       }
2030       else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
2031         pop3c->preftype = POP3_TYPE_SASL;
2032         pop3c->prefmech |= SASL_MECH_LOGIN;
2033       }
2034       else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
2035         pop3c->preftype = POP3_TYPE_SASL;
2036         pop3c->prefmech |= SASL_MECH_PLAIN;
2037       }
2038       else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
2039         pop3c->preftype = POP3_TYPE_SASL;
2040         pop3c->prefmech |= SASL_MECH_CRAM_MD5;
2041       }
2042       else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
2043         pop3c->preftype = POP3_TYPE_SASL;
2044         pop3c->prefmech |= SASL_MECH_DIGEST_MD5;
2045       }
2046       else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
2047         pop3c->preftype = POP3_TYPE_SASL;
2048         pop3c->prefmech |= SASL_MECH_GSSAPI;
2049       }
2050       else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
2051         pop3c->preftype = POP3_TYPE_SASL;
2052         pop3c->prefmech |= SASL_MECH_NTLM;
2053       }
2054       else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
2055         pop3c->preftype = POP3_TYPE_SASL;
2056         pop3c->prefmech |= SASL_MECH_XOAUTH2;
2057       }
2058
2059       if(*ptr == ';')
2060         ptr++;
2061     }
2062     else
2063       result = CURLE_URL_MALFORMAT;
2064   }
2065
2066   return result;
2067 }
2068
2069 /***********************************************************************
2070  *
2071  * pop3_parse_url_path()
2072  *
2073  * Parse the URL path into separate path components.
2074  */
2075 static CURLcode pop3_parse_url_path(struct connectdata *conn)
2076 {
2077   /* The POP3 struct is already initialised in pop3_connect() */
2078   struct SessionHandle *data = conn->data;
2079   struct POP3 *pop3 = data->req.protop;
2080   const char *path = data->state.path;
2081
2082   /* URL decode the path for the message ID */
2083   return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
2084 }
2085
2086 /***********************************************************************
2087  *
2088  * pop3_parse_custom_request()
2089  *
2090  * Parse the custom request.
2091  */
2092 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
2093 {
2094   CURLcode result = CURLE_OK;
2095   struct SessionHandle *data = conn->data;
2096   struct POP3 *pop3 = data->req.protop;
2097   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2098
2099   /* URL decode the custom request */
2100   if(custom)
2101     result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
2102
2103   return result;
2104 }
2105
2106 /***********************************************************************
2107  *
2108  * pop3_calc_sasl_details()
2109  *
2110  * Calculate the required login details for SASL authentication.
2111  */
2112 static CURLcode pop3_calc_sasl_details(struct connectdata *conn,
2113                                        const char **mech,
2114                                        char **initresp, size_t *len,
2115                                        pop3state *state1, pop3state *state2)
2116 {
2117   CURLcode result = CURLE_OK;
2118   struct SessionHandle *data = conn->data;
2119   struct pop3_conn *pop3c = &conn->proto.pop3c;
2120
2121   /* Calculate the supported authentication mechanism, by decreasing order of
2122      security, as well as the initial response where appropriate */
2123 #if defined(USE_KERBEROS5)
2124   if((pop3c->authmechs & SASL_MECH_GSSAPI) &&
2125       (pop3c->prefmech & SASL_MECH_GSSAPI)) {
2126     pop3c->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
2127
2128     *mech = SASL_MECH_STRING_GSSAPI;
2129     *state1 = POP3_AUTH_GSSAPI;
2130     *state2 = POP3_AUTH_GSSAPI_TOKEN;
2131     pop3c->authused = SASL_MECH_GSSAPI;
2132
2133     if(data->set.sasl_ir)
2134       result = Curl_sasl_create_gssapi_user_message(data, conn->user,
2135                                                     conn->passwd, "pop",
2136                                                     pop3c->mutual_auth,
2137                                                     NULL, &conn->krb5,
2138                                                     initresp, len);
2139   }
2140   else
2141 #endif
2142 #ifndef CURL_DISABLE_CRYPTO_AUTH
2143   if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) &&
2144       (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) {
2145     *mech = SASL_MECH_STRING_DIGEST_MD5;
2146     *state1 = POP3_AUTH_DIGESTMD5;
2147     pop3c->authused = SASL_MECH_DIGEST_MD5;
2148   }
2149   else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) &&
2150           (pop3c->prefmech & SASL_MECH_CRAM_MD5)) {
2151     *mech = SASL_MECH_STRING_CRAM_MD5;
2152     *state1 = POP3_AUTH_CRAMMD5;
2153     pop3c->authused = SASL_MECH_CRAM_MD5;
2154   }
2155   else
2156 #endif
2157 #ifdef USE_NTLM
2158   if((pop3c->authmechs & SASL_MECH_NTLM) &&
2159       (pop3c->prefmech & SASL_MECH_NTLM)) {
2160     *mech = SASL_MECH_STRING_NTLM;
2161     *state1 = POP3_AUTH_NTLM;
2162     *state2 = POP3_AUTH_NTLM_TYPE2MSG;
2163     pop3c->authused = SASL_MECH_NTLM;
2164
2165     if(data->set.sasl_ir)
2166       result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2167                                                     &conn->ntlm,
2168                                                     initresp, len);
2169   }
2170   else
2171 #endif
2172   if(((pop3c->authmechs & SASL_MECH_XOAUTH2) &&
2173       (pop3c->prefmech & SASL_MECH_XOAUTH2) &&
2174       (pop3c->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2175     *mech = SASL_MECH_STRING_XOAUTH2;
2176     *state1 = POP3_AUTH_XOAUTH2;
2177     *state2 = POP3_AUTH_FINAL;
2178     pop3c->authused = SASL_MECH_XOAUTH2;
2179
2180     if(data->set.sasl_ir)
2181       result = Curl_sasl_create_xoauth2_message(data, conn->user,
2182                                                 conn->xoauth2_bearer,
2183                                                 initresp, len);
2184   }
2185   else if((pop3c->authmechs & SASL_MECH_LOGIN) &&
2186           (pop3c->prefmech & SASL_MECH_LOGIN)) {
2187     *mech = SASL_MECH_STRING_LOGIN;
2188     *state1 = POP3_AUTH_LOGIN;
2189     *state2 = POP3_AUTH_LOGIN_PASSWD;
2190     pop3c->authused = SASL_MECH_LOGIN;
2191
2192     if(data->set.sasl_ir)
2193       result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2194   }
2195   else if((pop3c->authmechs & SASL_MECH_PLAIN) &&
2196           (pop3c->prefmech & SASL_MECH_PLAIN)) {
2197     *mech = SASL_MECH_STRING_PLAIN;
2198     *state1 = POP3_AUTH_PLAIN;
2199     *state2 = POP3_AUTH_FINAL;
2200     pop3c->authused = SASL_MECH_PLAIN;
2201
2202     if(data->set.sasl_ir)
2203       result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2204                                               initresp, len);
2205   }
2206
2207   return result;
2208 }
2209
2210 /***********************************************************************
2211  *
2212  * Curl_pop3_write()
2213  *
2214  * This function scans the body after the end-of-body and writes everything
2215  * until the end is found.
2216  */
2217 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
2218 {
2219   /* This code could be made into a special function in the handler struct */
2220   CURLcode result = CURLE_OK;
2221   struct SessionHandle *data = conn->data;
2222   struct SingleRequest *k = &data->req;
2223
2224   struct pop3_conn *pop3c = &conn->proto.pop3c;
2225   bool strip_dot = FALSE;
2226   size_t last = 0;
2227   size_t i;
2228
2229   /* Search through the buffer looking for the end-of-body marker which is
2230      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
2231      the eob so the server will have prefixed it with an extra dot which we
2232      need to strip out. Additionally the marker could of course be spread out
2233      over 5 different data chunks. */
2234   for(i = 0; i < nread; i++) {
2235     size_t prev = pop3c->eob;
2236
2237     switch(str[i]) {
2238     case 0x0d:
2239       if(pop3c->eob == 0) {
2240         pop3c->eob++;
2241
2242         if(i) {
2243           /* Write out the body part that didn't match */
2244           result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
2245                                      i - last);
2246
2247           if(result)
2248             return result;
2249
2250           last = i;
2251         }
2252       }
2253       else if(pop3c->eob == 3)
2254         pop3c->eob++;
2255       else
2256         /* If the character match wasn't at position 0 or 3 then restart the
2257            pattern matching */
2258         pop3c->eob = 1;
2259       break;
2260
2261     case 0x0a:
2262       if(pop3c->eob == 1 || pop3c->eob == 4)
2263         pop3c->eob++;
2264       else
2265         /* If the character match wasn't at position 1 or 4 then start the
2266            search again */
2267         pop3c->eob = 0;
2268       break;
2269
2270     case 0x2e:
2271       if(pop3c->eob == 2)
2272         pop3c->eob++;
2273       else if(pop3c->eob == 3) {
2274         /* We have an extra dot after the CRLF which we need to strip off */
2275         strip_dot = TRUE;
2276         pop3c->eob = 0;
2277       }
2278       else
2279         /* If the character match wasn't at position 2 then start the search
2280            again */
2281         pop3c->eob = 0;
2282       break;
2283
2284     default:
2285       pop3c->eob = 0;
2286       break;
2287     }
2288
2289     /* Did we have a partial match which has subsequently failed? */
2290     if(prev && prev >= pop3c->eob) {
2291       /* Strip can only be non-zero for the very first mismatch after CRLF
2292          and then both prev and strip are equal and nothing will be output
2293          below */
2294       while(prev && pop3c->strip) {
2295         prev--;
2296         pop3c->strip--;
2297       }
2298
2299       if(prev) {
2300         /* If the partial match was the CRLF and dot then only write the CRLF
2301            as the server would have inserted the dot */
2302         result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
2303                                    strip_dot ? prev - 1 : prev);
2304
2305         if(result)
2306           return result;
2307
2308         last = i;
2309         strip_dot = FALSE;
2310       }
2311     }
2312   }
2313
2314   if(pop3c->eob == POP3_EOB_LEN) {
2315     /* We have a full match so the transfer is done, however we must transfer
2316     the CRLF at the start of the EOB as this is considered to be part of the
2317     message as per RFC-1939, sect. 3 */
2318     result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
2319
2320     k->keepon &= ~KEEP_RECV;
2321     pop3c->eob = 0;
2322
2323     return result;
2324   }
2325
2326   if(pop3c->eob)
2327     /* While EOB is matching nothing should be output */
2328     return CURLE_OK;
2329
2330   if(nread - last) {
2331     result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
2332                                nread - last);
2333   }
2334
2335   return result;
2336 }
2337
2338 #endif /* CURL_DISABLE_POP3 */