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