pop3: Fixed APOP being determined by CAPA response rather than by timestamp
[platform/upstream/curl.git] / lib / pop3.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * RFC1734 POP3 Authentication
22  * RFC1939 POP3 protocol
23  * RFC2195 CRAM-MD5 authentication
24  * RFC2384 POP URL Scheme
25  * RFC2449 POP3 Extension Mechanism
26  * RFC2595 Using TLS with IMAP, POP3 and ACAP
27  * RFC2831 DIGEST-MD5 authentication
28  * RFC4422 Simple Authentication and Security Layer (SASL)
29  * RFC4616 PLAIN authentication
30  * RFC5034 POP3 SASL Authentication Mechanism
31  * 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_POP3 | 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     result = pop3_perform_user(conn);
785
786   return result;
787 }
788
789 /* For STARTTLS responses */
790 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
791                                          int pop3code,
792                                          pop3state instate)
793 {
794   CURLcode result = CURLE_OK;
795   struct SessionHandle *data = conn->data;
796
797   (void)instate; /* no use for this yet */
798
799   if(pop3code != '+') {
800     if(data->set.use_ssl != CURLUSESSL_TRY) {
801       failf(data, "STARTTLS denied. %c", pop3code);
802       result = CURLE_USE_SSL_FAILED;
803     }
804     else
805       result = pop3_perform_authentication(conn);
806   }
807   else
808     result = pop3_perform_upgrade_tls(conn);
809
810   return result;
811 }
812
813 /* For AUTH PLAIN (without initial response) responses */
814 static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
815                                            int pop3code,
816                                            pop3state instate)
817 {
818   CURLcode result = CURLE_OK;
819   struct SessionHandle *data = conn->data;
820   size_t len = 0;
821   char *plainauth = NULL;
822
823   (void)instate; /* no use for this yet */
824
825   if(pop3code != '+') {
826     failf(data, "Access denied. %c", pop3code);
827     result = CURLE_LOGIN_DENIED;
828   }
829   else {
830     /* Create the authorisation message */
831     result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
832                                             &plainauth, &len);
833     if(!result && plainauth) {
834       /* Send the message */
835       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
836
837       if(!result)
838         state(conn, POP3_AUTH_FINAL);
839     }
840   }
841
842   Curl_safefree(plainauth);
843
844   return result;
845 }
846
847 /* For AUTH LOGIN (without initial response) responses */
848 static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
849                                            int pop3code,
850                                            pop3state instate)
851 {
852   CURLcode result = CURLE_OK;
853   struct SessionHandle *data = conn->data;
854   size_t len = 0;
855   char *authuser = NULL;
856
857   (void)instate; /* no use for this yet */
858
859   if(pop3code != '+') {
860     failf(data, "Access denied: %d", pop3code);
861     result = CURLE_LOGIN_DENIED;
862   }
863   else {
864     /* Create the user message */
865     result = Curl_sasl_create_login_message(data, conn->user,
866                                             &authuser, &len);
867     if(!result && authuser) {
868       /* Send the user */
869       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
870
871       if(!result)
872         state(conn, POP3_AUTH_LOGIN_PASSWD);
873     }
874   }
875
876   Curl_safefree(authuser);
877
878   return result;
879 }
880
881 /* For AUTH LOGIN user entry responses */
882 static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
883                                                     int pop3code,
884                                                     pop3state instate)
885 {
886   CURLcode result = CURLE_OK;
887   struct SessionHandle *data = conn->data;
888   size_t len = 0;
889   char *authpasswd = NULL;
890
891   (void)instate; /* no use for this yet */
892
893   if(pop3code != '+') {
894     failf(data, "Access denied: %d", pop3code);
895     result = CURLE_LOGIN_DENIED;
896   }
897   else {
898     /* Create the password message */
899     result = Curl_sasl_create_login_message(data, conn->passwd,
900                                             &authpasswd, &len);
901     if(!result && authpasswd) {
902       /* Send the password */
903       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
904
905       if(!result)
906         state(conn, POP3_AUTH_FINAL);
907     }
908   }
909
910   Curl_safefree(authpasswd);
911
912   return result;
913 }
914
915 #ifndef CURL_DISABLE_CRYPTO_AUTH
916 /* For AUTH CRAM-MD5 responses */
917 static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
918                                           int pop3code,
919                                           pop3state instate)
920 {
921   CURLcode result = CURLE_OK;
922   struct SessionHandle *data = conn->data;
923   char *chlg = NULL;
924   char *chlg64 = NULL;
925   char *rplyb64 = NULL;
926   size_t len = 0;
927
928   (void)instate; /* no use for this yet */
929
930   if(pop3code != '+') {
931     failf(data, "Access denied: %d", pop3code);
932     return CURLE_LOGIN_DENIED;
933   }
934
935   /* Get the challenge message */
936   pop3_get_message(data->state.buffer, &chlg64);
937
938   /* Decode the challenge message */
939   result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
940   if(result) {
941     /* Send the cancellation */
942     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
943
944     if(!result)
945       state(conn, POP3_AUTH_CANCEL);
946   }
947   else {
948     /* Create the response message */
949     result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
950                                                conn->passwd, &rplyb64, &len);
951     if(!result && rplyb64) {
952       /* Send the response */
953       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
954
955       if(!result)
956         state(conn, POP3_AUTH_FINAL);
957     }
958   }
959
960   Curl_safefree(chlg);
961   Curl_safefree(rplyb64);
962
963   return result;
964 }
965
966 /* For AUTH DIGEST-MD5 challenge responses */
967 static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
968                                             int pop3code,
969                                             pop3state instate)
970 {
971   CURLcode result = CURLE_OK;
972   struct SessionHandle *data = conn->data;
973   char *chlg64 = NULL;
974   char *rplyb64 = NULL;
975   size_t len = 0;
976
977   char nonce[64];
978   char realm[128];
979   char algorithm[64];
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   /* Decode the challange message */
992   result = Curl_sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
993                                                realm, sizeof(realm),
994                                                algorithm, sizeof(algorithm));
995   if(result || strcmp(algorithm, "md5-sess") != 0) {
996     /* Send the cancellation */
997     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
998
999     if(!result)
1000       state(conn, POP3_AUTH_CANCEL);
1001   }
1002   else {
1003     /* Create the response message */
1004     result = Curl_sasl_create_digest_md5_message(data, nonce, realm,
1005                                                  conn->user, conn->passwd,
1006                                                  "pop", &rplyb64, &len);
1007     if(!result && rplyb64) {
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
1016   Curl_safefree(rplyb64);
1017
1018   return result;
1019 }
1020
1021 /* For AUTH DIGEST-MD5 challenge-response responses */
1022 static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
1023                                                  int pop3code,
1024                                                  pop3state instate)
1025 {
1026   CURLcode result = CURLE_OK;
1027   struct SessionHandle *data = conn->data;
1028
1029   (void)instate; /* no use for this yet */
1030
1031   if(pop3code != '+') {
1032     failf(data, "Authentication failed: %d", pop3code);
1033     result = CURLE_LOGIN_DENIED;
1034   }
1035   else {
1036     /* Send an empty response */
1037     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "");
1038
1039     if(!result)
1040       state(conn, POP3_AUTH_FINAL);
1041   }
1042
1043   return result;
1044 }
1045 #endif
1046
1047 #ifdef USE_NTLM
1048 /* For AUTH NTLM (without initial response) responses */
1049 static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
1050                                           int pop3code,
1051                                           pop3state instate)
1052 {
1053   CURLcode result = CURLE_OK;
1054   struct SessionHandle *data = conn->data;
1055   size_t len = 0;
1056   char *type1msg = NULL;
1057
1058   (void)instate; /* no use for this yet */
1059
1060   if(pop3code != '+') {
1061     failf(data, "Access denied: %d", pop3code);
1062     result = CURLE_LOGIN_DENIED;
1063   }
1064   else {
1065     /* Create the type-1 message */
1066     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1067                                                  &conn->ntlm,
1068                                                  &type1msg, &len);
1069     if(!result && type1msg) {
1070       /* Send the message */
1071       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
1072
1073       if(!result)
1074         state(conn, POP3_AUTH_NTLM_TYPE2MSG);
1075     }
1076   }
1077
1078   Curl_safefree(type1msg);
1079
1080   return result;
1081 }
1082
1083 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1084 static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1085                                                    int pop3code,
1086                                                    pop3state instate)
1087 {
1088   CURLcode result = CURLE_OK;
1089   struct SessionHandle *data = conn->data;
1090   char *type2msg = NULL;
1091   char *type3msg = NULL;
1092   size_t len = 0;
1093
1094   (void)instate; /* no use for this yet */
1095
1096   if(pop3code != '+') {
1097     failf(data, "Access denied: %d", pop3code);
1098     result = CURLE_LOGIN_DENIED;
1099   }
1100   else {
1101     /* Get the type-2 message */
1102     pop3_get_message(data->state.buffer, &type2msg);
1103
1104     /* Decode the type-2 message */
1105     result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1106     if(result) {
1107       /* Send the cancellation */
1108       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
1109
1110       if(!result)
1111         state(conn, POP3_AUTH_CANCEL);
1112     }
1113     else {
1114       /* Create the type-3 message */
1115       result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1116                                                    conn->passwd, &conn->ntlm,
1117                                                    &type3msg, &len);
1118       if(!result && type3msg) {
1119         /* Send the message */
1120         result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
1121
1122         if(!result)
1123           state(conn, POP3_AUTH_FINAL);
1124       }
1125     }
1126   }
1127
1128   Curl_safefree(type3msg);
1129
1130   return result;
1131 }
1132 #endif
1133
1134 /* For AUTH XOAUTH2 (without initial response) responses */
1135 static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn,
1136                                              int pop3code, pop3state instate)
1137 {
1138   CURLcode result = CURLE_OK;
1139   struct SessionHandle *data = conn->data;
1140   size_t len = 0;
1141   char *xoauth = NULL;
1142
1143   (void)instate; /* no use for this yet */
1144
1145   if(pop3code != '+') {
1146     failf(data, "Access denied: %d", pop3code);
1147     result = CURLE_LOGIN_DENIED;
1148   }
1149   else {
1150     /* Create the authorisation message */
1151     result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1152                                               conn->xoauth2_bearer,
1153                                               &xoauth, &len);
1154     if(!result && xoauth) {
1155       /* Send the message */
1156       result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth);
1157
1158       if(!result)
1159         state(conn, POP3_AUTH_FINAL);
1160     }
1161   }
1162
1163   Curl_safefree(xoauth);
1164
1165   return result;
1166 }
1167
1168 /* For AUTH cancellation responses */
1169 static CURLcode pop3_state_auth_cancel_resp(struct connectdata *conn,
1170                                             int pop3code,
1171                                             pop3state instate)
1172 {
1173   CURLcode result = CURLE_OK;
1174   struct SessionHandle *data = conn->data;
1175   struct pop3_conn *pop3c = &conn->proto.pop3c;
1176   const char *mech = NULL;
1177   char *initresp = NULL;
1178   size_t len = 0;
1179   pop3state state1 = POP3_STOP;
1180   pop3state state2 = POP3_STOP;
1181
1182   (void)pop3code;
1183   (void)instate; /* no use for this yet */
1184
1185   /* Remove the offending mechanism from the supported list */
1186   pop3c->authmechs ^= pop3c->authused;
1187
1188   /* Calculate alternative SASL login details */
1189   result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1190                                   &state2);
1191
1192   if(!result) {
1193     /* Do we have any mechanisms left or can we fallback to another
1194        authentication type? */
1195     if(mech) {
1196       /* Retry SASL based authentication */
1197       result = pop3_perform_auth(conn, mech, initresp, len, state1, state2);
1198
1199       Curl_safefree(initresp);
1200     }
1201 #ifndef CURL_DISABLE_CRYPTO_AUTH
1202     else if((pop3c->authtypes & POP3_TYPE_APOP) &&
1203             (pop3c->preftype & POP3_TYPE_APOP))
1204       /* Perform APOP authentication */
1205       result = pop3_perform_apop(conn);
1206 #endif
1207     else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
1208             (pop3c->preftype & POP3_TYPE_CLEARTEXT))
1209       /* Perform clear text authentication */
1210       result = pop3_perform_user(conn);
1211     else {
1212       failf(data, "Authentication cancelled");
1213
1214       result = CURLE_LOGIN_DENIED;
1215     }
1216   }
1217
1218   return result;
1219 }
1220
1221 /* For final responses in the AUTH sequence */
1222 static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
1223                                            int pop3code,
1224                                            pop3state instate)
1225 {
1226   CURLcode result = CURLE_OK;
1227   struct SessionHandle *data = conn->data;
1228
1229   (void)instate; /* no use for this yet */
1230
1231   if(pop3code != '+') {
1232     failf(data, "Authentication failed: %d", pop3code);
1233     result = CURLE_LOGIN_DENIED;
1234   }
1235   else
1236     /* End of connect phase */
1237     state(conn, POP3_STOP);
1238
1239   return result;
1240 }
1241
1242 #ifndef CURL_DISABLE_CRYPTO_AUTH
1243 /* For APOP responses */
1244 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
1245                                      pop3state instate)
1246 {
1247   CURLcode result = CURLE_OK;
1248   struct SessionHandle *data = conn->data;
1249
1250   (void)instate; /* no use for this yet */
1251
1252   if(pop3code != '+') {
1253     failf(data, "Authentication failed: %d", pop3code);
1254     result = CURLE_LOGIN_DENIED;
1255   }
1256   else
1257     /* End of connect phase */
1258     state(conn, POP3_STOP);
1259
1260   return result;
1261 }
1262 #endif
1263
1264 /* For USER responses */
1265 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
1266                                      pop3state instate)
1267 {
1268   CURLcode result = CURLE_OK;
1269   struct SessionHandle *data = conn->data;
1270
1271   (void)instate; /* no use for this yet */
1272
1273   if(pop3code != '+') {
1274     failf(data, "Access denied. %c", pop3code);
1275     result = CURLE_LOGIN_DENIED;
1276   }
1277   else
1278     /* Send the PASS command */
1279     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
1280                            conn->passwd ? conn->passwd : "");
1281   if(!result)
1282     state(conn, POP3_PASS);
1283
1284   return result;
1285 }
1286
1287 /* For PASS responses */
1288 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
1289                                      pop3state instate)
1290 {
1291   CURLcode result = CURLE_OK;
1292   struct SessionHandle *data = conn->data;
1293
1294   (void)instate; /* no use for this yet */
1295
1296   if(pop3code != '+') {
1297     failf(data, "Access denied. %c", pop3code);
1298     result = CURLE_LOGIN_DENIED;
1299   }
1300   else
1301     /* End of connect phase */
1302     state(conn, POP3_STOP);
1303
1304   return result;
1305 }
1306
1307 /* For command responses */
1308 static CURLcode pop3_state_command_resp(struct connectdata *conn,
1309                                         int pop3code,
1310                                         pop3state instate)
1311 {
1312   CURLcode result = CURLE_OK;
1313   struct SessionHandle *data = conn->data;
1314   struct POP3 *pop3 = data->req.protop;
1315   struct pop3_conn *pop3c = &conn->proto.pop3c;
1316   struct pingpong *pp = &pop3c->pp;
1317
1318   (void)instate; /* no use for this yet */
1319
1320   if(pop3code != '+') {
1321     state(conn, POP3_STOP);
1322     return CURLE_RECV_ERROR;
1323   }
1324
1325   /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1326      EOB string so count this is two matching bytes. This is necessary to make
1327      the code detect the EOB if the only data than comes now is %2e CR LF like
1328      when there is no body to return. */
1329   pop3c->eob = 2;
1330
1331   /* But since this initial CR LF pair is not part of the actual body, we set
1332      the strip counter here so that these bytes won't be delivered. */
1333   pop3c->strip = 2;
1334
1335   if(pop3->transfer == FTPTRANSFER_BODY) {
1336     /* POP3 download */
1337     Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
1338
1339     if(pp->cache) {
1340       /* The header "cache" contains a bunch of data that is actually body
1341          content so send it as such. Note that there may even be additional
1342          "headers" after the body */
1343
1344       if(!data->set.opt_no_body) {
1345         result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
1346         if(result)
1347           return result;
1348       }
1349
1350       /* Free the cache */
1351       Curl_safefree(pp->cache);
1352
1353       /* Reset the cache size */
1354       pp->cache_size = 0;
1355     }
1356   }
1357
1358   /* End of DO phase */
1359   state(conn, POP3_STOP);
1360
1361   return result;
1362 }
1363
1364 static CURLcode pop3_statemach_act(struct connectdata *conn)
1365 {
1366   CURLcode result = CURLE_OK;
1367   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1368   int pop3code;
1369   struct pop3_conn *pop3c = &conn->proto.pop3c;
1370   struct pingpong *pp = &pop3c->pp;
1371   size_t nread = 0;
1372
1373   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1374   if(pop3c->state == POP3_UPGRADETLS)
1375     return pop3_perform_upgrade_tls(conn);
1376
1377   /* Flush any data that needs to be sent */
1378   if(pp->sendleft)
1379     return Curl_pp_flushsend(pp);
1380
1381  do {
1382     /* Read the response from the server */
1383     result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1384     if(result)
1385       return result;
1386
1387     if(!pop3code)
1388       break;
1389
1390     /* We have now received a full POP3 server response */
1391     switch(pop3c->state) {
1392     case POP3_SERVERGREET:
1393       result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1394       break;
1395
1396     case POP3_CAPA:
1397       result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1398       break;
1399
1400     case POP3_STARTTLS:
1401       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1402       break;
1403
1404     case POP3_AUTH_PLAIN:
1405       result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
1406       break;
1407
1408     case POP3_AUTH_LOGIN:
1409       result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
1410       break;
1411
1412     case POP3_AUTH_LOGIN_PASSWD:
1413       result = pop3_state_auth_login_password_resp(conn, pop3code,
1414                                                    pop3c->state);
1415       break;
1416
1417 #ifndef CURL_DISABLE_CRYPTO_AUTH
1418     case POP3_AUTH_CRAMMD5:
1419       result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
1420       break;
1421
1422     case POP3_AUTH_DIGESTMD5:
1423       result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
1424       break;
1425
1426     case POP3_AUTH_DIGESTMD5_RESP:
1427       result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
1428       break;
1429 #endif
1430
1431 #ifdef USE_NTLM
1432     case POP3_AUTH_NTLM:
1433       result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
1434       break;
1435
1436     case POP3_AUTH_NTLM_TYPE2MSG:
1437       result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
1438                                                   pop3c->state);
1439       break;
1440 #endif
1441
1442     case POP3_AUTH_XOAUTH2:
1443       result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state);
1444       break;
1445
1446     case POP3_AUTH_CANCEL:
1447       result = pop3_state_auth_cancel_resp(conn, pop3code, pop3c->state);
1448       break;
1449
1450     case POP3_AUTH_FINAL:
1451       result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
1452       break;
1453
1454 #ifndef CURL_DISABLE_CRYPTO_AUTH
1455     case POP3_APOP:
1456       result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1457       break;
1458 #endif
1459
1460     case POP3_USER:
1461       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1462       break;
1463
1464     case POP3_PASS:
1465       result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1466       break;
1467
1468     case POP3_COMMAND:
1469       result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1470       break;
1471
1472     case POP3_QUIT:
1473       /* fallthrough, just stop! */
1474     default:
1475       /* internal error */
1476       state(conn, POP3_STOP);
1477       break;
1478     }
1479   } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1480
1481   return result;
1482 }
1483
1484 /* Called repeatedly until done from multi.c */
1485 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1486 {
1487   CURLcode result = CURLE_OK;
1488   struct pop3_conn *pop3c = &conn->proto.pop3c;
1489
1490   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1491     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1492     if(result || !pop3c->ssldone)
1493       return result;
1494   }
1495
1496   result = Curl_pp_statemach(&pop3c->pp, FALSE);
1497   *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1498
1499   return result;
1500 }
1501
1502 static CURLcode pop3_block_statemach(struct connectdata *conn)
1503 {
1504   CURLcode result = CURLE_OK;
1505   struct pop3_conn *pop3c = &conn->proto.pop3c;
1506
1507   while(pop3c->state != POP3_STOP && !result)
1508     result = Curl_pp_statemach(&pop3c->pp, TRUE);
1509
1510   return result;
1511 }
1512
1513 /* Allocate and initialize the POP3 struct for the current SessionHandle if
1514    required */
1515 static CURLcode pop3_init(struct connectdata *conn)
1516 {
1517   CURLcode result = CURLE_OK;
1518   struct SessionHandle *data = conn->data;
1519   struct POP3 *pop3;
1520
1521   pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
1522   if(!pop3)
1523     result = CURLE_OUT_OF_MEMORY;
1524
1525   return result;
1526 }
1527
1528 /* For the POP3 "protocol connect" and "doing" phases only */
1529 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1530                         int numsocks)
1531 {
1532   return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1533 }
1534
1535 /***********************************************************************
1536  *
1537  * pop3_connect()
1538  *
1539  * This function should do everything that is to be considered a part of the
1540  * connection phase.
1541  *
1542  * The variable 'done' points to will be TRUE if the protocol-layer connect
1543  * phase is done when this function returns, or FALSE if not.
1544  */
1545 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1546 {
1547   CURLcode result = CURLE_OK;
1548   struct pop3_conn *pop3c = &conn->proto.pop3c;
1549   struct pingpong *pp = &pop3c->pp;
1550
1551   *done = FALSE; /* default to not done yet */
1552
1553   /* We always support persistent connections in POP3 */
1554   conn->bits.close = FALSE;
1555
1556   /* Set the default response time-out */
1557   pp->response_time = RESP_TIMEOUT;
1558   pp->statemach_act = pop3_statemach_act;
1559   pp->endofresp = pop3_endofresp;
1560   pp->conn = conn;
1561
1562   /* Set the default preferred authentication type and mechanism */
1563   pop3c->preftype = POP3_TYPE_ANY;
1564   pop3c->prefmech = SASL_AUTH_ANY;
1565
1566   /* Initialise the pingpong layer */
1567   Curl_pp_init(pp);
1568
1569   /* Parse the URL options */
1570   result = pop3_parse_url_options(conn);
1571   if(result)
1572     return result;
1573
1574   /* Start off waiting for the server greeting response */
1575   state(conn, POP3_SERVERGREET);
1576
1577   result = pop3_multi_statemach(conn, done);
1578
1579   return result;
1580 }
1581
1582 /***********************************************************************
1583  *
1584  * pop3_done()
1585  *
1586  * The DONE function. This does what needs to be done after a single DO has
1587  * performed.
1588  *
1589  * Input argument is already checked for validity.
1590  */
1591 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1592                           bool premature)
1593 {
1594   CURLcode result = CURLE_OK;
1595   struct SessionHandle *data = conn->data;
1596   struct POP3 *pop3 = data->req.protop;
1597
1598   (void)premature;
1599
1600   if(!pop3)
1601     /* When the easy handle is removed from the multi interface while libcurl
1602        is still trying to resolve the host name, the POP3 struct is not yet
1603        initialized. However, the removal action calls Curl_done() which in
1604        turn calls this function, so we simply return success. */
1605     return CURLE_OK;
1606
1607   if(status) {
1608     conn->bits.close = TRUE; /* marked for closure */
1609     result = status;         /* use the already set error code */
1610   }
1611
1612   /* Cleanup our per-request based variables */
1613   Curl_safefree(pop3->id);
1614   Curl_safefree(pop3->custom);
1615
1616   /* Clear the transfer mode for the next request */
1617   pop3->transfer = FTPTRANSFER_BODY;
1618
1619   return result;
1620 }
1621
1622 /***********************************************************************
1623  *
1624  * pop3_perform()
1625  *
1626  * This is the actual DO function for POP3. Get a message/listing according to
1627  * the options previously setup.
1628  */
1629 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1630                              bool *dophase_done)
1631 {
1632   /* This is POP3 and no proxy */
1633   CURLcode result = CURLE_OK;
1634   struct POP3 *pop3 = conn->data->req.protop;
1635
1636   DEBUGF(infof(conn->data, "DO phase starts\n"));
1637
1638   if(conn->data->set.opt_no_body) {
1639     /* Requested no body means no transfer */
1640     pop3->transfer = FTPTRANSFER_INFO;
1641   }
1642
1643   *dophase_done = FALSE; /* not done yet */
1644
1645   /* Start the first command in the DO phase */
1646   result = pop3_perform_command(conn);
1647   if(result)
1648     return result;
1649
1650   /* Run the state-machine */
1651   result = pop3_multi_statemach(conn, dophase_done);
1652
1653   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1654
1655   if(*dophase_done)
1656     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1657
1658   return result;
1659 }
1660
1661 /***********************************************************************
1662  *
1663  * pop3_do()
1664  *
1665  * This function is registered as 'curl_do' function. It decodes the path
1666  * parts etc as a wrapper to the actual DO function (pop3_perform).
1667  *
1668  * The input argument is already checked for validity.
1669  */
1670 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1671 {
1672   CURLcode result = CURLE_OK;
1673
1674   *done = FALSE; /* default to false */
1675
1676   /* Parse the URL path */
1677   result = pop3_parse_url_path(conn);
1678   if(result)
1679     return result;
1680
1681   /* Parse the custom request */
1682   result = pop3_parse_custom_request(conn);
1683   if(result)
1684     return result;
1685
1686   result = pop3_regular_transfer(conn, done);
1687
1688   return result;
1689 }
1690
1691 /***********************************************************************
1692  *
1693  * pop3_disconnect()
1694  *
1695  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1696  * resources. BLOCKING.
1697  */
1698 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
1699 {
1700   struct pop3_conn *pop3c = &conn->proto.pop3c;
1701
1702   /* We cannot send quit unconditionally. If this connection is stale or
1703      bad in any way, sending quit and waiting around here will make the
1704      disconnect wait in vain and cause more problems than we need to. */
1705
1706   /* The POP3 session may or may not have been allocated/setup at this
1707      point! */
1708   if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
1709     if(!pop3_perform_quit(conn))
1710       (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1711
1712   /* Disconnect from the server */
1713   Curl_pp_disconnect(&pop3c->pp);
1714
1715   /* Cleanup the SASL module */
1716   Curl_sasl_cleanup(conn, pop3c->authused);
1717
1718   /* Cleanup our connection based variables */
1719   Curl_safefree(pop3c->apoptimestamp);
1720
1721   return CURLE_OK;
1722 }
1723
1724 /* Call this when the DO phase has completed */
1725 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1726 {
1727   (void)conn;
1728   (void)connected;
1729
1730   return CURLE_OK;
1731 }
1732
1733 /* Called from multi.c while DOing */
1734 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1735 {
1736   CURLcode result = pop3_multi_statemach(conn, dophase_done);
1737
1738   if(result)
1739     DEBUGF(infof(conn->data, "DO phase failed\n"));
1740   else if(*dophase_done) {
1741     result = pop3_dophase_done(conn, FALSE /* not connected */);
1742
1743     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1744   }
1745
1746   return result;
1747 }
1748
1749 /***********************************************************************
1750  *
1751  * pop3_regular_transfer()
1752  *
1753  * The input argument is already checked for validity.
1754  *
1755  * Performs all commands done before a regular transfer between a local and a
1756  * remote host.
1757  */
1758 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1759                                       bool *dophase_done)
1760 {
1761   CURLcode result = CURLE_OK;
1762   bool connected = FALSE;
1763   struct SessionHandle *data = conn->data;
1764
1765   /* Make sure size is unknown at this point */
1766   data->req.size = -1;
1767
1768   /* Set the progress data */
1769   Curl_pgrsSetUploadCounter(data, 0);
1770   Curl_pgrsSetDownloadCounter(data, 0);
1771   Curl_pgrsSetUploadSize(data, 0);
1772   Curl_pgrsSetDownloadSize(data, 0);
1773
1774   /* Carry out the perform */
1775   result = pop3_perform(conn, &connected, dophase_done);
1776
1777   /* Perform post DO phase operations if necessary */
1778   if(!result && *dophase_done)
1779     result = pop3_dophase_done(conn, connected);
1780
1781   return result;
1782 }
1783
1784 static CURLcode pop3_setup_connection(struct connectdata *conn)
1785 {
1786   struct SessionHandle *data = conn->data;
1787
1788   /* Initialise the POP3 layer */
1789   CURLcode result = pop3_init(conn);
1790   if(result)
1791     return result;
1792
1793   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1794     /* Unless we have asked to tunnel POP3 operations through the proxy, we
1795        switch and use HTTP operations only */
1796 #ifndef CURL_DISABLE_HTTP
1797     if(conn->handler == &Curl_handler_pop3)
1798       conn->handler = &Curl_handler_pop3_proxy;
1799     else {
1800 #ifdef USE_SSL
1801       conn->handler = &Curl_handler_pop3s_proxy;
1802 #else
1803       failf(data, "POP3S not supported!");
1804       return CURLE_UNSUPPORTED_PROTOCOL;
1805 #endif
1806     }
1807
1808     /* set it up as an HTTP connection instead */
1809     return conn->handler->setup_connection(conn);
1810 #else
1811     failf(data, "POP3 over http proxy requires HTTP support built-in!");
1812     return CURLE_UNSUPPORTED_PROTOCOL;
1813 #endif
1814   }
1815
1816   data->state.path++;   /* don't include the initial slash */
1817
1818   return CURLE_OK;
1819 }
1820
1821 /***********************************************************************
1822  *
1823  * pop3_parse_url_options()
1824  *
1825  * Parse the URL login options.
1826  */
1827 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1828 {
1829   CURLcode result = CURLE_OK;
1830   struct pop3_conn *pop3c = &conn->proto.pop3c;
1831   const char *options = conn->options;
1832   const char *ptr = options;
1833   bool reset = TRUE;
1834
1835   while(ptr && *ptr) {
1836     const char *key = ptr;
1837
1838     while(*ptr && *ptr != '=')
1839         ptr++;
1840
1841     if(strnequal(key, "AUTH", 4)) {
1842       size_t len = 0;
1843       const char *value = ++ptr;
1844
1845       if(reset) {
1846         reset = FALSE;
1847         pop3c->preftype = POP3_TYPE_NONE;
1848         pop3c->prefmech = SASL_AUTH_NONE;
1849       }
1850
1851       while(*ptr && *ptr != ';') {
1852         ptr++;
1853         len++;
1854       }
1855
1856       if(strnequal(value, "*", len)) {
1857         pop3c->preftype = POP3_TYPE_ANY;
1858         pop3c->prefmech = SASL_AUTH_ANY;
1859       }
1860       else if(strnequal(value, "+APOP", len)) {
1861         pop3c->preftype = POP3_TYPE_APOP;
1862         pop3c->prefmech = SASL_AUTH_NONE;
1863       }
1864       else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
1865         pop3c->preftype = POP3_TYPE_SASL;
1866         pop3c->prefmech |= SASL_MECH_LOGIN;
1867       }
1868       else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
1869         pop3c->preftype = POP3_TYPE_SASL;
1870         pop3c->prefmech |= SASL_MECH_PLAIN;
1871       }
1872       else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
1873         pop3c->preftype = POP3_TYPE_SASL;
1874         pop3c->prefmech |= SASL_MECH_CRAM_MD5;
1875       }
1876       else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
1877         pop3c->preftype = POP3_TYPE_SASL;
1878         pop3c->prefmech |= SASL_MECH_DIGEST_MD5;
1879       }
1880       else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
1881         pop3c->preftype = POP3_TYPE_SASL;
1882         pop3c->prefmech |= SASL_MECH_GSSAPI;
1883       }
1884       else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
1885         pop3c->preftype = POP3_TYPE_SASL;
1886         pop3c->prefmech |= SASL_MECH_NTLM;
1887       }
1888       else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
1889         pop3c->preftype = POP3_TYPE_SASL;
1890         pop3c->prefmech |= SASL_MECH_XOAUTH2;
1891       }
1892
1893       if(*ptr == ';')
1894         ptr++;
1895     }
1896     else
1897       result = CURLE_URL_MALFORMAT;
1898   }
1899
1900   return result;
1901 }
1902
1903 /***********************************************************************
1904  *
1905  * pop3_parse_url_path()
1906  *
1907  * Parse the URL path into separate path components.
1908  */
1909 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1910 {
1911   /* The POP3 struct is already initialised in pop3_connect() */
1912   struct SessionHandle *data = conn->data;
1913   struct POP3 *pop3 = data->req.protop;
1914   const char *path = data->state.path;
1915
1916   /* URL decode the path for the message ID */
1917   return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1918 }
1919
1920 /***********************************************************************
1921  *
1922  * pop3_parse_custom_request()
1923  *
1924  * Parse the custom request.
1925  */
1926 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1927 {
1928   CURLcode result = CURLE_OK;
1929   struct SessionHandle *data = conn->data;
1930   struct POP3 *pop3 = data->req.protop;
1931   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1932
1933   /* URL decode the custom request */
1934   if(custom)
1935     result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1936
1937   return result;
1938 }
1939
1940 /***********************************************************************
1941  *
1942  * pop3_calc_sasl_details()
1943  *
1944  * Calculate the required login details for SASL authentication.
1945  */
1946 static CURLcode pop3_calc_sasl_details(struct connectdata *conn,
1947                                        const char **mech,
1948                                        char **initresp, size_t *len,
1949                                        pop3state *state1, pop3state *state2)
1950 {
1951   CURLcode result = CURLE_OK;
1952   struct SessionHandle *data = conn->data;
1953   struct pop3_conn *pop3c = &conn->proto.pop3c;
1954
1955   /* Calculate the supported authentication mechanism, by decreasing order of
1956      security, as well as the initial response where appropriate */
1957 #ifndef CURL_DISABLE_CRYPTO_AUTH
1958   if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) &&
1959       (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) {
1960     *mech = SASL_MECH_STRING_DIGEST_MD5;
1961     *state1 = POP3_AUTH_DIGESTMD5;
1962     pop3c->authused = SASL_MECH_DIGEST_MD5;
1963   }
1964   else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) &&
1965           (pop3c->prefmech & SASL_MECH_CRAM_MD5)) {
1966     *mech = SASL_MECH_STRING_CRAM_MD5;
1967     *state1 = POP3_AUTH_CRAMMD5;
1968     pop3c->authused = SASL_MECH_CRAM_MD5;
1969   }
1970   else
1971 #endif
1972 #ifdef USE_NTLM
1973   if((pop3c->authmechs & SASL_MECH_NTLM) &&
1974       (pop3c->prefmech & SASL_MECH_NTLM)) {
1975     *mech = SASL_MECH_STRING_NTLM;
1976     *state1 = POP3_AUTH_NTLM;
1977     *state2 = POP3_AUTH_NTLM_TYPE2MSG;
1978     pop3c->authused = SASL_MECH_NTLM;
1979
1980     if(data->set.sasl_ir)
1981       result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1982                                                     &conn->ntlm,
1983                                                     initresp, len);
1984   }
1985   else
1986 #endif
1987   if(((pop3c->authmechs & SASL_MECH_XOAUTH2) &&
1988       (pop3c->prefmech & SASL_MECH_XOAUTH2) &&
1989       (pop3c->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
1990     *mech = SASL_MECH_STRING_XOAUTH2;
1991     *state1 = POP3_AUTH_XOAUTH2;
1992     *state2 = POP3_AUTH_FINAL;
1993     pop3c->authused = SASL_MECH_XOAUTH2;
1994
1995     if(data->set.sasl_ir)
1996       result = Curl_sasl_create_xoauth2_message(data, conn->user,
1997                                                 conn->xoauth2_bearer,
1998                                                 initresp, len);
1999   }
2000   else if((pop3c->authmechs & SASL_MECH_LOGIN) &&
2001           (pop3c->prefmech & SASL_MECH_LOGIN)) {
2002     *mech = SASL_MECH_STRING_LOGIN;
2003     *state1 = POP3_AUTH_LOGIN;
2004     *state2 = POP3_AUTH_LOGIN_PASSWD;
2005     pop3c->authused = SASL_MECH_LOGIN;
2006
2007     if(data->set.sasl_ir)
2008       result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2009   }
2010   else if((pop3c->authmechs & SASL_MECH_PLAIN) &&
2011           (pop3c->prefmech & SASL_MECH_PLAIN)) {
2012     *mech = SASL_MECH_STRING_PLAIN;
2013     *state1 = POP3_AUTH_PLAIN;
2014     *state2 = POP3_AUTH_FINAL;
2015     pop3c->authused = SASL_MECH_PLAIN;
2016
2017     if(data->set.sasl_ir)
2018       result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2019                                               initresp, len);
2020   }
2021
2022   return result;
2023 }
2024
2025 /***********************************************************************
2026  *
2027  * Curl_pop3_write()
2028  *
2029  * This function scans the body after the end-of-body and writes everything
2030  * until the end is found.
2031  */
2032 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
2033 {
2034   /* This code could be made into a special function in the handler struct */
2035   CURLcode result = CURLE_OK;
2036   struct SessionHandle *data = conn->data;
2037   struct SingleRequest *k = &data->req;
2038
2039   struct pop3_conn *pop3c = &conn->proto.pop3c;
2040   bool strip_dot = FALSE;
2041   size_t last = 0;
2042   size_t i;
2043
2044   /* Search through the buffer looking for the end-of-body marker which is
2045      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
2046      the eob so the server will have prefixed it with an extra dot which we
2047      need to strip out. Additionally the marker could of course be spread out
2048      over 5 different data chunks. */
2049   for(i = 0; i < nread; i++) {
2050     size_t prev = pop3c->eob;
2051
2052     switch(str[i]) {
2053     case 0x0d:
2054       if(pop3c->eob == 0) {
2055         pop3c->eob++;
2056
2057         if(i) {
2058           /* Write out the body part that didn't match */
2059           result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
2060                                      i - last);
2061
2062           if(result)
2063             return result;
2064
2065           last = i;
2066         }
2067       }
2068       else if(pop3c->eob == 3)
2069         pop3c->eob++;
2070       else
2071         /* If the character match wasn't at position 0 or 3 then restart the
2072            pattern matching */
2073         pop3c->eob = 1;
2074       break;
2075
2076     case 0x0a:
2077       if(pop3c->eob == 1 || pop3c->eob == 4)
2078         pop3c->eob++;
2079       else
2080         /* If the character match wasn't at position 1 or 4 then start the
2081            search again */
2082         pop3c->eob = 0;
2083       break;
2084
2085     case 0x2e:
2086       if(pop3c->eob == 2)
2087         pop3c->eob++;
2088       else if(pop3c->eob == 3) {
2089         /* We have an extra dot after the CRLF which we need to strip off */
2090         strip_dot = TRUE;
2091         pop3c->eob = 0;
2092       }
2093       else
2094         /* If the character match wasn't at position 2 then start the search
2095            again */
2096         pop3c->eob = 0;
2097       break;
2098
2099     default:
2100       pop3c->eob = 0;
2101       break;
2102     }
2103
2104     /* Did we have a partial match which has subsequently failed? */
2105     if(prev && prev >= pop3c->eob) {
2106       /* Strip can only be non-zero for the very first mismatch after CRLF
2107          and then both prev and strip are equal and nothing will be output
2108          below */
2109       while(prev && pop3c->strip) {
2110         prev--;
2111         pop3c->strip--;
2112       }
2113
2114       if(prev) {
2115         /* If the partial match was the CRLF and dot then only write the CRLF
2116            as the server would have inserted the dot */
2117         result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
2118                                    strip_dot ? prev - 1 : prev);
2119
2120         if(result)
2121           return result;
2122
2123         last = i;
2124         strip_dot = FALSE;
2125       }
2126     }
2127   }
2128
2129   if(pop3c->eob == POP3_EOB_LEN) {
2130     /* We have a full match so the transfer is done, however we must transfer
2131     the CRLF at the start of the EOB as this is considered to be part of the
2132     message as per RFC-1939, sect. 3 */
2133     result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
2134
2135     k->keepon &= ~KEEP_RECV;
2136     pop3c->eob = 0;
2137
2138     return result;
2139   }
2140
2141   if(pop3c->eob)
2142     /* While EOB is matching nothing should be output */
2143     return CURLE_OK;
2144
2145   if(nread - last) {
2146     result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
2147                                nread - last);
2148   }
2149
2150   return result;
2151 }
2152
2153 #endif /* CURL_DISABLE_POP3 */