openssl: guard against OOM on context creation
[platform/upstream/curl.git] / lib / pop3.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2020, 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 https://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * RFC1734 POP3 Authentication
22  * RFC1939 POP3 protocol
23  * RFC2195 CRAM-MD5 authentication
24  * RFC2384 POP URL Scheme
25  * RFC2449 POP3 Extension Mechanism
26  * RFC2595 Using TLS with IMAP, POP3 and ACAP
27  * RFC2831 DIGEST-MD5 authentication
28  * RFC4422 Simple Authentication and Security Layer (SASL)
29  * RFC4616 PLAIN authentication
30  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
31  * RFC5034 POP3 SASL Authentication Mechanism
32  * RFC6749 OAuth 2.0 Authorization Framework
33  * RFC8314 Use of TLS for Email Submission and Access
34  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
35  *
36  ***************************************************************************/
37
38 #include "curl_setup.h"
39
40 #ifndef CURL_DISABLE_POP3
41
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
45 #ifdef HAVE_ARPA_INET_H
46 #include <arpa/inet.h>
47 #endif
48 #ifdef HAVE_UTSNAME_H
49 #include <sys/utsname.h>
50 #endif
51 #ifdef HAVE_NETDB_H
52 #include <netdb.h>
53 #endif
54 #ifdef __VMS
55 #include <in.h>
56 #include <inet.h>
57 #endif
58
59 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
60 #undef in_addr_t
61 #define in_addr_t unsigned long
62 #endif
63
64 #include <curl/curl.h>
65 #include "urldata.h"
66 #include "sendf.h"
67 #include "hostip.h"
68 #include "progress.h"
69 #include "transfer.h"
70 #include "escape.h"
71 #include "http.h" /* for HTTP proxy tunnel stuff */
72 #include "socks.h"
73 #include "pop3.h"
74 #include "strtoofft.h"
75 #include "strcase.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 "curl_sasl.h"
83 #include "curl_md5.h"
84 #include "warnless.h"
85 /* The last 3 #include files should be in this order */
86 #include "curl_printf.h"
87 #include "curl_memory.h"
88 #include "memdebug.h"
89
90 /* Local API functions */
91 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
92 static CURLcode pop3_do(struct connectdata *conn, bool *done);
93 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
94                           bool premature);
95 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
96 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
97 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
98 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks);
99 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode pop3_setup_connection(struct connectdata *conn);
101 static CURLcode pop3_parse_url_options(struct connectdata *conn);
102 static CURLcode pop3_parse_url_path(struct connectdata *conn);
103 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
104 static CURLcode pop3_perform_auth(struct connectdata *conn, const char *mech,
105                                   const char *initresp);
106 static CURLcode pop3_continue_auth(struct connectdata *conn, const char *resp);
107 static void pop3_get_message(char *buffer, char **outptr);
108
109 /*
110  * POP3 protocol handler.
111  */
112
113 const struct Curl_handler Curl_handler_pop3 = {
114   "POP3",                           /* scheme */
115   pop3_setup_connection,            /* setup_connection */
116   pop3_do,                          /* do_it */
117   pop3_done,                        /* done */
118   ZERO_NULL,                        /* do_more */
119   pop3_connect,                     /* connect_it */
120   pop3_multi_statemach,             /* connecting */
121   pop3_doing,                       /* doing */
122   pop3_getsock,                     /* proto_getsock */
123   pop3_getsock,                     /* doing_getsock */
124   ZERO_NULL,                        /* domore_getsock */
125   ZERO_NULL,                        /* perform_getsock */
126   pop3_disconnect,                  /* disconnect */
127   ZERO_NULL,                        /* readwrite */
128   ZERO_NULL,                        /* connection_check */
129   PORT_POP3,                        /* defport */
130   CURLPROTO_POP3,                   /* protocol */
131   CURLPROTO_POP3,                   /* family */
132   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
133   PROTOPT_URLOPTIONS
134 };
135
136 #ifdef USE_SSL
137 /*
138  * POP3S protocol handler.
139  */
140
141 const struct Curl_handler Curl_handler_pop3s = {
142   "POP3S",                          /* scheme */
143   pop3_setup_connection,            /* setup_connection */
144   pop3_do,                          /* do_it */
145   pop3_done,                        /* done */
146   ZERO_NULL,                        /* do_more */
147   pop3_connect,                     /* connect_it */
148   pop3_multi_statemach,             /* connecting */
149   pop3_doing,                       /* doing */
150   pop3_getsock,                     /* proto_getsock */
151   pop3_getsock,                     /* doing_getsock */
152   ZERO_NULL,                        /* domore_getsock */
153   ZERO_NULL,                        /* perform_getsock */
154   pop3_disconnect,                  /* disconnect */
155   ZERO_NULL,                        /* readwrite */
156   ZERO_NULL,                        /* connection_check */
157   PORT_POP3S,                       /* defport */
158   CURLPROTO_POP3S,                  /* protocol */
159   CURLPROTO_POP3,                   /* family */
160   PROTOPT_CLOSEACTION | PROTOPT_SSL
161   | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
162 };
163 #endif
164
165 /* SASL parameters for the pop3 protocol */
166 static const struct SASLproto saslpop3 = {
167   "pop",                      /* The service name */
168   '*',                        /* Code received when continuation is expected */
169   '+',                        /* Code to receive upon authentication success */
170   255 - 8,                    /* Maximum initial response length (no max) */
171   pop3_perform_auth,          /* Send authentication command */
172   pop3_continue_auth,         /* Send authentication continuation */
173   pop3_get_message            /* Get SASL response message */
174 };
175
176 #ifdef USE_SSL
177 static void pop3_to_pop3s(struct connectdata *conn)
178 {
179   /* Change the connection handler */
180   conn->handler = &Curl_handler_pop3s;
181
182   /* Set the connection's upgraded to TLS flag */
183   conn->bits.tls_upgraded = TRUE;
184 }
185 #else
186 #define pop3_to_pop3s(x) Curl_nop_stmt
187 #endif
188
189 /***********************************************************************
190  *
191  * pop3_endofresp()
192  *
193  * Checks for an ending POP3 status code at the start of the given string, but
194  * also detects the APOP timestamp from the server greeting and various
195  * capabilities from the CAPA response including the supported authentication
196  * types and allowed SASL mechanisms.
197  */
198 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
199                            int *resp)
200 {
201   struct pop3_conn *pop3c = &conn->proto.pop3c;
202
203   /* Do we have an error response? */
204   if(len >= 4 && !memcmp("-ERR", line, 4)) {
205     *resp = '-';
206
207     return TRUE;
208   }
209
210   /* Are we processing CAPA command responses? */
211   if(pop3c->state == POP3_CAPA) {
212     /* Do we have the terminating line? */
213     if(len >= 1 && line[0] == '.')
214       /* Treat the response as a success */
215       *resp = '+';
216     else
217       /* Treat the response as an untagged continuation */
218       *resp = '*';
219
220     return TRUE;
221   }
222
223   /* Do we have a success response? */
224   if(len >= 3 && !memcmp("+OK", line, 3)) {
225     *resp = '+';
226
227     return TRUE;
228   }
229
230   /* Do we have a continuation response? */
231   if(len >= 1 && line[0] == '+') {
232     *resp = '*';
233
234     return TRUE;
235   }
236
237   return FALSE; /* Nothing for us */
238 }
239
240 /***********************************************************************
241  *
242  * pop3_get_message()
243  *
244  * Gets the authentication message from the response buffer.
245  */
246 static void pop3_get_message(char *buffer, char **outptr)
247 {
248   size_t len = strlen(buffer);
249   char *message = NULL;
250
251   if(len > 2) {
252     /* Find the start of the message */
253     len -= 2;
254     for(message = buffer + 2; *message == ' ' || *message == '\t';
255         message++, len--)
256       ;
257
258     /* Find the end of the message */
259     for(; len--;)
260       if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
261          message[len] != '\t')
262         break;
263
264     /* Terminate the message */
265     if(++len) {
266       message[len] = '\0';
267     }
268   }
269   else
270     /* junk input => zero length output */
271     message = &buffer[len];
272
273   *outptr = message;
274 }
275
276 /***********************************************************************
277  *
278  * state()
279  *
280  * This is the ONLY way to change POP3 state!
281  */
282 static void state(struct connectdata *conn, pop3state newstate)
283 {
284   struct pop3_conn *pop3c = &conn->proto.pop3c;
285 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
286   /* for debug purposes */
287   static const char * const names[] = {
288     "STOP",
289     "SERVERGREET",
290     "CAPA",
291     "STARTTLS",
292     "UPGRADETLS",
293     "AUTH",
294     "APOP",
295     "USER",
296     "PASS",
297     "COMMAND",
298     "QUIT",
299     /* LAST */
300   };
301
302   if(pop3c->state != newstate)
303     infof(conn->data, "POP3 %p state change from %s to %s\n",
304           (void *)pop3c, names[pop3c->state], names[newstate]);
305 #endif
306
307   pop3c->state = newstate;
308 }
309
310 /***********************************************************************
311  *
312  * pop3_perform_capa()
313  *
314  * Sends the CAPA command in order to obtain a list of server side supported
315  * capabilities.
316  */
317 static CURLcode pop3_perform_capa(struct connectdata *conn)
318 {
319   CURLcode result = CURLE_OK;
320   struct pop3_conn *pop3c = &conn->proto.pop3c;
321
322   pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
323   pop3c->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
324   pop3c->tls_supported = FALSE;           /* Clear the TLS capability */
325
326   /* Send the CAPA command */
327   result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA");
328
329   if(!result)
330     state(conn, POP3_CAPA);
331
332   return result;
333 }
334
335 /***********************************************************************
336  *
337  * pop3_perform_starttls()
338  *
339  * Sends the STLS command to start the upgrade to TLS.
340  */
341 static CURLcode pop3_perform_starttls(struct connectdata *conn)
342 {
343   /* Send the STLS command */
344   CURLcode result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS");
345
346   if(!result)
347     state(conn, POP3_STARTTLS);
348
349   return result;
350 }
351
352 /***********************************************************************
353  *
354  * pop3_perform_upgrade_tls()
355  *
356  * Performs the upgrade to TLS.
357  */
358 static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
359 {
360   /* Start the SSL connection */
361   struct pop3_conn *pop3c = &conn->proto.pop3c;
362   CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
363                                                  &pop3c->ssldone);
364
365   if(!result) {
366     if(pop3c->state != POP3_UPGRADETLS)
367       state(conn, POP3_UPGRADETLS);
368
369     if(pop3c->ssldone) {
370       pop3_to_pop3s(conn);
371       result = pop3_perform_capa(conn);
372     }
373   }
374
375   return result;
376 }
377
378 /***********************************************************************
379  *
380  * pop3_perform_user()
381  *
382  * Sends a clear text USER command to authenticate with.
383  */
384 static CURLcode pop3_perform_user(struct connectdata *conn)
385 {
386   CURLcode result = CURLE_OK;
387
388   /* Check we have a username and password to authenticate with and end the
389      connect phase if we don't */
390   if(!conn->bits.user_passwd) {
391     state(conn, POP3_STOP);
392
393     return result;
394   }
395
396   /* Send the USER command */
397   result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
398                          conn->user ? conn->user : "");
399   if(!result)
400     state(conn, POP3_USER);
401
402   return result;
403 }
404
405 #ifndef CURL_DISABLE_CRYPTO_AUTH
406 /***********************************************************************
407  *
408  * pop3_perform_apop()
409  *
410  * Sends an APOP command to authenticate with.
411  */
412 static CURLcode pop3_perform_apop(struct connectdata *conn)
413 {
414   CURLcode result = CURLE_OK;
415   struct pop3_conn *pop3c = &conn->proto.pop3c;
416   size_t i;
417   struct MD5_context *ctxt;
418   unsigned char digest[MD5_DIGEST_LEN];
419   char secret[2 * MD5_DIGEST_LEN + 1];
420
421   /* Check we have a username and password to authenticate with and end the
422      connect phase if we don't */
423   if(!conn->bits.user_passwd) {
424     state(conn, POP3_STOP);
425
426     return result;
427   }
428
429   /* Create the digest */
430   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
431   if(!ctxt)
432     return CURLE_OUT_OF_MEMORY;
433
434   Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
435                   curlx_uztoui(strlen(pop3c->apoptimestamp)));
436
437   Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
438                   curlx_uztoui(strlen(conn->passwd)));
439
440   /* Finalise the digest */
441   Curl_MD5_final(ctxt, digest);
442
443   /* Convert the calculated 16 octet digest into a 32 byte hex string */
444   for(i = 0; i < MD5_DIGEST_LEN; i++)
445     msnprintf(&secret[2 * i], 3, "%02x", digest[i]);
446
447   result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
448
449   if(!result)
450     state(conn, POP3_APOP);
451
452   return result;
453 }
454 #endif
455
456 /***********************************************************************
457  *
458  * pop3_perform_auth()
459  *
460  * Sends an AUTH command allowing the client to login with the given SASL
461  * authentication mechanism.
462  */
463 static CURLcode pop3_perform_auth(struct connectdata *conn,
464                                   const char *mech,
465                                   const char *initresp)
466 {
467   CURLcode result = CURLE_OK;
468   struct pop3_conn *pop3c = &conn->proto.pop3c;
469
470   if(initresp) {                                  /* AUTH <mech> ...<crlf> */
471     /* Send the AUTH command with the initial response */
472     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
473   }
474   else {
475     /* Send the AUTH command */
476     result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
477   }
478
479   return result;
480 }
481
482 /***********************************************************************
483  *
484  * pop3_continue_auth()
485  *
486  * Sends SASL continuation data or cancellation.
487  */
488 static CURLcode pop3_continue_auth(struct connectdata *conn,
489                                    const char *resp)
490 {
491   struct pop3_conn *pop3c = &conn->proto.pop3c;
492
493   return Curl_pp_sendf(&pop3c->pp, "%s", resp);
494 }
495
496 /***********************************************************************
497  *
498  * pop3_perform_authentication()
499  *
500  * Initiates the authentication sequence, with the appropriate SASL
501  * authentication mechanism, falling back to APOP and clear text should a
502  * common mechanism not be available between the client and server.
503  */
504 static CURLcode pop3_perform_authentication(struct connectdata *conn)
505 {
506   CURLcode result = CURLE_OK;
507   struct pop3_conn *pop3c = &conn->proto.pop3c;
508   saslprogress progress = SASL_IDLE;
509
510   /* Check we have enough data to authenticate with and end the
511      connect phase if we don't */
512   if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) {
513     state(conn, POP3_STOP);
514     return result;
515   }
516
517   if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
518     /* Calculate the SASL login details */
519     result = Curl_sasl_start(&pop3c->sasl, conn, FALSE, &progress);
520
521     if(!result)
522       if(progress == SASL_INPROGRESS)
523         state(conn, POP3_AUTH);
524   }
525
526   if(!result && progress == SASL_IDLE) {
527 #ifndef CURL_DISABLE_CRYPTO_AUTH
528     if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
529       /* Perform APOP authentication */
530       result = pop3_perform_apop(conn);
531     else
532 #endif
533     if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
534       /* Perform clear text authentication */
535       result = pop3_perform_user(conn);
536     else {
537       /* Other mechanisms not supported */
538       infof(conn->data, "No known authentication mechanisms supported!\n");
539       result = CURLE_LOGIN_DENIED;
540     }
541   }
542
543   return result;
544 }
545
546 /***********************************************************************
547  *
548  * pop3_perform_command()
549  *
550  * Sends a POP3 based command.
551  */
552 static CURLcode pop3_perform_command(struct connectdata *conn)
553 {
554   CURLcode result = CURLE_OK;
555   struct Curl_easy *data = conn->data;
556   struct POP3 *pop3 = data->req.protop;
557   const char *command = NULL;
558
559   /* Calculate the default command */
560   if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
561     command = "LIST";
562
563     if(pop3->id[0] != '\0')
564       /* Message specific LIST so skip the BODY transfer */
565       pop3->transfer = FTPTRANSFER_INFO;
566   }
567   else
568     command = "RETR";
569
570   /* Send the command */
571   if(pop3->id[0] != '\0')
572     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
573                            (pop3->custom && pop3->custom[0] != '\0' ?
574                             pop3->custom : command), pop3->id);
575   else
576     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s",
577                            (pop3->custom && pop3->custom[0] != '\0' ?
578                             pop3->custom : command));
579
580   if(!result)
581     state(conn, POP3_COMMAND);
582
583   return result;
584 }
585
586 /***********************************************************************
587  *
588  * pop3_perform_quit()
589  *
590  * Performs the quit action prior to sclose() be called.
591  */
592 static CURLcode pop3_perform_quit(struct connectdata *conn)
593 {
594   /* Send the QUIT command */
595   CURLcode result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT");
596
597   if(!result)
598     state(conn, POP3_QUIT);
599
600   return result;
601 }
602
603 /* For the initial server greeting */
604 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
605                                             int pop3code,
606                                             pop3state instate)
607 {
608   CURLcode result = CURLE_OK;
609   struct Curl_easy *data = conn->data;
610   struct pop3_conn *pop3c = &conn->proto.pop3c;
611   const char *line = data->state.buffer;
612   size_t len = strlen(line);
613
614   (void)instate; /* no use for this yet */
615
616   if(pop3code != '+') {
617     failf(data, "Got unexpected pop3-server response");
618     result = CURLE_WEIRD_SERVER_REPLY;
619   }
620   else {
621     /* Does the server support APOP authentication? */
622     if(len >= 4 && line[len - 2] == '>') {
623       /* Look for the APOP timestamp */
624       size_t i;
625       for(i = 3; i < len - 2; ++i) {
626         if(line[i] == '<') {
627           /* Calculate the length of the timestamp */
628           size_t timestamplen = len - 1 - i;
629           char *at;
630           if(!timestamplen)
631             break;
632
633           /* Allocate some memory for the timestamp */
634           pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
635
636           if(!pop3c->apoptimestamp)
637             break;
638
639           /* Copy the timestamp */
640           memcpy(pop3c->apoptimestamp, line + i, timestamplen);
641           pop3c->apoptimestamp[timestamplen] = '\0';
642
643           /* If the timestamp does not contain '@' it is not (as required by
644              RFC-1939) conformant to the RFC-822 message id syntax, and we
645              therefore do not use APOP authentication. */
646           at = strchr(pop3c->apoptimestamp, '@');
647           if(!at)
648             Curl_safefree(pop3c->apoptimestamp);
649           else
650             /* Store the APOP capability */
651             pop3c->authtypes |= POP3_TYPE_APOP;
652           break;
653         }
654       }
655     }
656
657     result = pop3_perform_capa(conn);
658   }
659
660   return result;
661 }
662
663 /* For CAPA responses */
664 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
665                                      pop3state instate)
666 {
667   CURLcode result = CURLE_OK;
668   struct Curl_easy *data = conn->data;
669   struct pop3_conn *pop3c = &conn->proto.pop3c;
670   const char *line = data->state.buffer;
671   size_t len = strlen(line);
672
673   (void)instate; /* no use for this yet */
674
675   /* Do we have a untagged continuation response? */
676   if(pop3code == '*') {
677     /* Does the server support the STLS capability? */
678     if(len >= 4 && !memcmp(line, "STLS", 4))
679       pop3c->tls_supported = TRUE;
680
681     /* Does the server support clear text authentication? */
682     else if(len >= 4 && !memcmp(line, "USER", 4))
683       pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
684
685     /* Does the server support SASL based authentication? */
686     else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
687       pop3c->authtypes |= POP3_TYPE_SASL;
688
689       /* Advance past the SASL keyword */
690       line += 5;
691       len -= 5;
692
693       /* Loop through the data line */
694       for(;;) {
695         size_t llen;
696         size_t wordlen;
697         unsigned int mechbit;
698
699         while(len &&
700               (*line == ' ' || *line == '\t' ||
701                *line == '\r' || *line == '\n')) {
702
703           line++;
704           len--;
705         }
706
707         if(!len)
708           break;
709
710         /* Extract the word */
711         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
712               line[wordlen] != '\t' && line[wordlen] != '\r' &&
713               line[wordlen] != '\n';)
714           wordlen++;
715
716         /* Test the word for a matching authentication mechanism */
717         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
718         if(mechbit && llen == wordlen)
719           pop3c->sasl.authmechs |= mechbit;
720
721         line += wordlen;
722         len -= wordlen;
723       }
724     }
725   }
726   else if(pop3code == '+') {
727     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
728       /* We don't have a SSL/TLS connection yet, but SSL is requested */
729       if(pop3c->tls_supported)
730         /* Switch to TLS connection now */
731         result = pop3_perform_starttls(conn);
732       else if(data->set.use_ssl == CURLUSESSL_TRY)
733         /* Fallback and carry on with authentication */
734         result = pop3_perform_authentication(conn);
735       else {
736         failf(data, "STLS not supported.");
737         result = CURLE_USE_SSL_FAILED;
738       }
739     }
740     else
741       result = pop3_perform_authentication(conn);
742   }
743   else {
744     /* Clear text is supported when CAPA isn't recognised */
745     pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
746
747     result = pop3_perform_authentication(conn);
748   }
749
750   return result;
751 }
752
753 /* For STARTTLS responses */
754 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
755                                          int pop3code,
756                                          pop3state instate)
757 {
758   CURLcode result = CURLE_OK;
759   struct Curl_easy *data = conn->data;
760
761   (void)instate; /* no use for this yet */
762
763   if(pop3code != '+') {
764     if(data->set.use_ssl != CURLUSESSL_TRY) {
765       failf(data, "STARTTLS denied");
766       result = CURLE_USE_SSL_FAILED;
767     }
768     else
769       result = pop3_perform_authentication(conn);
770   }
771   else
772     result = pop3_perform_upgrade_tls(conn);
773
774   return result;
775 }
776
777 /* For SASL authentication responses */
778 static CURLcode pop3_state_auth_resp(struct connectdata *conn,
779                                      int pop3code,
780                                      pop3state instate)
781 {
782   CURLcode result = CURLE_OK;
783   struct Curl_easy *data = conn->data;
784   struct pop3_conn *pop3c = &conn->proto.pop3c;
785   saslprogress progress;
786
787   (void)instate; /* no use for this yet */
788
789   result = Curl_sasl_continue(&pop3c->sasl, conn, pop3code, &progress);
790   if(!result)
791     switch(progress) {
792     case SASL_DONE:
793       state(conn, POP3_STOP);  /* Authenticated */
794       break;
795     case SASL_IDLE:            /* No mechanism left after cancellation */
796 #ifndef CURL_DISABLE_CRYPTO_AUTH
797       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
798         /* Perform APOP authentication */
799         result = pop3_perform_apop(conn);
800       else
801 #endif
802       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
803         /* Perform clear text authentication */
804         result = pop3_perform_user(conn);
805       else {
806         failf(data, "Authentication cancelled");
807         result = CURLE_LOGIN_DENIED;
808       }
809       break;
810     default:
811       break;
812     }
813
814   return result;
815 }
816
817 #ifndef CURL_DISABLE_CRYPTO_AUTH
818 /* For APOP responses */
819 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
820                                      pop3state instate)
821 {
822   CURLcode result = CURLE_OK;
823   struct Curl_easy *data = conn->data;
824
825   (void)instate; /* no use for this yet */
826
827   if(pop3code != '+') {
828     failf(data, "Authentication failed: %d", pop3code);
829     result = CURLE_LOGIN_DENIED;
830   }
831   else
832     /* End of connect phase */
833     state(conn, POP3_STOP);
834
835   return result;
836 }
837 #endif
838
839 /* For USER responses */
840 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
841                                      pop3state instate)
842 {
843   CURLcode result = CURLE_OK;
844   struct Curl_easy *data = conn->data;
845
846   (void)instate; /* no use for this yet */
847
848   if(pop3code != '+') {
849     failf(data, "Access denied. %c", pop3code);
850     result = CURLE_LOGIN_DENIED;
851   }
852   else
853     /* Send the PASS command */
854     result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
855                            conn->passwd ? conn->passwd : "");
856   if(!result)
857     state(conn, POP3_PASS);
858
859   return result;
860 }
861
862 /* For PASS responses */
863 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
864                                      pop3state instate)
865 {
866   CURLcode result = CURLE_OK;
867   struct Curl_easy *data = conn->data;
868
869   (void)instate; /* no use for this yet */
870
871   if(pop3code != '+') {
872     failf(data, "Access denied. %c", pop3code);
873     result = CURLE_LOGIN_DENIED;
874   }
875   else
876     /* End of connect phase */
877     state(conn, POP3_STOP);
878
879   return result;
880 }
881
882 /* For command responses */
883 static CURLcode pop3_state_command_resp(struct connectdata *conn,
884                                         int pop3code,
885                                         pop3state instate)
886 {
887   CURLcode result = CURLE_OK;
888   struct Curl_easy *data = conn->data;
889   struct POP3 *pop3 = data->req.protop;
890   struct pop3_conn *pop3c = &conn->proto.pop3c;
891   struct pingpong *pp = &pop3c->pp;
892
893   (void)instate; /* no use for this yet */
894
895   if(pop3code != '+') {
896     state(conn, POP3_STOP);
897     return CURLE_RECV_ERROR;
898   }
899
900   /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
901      EOB string so count this is two matching bytes. This is necessary to make
902      the code detect the EOB if the only data than comes now is %2e CR LF like
903      when there is no body to return. */
904   pop3c->eob = 2;
905
906   /* But since this initial CR LF pair is not part of the actual body, we set
907      the strip counter here so that these bytes won't be delivered. */
908   pop3c->strip = 2;
909
910   if(pop3->transfer == FTPTRANSFER_BODY) {
911     /* POP3 download */
912     Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
913
914     if(pp->cache) {
915       /* The header "cache" contains a bunch of data that is actually body
916          content so send it as such. Note that there may even be additional
917          "headers" after the body */
918
919       if(!data->set.opt_no_body) {
920         result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
921         if(result)
922           return result;
923       }
924
925       /* Free the cache */
926       Curl_safefree(pp->cache);
927
928       /* Reset the cache size */
929       pp->cache_size = 0;
930     }
931   }
932
933   /* End of DO phase */
934   state(conn, POP3_STOP);
935
936   return result;
937 }
938
939 static CURLcode pop3_statemach_act(struct connectdata *conn)
940 {
941   CURLcode result = CURLE_OK;
942   curl_socket_t sock = conn->sock[FIRSTSOCKET];
943   int pop3code;
944   struct pop3_conn *pop3c = &conn->proto.pop3c;
945   struct pingpong *pp = &pop3c->pp;
946   size_t nread = 0;
947
948   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
949   if(pop3c->state == POP3_UPGRADETLS)
950     return pop3_perform_upgrade_tls(conn);
951
952   /* Flush any data that needs to be sent */
953   if(pp->sendleft)
954     return Curl_pp_flushsend(pp);
955
956  do {
957     /* Read the response from the server */
958     result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
959     if(result)
960       return result;
961
962     if(!pop3code)
963       break;
964
965     /* We have now received a full POP3 server response */
966     switch(pop3c->state) {
967     case POP3_SERVERGREET:
968       result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
969       break;
970
971     case POP3_CAPA:
972       result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
973       break;
974
975     case POP3_STARTTLS:
976       result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
977       break;
978
979     case POP3_AUTH:
980       result = pop3_state_auth_resp(conn, pop3code, pop3c->state);
981       break;
982
983 #ifndef CURL_DISABLE_CRYPTO_AUTH
984     case POP3_APOP:
985       result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
986       break;
987 #endif
988
989     case POP3_USER:
990       result = pop3_state_user_resp(conn, pop3code, pop3c->state);
991       break;
992
993     case POP3_PASS:
994       result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
995       break;
996
997     case POP3_COMMAND:
998       result = pop3_state_command_resp(conn, pop3code, pop3c->state);
999       break;
1000
1001     case POP3_QUIT:
1002       /* fallthrough, just stop! */
1003     default:
1004       /* internal error */
1005       state(conn, POP3_STOP);
1006       break;
1007     }
1008   } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1009
1010   return result;
1011 }
1012
1013 /* Called repeatedly until done from multi.c */
1014 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1015 {
1016   CURLcode result = CURLE_OK;
1017   struct pop3_conn *pop3c = &conn->proto.pop3c;
1018
1019   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1020     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1021     if(result || !pop3c->ssldone)
1022       return result;
1023   }
1024
1025   result = Curl_pp_statemach(&pop3c->pp, FALSE, FALSE);
1026   *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1027
1028   return result;
1029 }
1030
1031 static CURLcode pop3_block_statemach(struct connectdata *conn,
1032                                      bool disconnecting)
1033 {
1034   CURLcode result = CURLE_OK;
1035   struct pop3_conn *pop3c = &conn->proto.pop3c;
1036
1037   while(pop3c->state != POP3_STOP && !result)
1038     result = Curl_pp_statemach(&pop3c->pp, TRUE, disconnecting);
1039
1040   return result;
1041 }
1042
1043 /* Allocate and initialize the POP3 struct for the current Curl_easy if
1044    required */
1045 static CURLcode pop3_init(struct connectdata *conn)
1046 {
1047   CURLcode result = CURLE_OK;
1048   struct Curl_easy *data = conn->data;
1049   struct POP3 *pop3;
1050
1051   pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
1052   if(!pop3)
1053     result = CURLE_OUT_OF_MEMORY;
1054
1055   return result;
1056 }
1057
1058 /* For the POP3 "protocol connect" and "doing" phases only */
1059 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks)
1060 {
1061   return Curl_pp_getsock(&conn->proto.pop3c.pp, socks);
1062 }
1063
1064 /***********************************************************************
1065  *
1066  * pop3_connect()
1067  *
1068  * This function should do everything that is to be considered a part of the
1069  * connection phase.
1070  *
1071  * The variable 'done' points to will be TRUE if the protocol-layer connect
1072  * phase is done when this function returns, or FALSE if not.
1073  */
1074 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1075 {
1076   CURLcode result = CURLE_OK;
1077   struct pop3_conn *pop3c = &conn->proto.pop3c;
1078   struct pingpong *pp = &pop3c->pp;
1079
1080   *done = FALSE; /* default to not done yet */
1081
1082   /* We always support persistent connections in POP3 */
1083   connkeep(conn, "POP3 default");
1084
1085   /* Set the default response time-out */
1086   pp->response_time = RESP_TIMEOUT;
1087   pp->statemach_act = pop3_statemach_act;
1088   pp->endofresp = pop3_endofresp;
1089   pp->conn = conn;
1090
1091   /* Set the default preferred authentication type and mechanism */
1092   pop3c->preftype = POP3_TYPE_ANY;
1093   Curl_sasl_init(&pop3c->sasl, &saslpop3);
1094
1095   /* Initialise the pingpong layer */
1096   Curl_pp_setup(pp);
1097   Curl_pp_init(pp);
1098
1099   /* Parse the URL options */
1100   result = pop3_parse_url_options(conn);
1101   if(result)
1102     return result;
1103
1104   /* Start off waiting for the server greeting response */
1105   state(conn, POP3_SERVERGREET);
1106
1107   result = pop3_multi_statemach(conn, done);
1108
1109   return result;
1110 }
1111
1112 /***********************************************************************
1113  *
1114  * pop3_done()
1115  *
1116  * The DONE function. This does what needs to be done after a single DO has
1117  * performed.
1118  *
1119  * Input argument is already checked for validity.
1120  */
1121 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1122                           bool premature)
1123 {
1124   CURLcode result = CURLE_OK;
1125   struct Curl_easy *data = conn->data;
1126   struct POP3 *pop3 = data->req.protop;
1127
1128   (void)premature;
1129
1130   if(!pop3)
1131     return CURLE_OK;
1132
1133   if(status) {
1134     connclose(conn, "POP3 done with bad status");
1135     result = status;         /* use the already set error code */
1136   }
1137
1138   /* Cleanup our per-request based variables */
1139   Curl_safefree(pop3->id);
1140   Curl_safefree(pop3->custom);
1141
1142   /* Clear the transfer mode for the next request */
1143   pop3->transfer = FTPTRANSFER_BODY;
1144
1145   return result;
1146 }
1147
1148 /***********************************************************************
1149  *
1150  * pop3_perform()
1151  *
1152  * This is the actual DO function for POP3. Get a message/listing according to
1153  * the options previously setup.
1154  */
1155 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1156                              bool *dophase_done)
1157 {
1158   /* This is POP3 and no proxy */
1159   CURLcode result = CURLE_OK;
1160   struct POP3 *pop3 = conn->data->req.protop;
1161
1162   DEBUGF(infof(conn->data, "DO phase starts\n"));
1163
1164   if(conn->data->set.opt_no_body) {
1165     /* Requested no body means no transfer */
1166     pop3->transfer = FTPTRANSFER_INFO;
1167   }
1168
1169   *dophase_done = FALSE; /* not done yet */
1170
1171   /* Start the first command in the DO phase */
1172   result = pop3_perform_command(conn);
1173   if(result)
1174     return result;
1175
1176   /* Run the state-machine */
1177   result = pop3_multi_statemach(conn, dophase_done);
1178
1179   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1180
1181   if(*dophase_done)
1182     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1183
1184   return result;
1185 }
1186
1187 /***********************************************************************
1188  *
1189  * pop3_do()
1190  *
1191  * This function is registered as 'curl_do' function. It decodes the path
1192  * parts etc as a wrapper to the actual DO function (pop3_perform).
1193  *
1194  * The input argument is already checked for validity.
1195  */
1196 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1197 {
1198   CURLcode result = CURLE_OK;
1199
1200   *done = FALSE; /* default to false */
1201
1202   /* Parse the URL path */
1203   result = pop3_parse_url_path(conn);
1204   if(result)
1205     return result;
1206
1207   /* Parse the custom request */
1208   result = pop3_parse_custom_request(conn);
1209   if(result)
1210     return result;
1211
1212   result = pop3_regular_transfer(conn, done);
1213
1214   return result;
1215 }
1216
1217 /***********************************************************************
1218  *
1219  * pop3_disconnect()
1220  *
1221  * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1222  * resources. BLOCKING.
1223  */
1224 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
1225 {
1226   struct pop3_conn *pop3c = &conn->proto.pop3c;
1227
1228   /* We cannot send quit unconditionally. If this connection is stale or
1229      bad in any way, sending quit and waiting around here will make the
1230      disconnect wait in vain and cause more problems than we need to. */
1231
1232   /* The POP3 session may or may not have been allocated/setup at this
1233      point! */
1234   if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
1235     if(!pop3_perform_quit(conn))
1236       (void)pop3_block_statemach(conn, TRUE); /* ignore errors on QUIT */
1237
1238   /* Disconnect from the server */
1239   Curl_pp_disconnect(&pop3c->pp);
1240
1241   /* Cleanup the SASL module */
1242   Curl_sasl_cleanup(conn, pop3c->sasl.authused);
1243
1244   /* Cleanup our connection based variables */
1245   Curl_safefree(pop3c->apoptimestamp);
1246
1247   return CURLE_OK;
1248 }
1249
1250 /* Call this when the DO phase has completed */
1251 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1252 {
1253   (void)conn;
1254   (void)connected;
1255
1256   return CURLE_OK;
1257 }
1258
1259 /* Called from multi.c while DOing */
1260 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1261 {
1262   CURLcode result = pop3_multi_statemach(conn, dophase_done);
1263
1264   if(result)
1265     DEBUGF(infof(conn->data, "DO phase failed\n"));
1266   else if(*dophase_done) {
1267     result = pop3_dophase_done(conn, FALSE /* not connected */);
1268
1269     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1270   }
1271
1272   return result;
1273 }
1274
1275 /***********************************************************************
1276  *
1277  * pop3_regular_transfer()
1278  *
1279  * The input argument is already checked for validity.
1280  *
1281  * Performs all commands done before a regular transfer between a local and a
1282  * remote host.
1283  */
1284 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1285                                       bool *dophase_done)
1286 {
1287   CURLcode result = CURLE_OK;
1288   bool connected = FALSE;
1289   struct Curl_easy *data = conn->data;
1290
1291   /* Make sure size is unknown at this point */
1292   data->req.size = -1;
1293
1294   /* Set the progress data */
1295   Curl_pgrsSetUploadCounter(data, 0);
1296   Curl_pgrsSetDownloadCounter(data, 0);
1297   Curl_pgrsSetUploadSize(data, -1);
1298   Curl_pgrsSetDownloadSize(data, -1);
1299
1300   /* Carry out the perform */
1301   result = pop3_perform(conn, &connected, dophase_done);
1302
1303   /* Perform post DO phase operations if necessary */
1304   if(!result && *dophase_done)
1305     result = pop3_dophase_done(conn, connected);
1306
1307   return result;
1308 }
1309
1310 static CURLcode pop3_setup_connection(struct connectdata *conn)
1311 {
1312   /* Initialise the POP3 layer */
1313   CURLcode result = pop3_init(conn);
1314   if(result)
1315     return result;
1316
1317   /* Clear the TLS upgraded flag */
1318   conn->bits.tls_upgraded = FALSE;
1319
1320   return CURLE_OK;
1321 }
1322
1323 /***********************************************************************
1324  *
1325  * pop3_parse_url_options()
1326  *
1327  * Parse the URL login options.
1328  */
1329 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1330 {
1331   CURLcode result = CURLE_OK;
1332   struct pop3_conn *pop3c = &conn->proto.pop3c;
1333   const char *ptr = conn->options;
1334
1335   pop3c->sasl.resetprefs = TRUE;
1336
1337   while(!result && ptr && *ptr) {
1338     const char *key = ptr;
1339     const char *value;
1340
1341     while(*ptr && *ptr != '=')
1342         ptr++;
1343
1344     value = ptr + 1;
1345
1346     while(*ptr && *ptr != ';')
1347       ptr++;
1348
1349     if(strncasecompare(key, "AUTH=", 5)) {
1350       result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
1351                                                value, ptr - value);
1352
1353       if(result && strncasecompare(value, "+APOP", ptr - value)) {
1354         pop3c->preftype = POP3_TYPE_APOP;
1355         pop3c->sasl.prefmech = SASL_AUTH_NONE;
1356         result = CURLE_OK;
1357       }
1358     }
1359     else
1360       result = CURLE_URL_MALFORMAT;
1361
1362     if(*ptr == ';')
1363       ptr++;
1364   }
1365
1366   if(pop3c->preftype != POP3_TYPE_APOP)
1367     switch(pop3c->sasl.prefmech) {
1368     case SASL_AUTH_NONE:
1369       pop3c->preftype = POP3_TYPE_NONE;
1370       break;
1371     case SASL_AUTH_DEFAULT:
1372       pop3c->preftype = POP3_TYPE_ANY;
1373       break;
1374     default:
1375       pop3c->preftype = POP3_TYPE_SASL;
1376       break;
1377     }
1378
1379   return result;
1380 }
1381
1382 /***********************************************************************
1383  *
1384  * pop3_parse_url_path()
1385  *
1386  * Parse the URL path into separate path components.
1387  */
1388 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1389 {
1390   /* The POP3 struct is already initialised in pop3_connect() */
1391   struct Curl_easy *data = conn->data;
1392   struct POP3 *pop3 = data->req.protop;
1393   const char *path = &data->state.up.path[1]; /* skip leading path */
1394
1395   /* URL decode the path for the message ID */
1396   return Curl_urldecode(data, path, 0, &pop3->id, NULL, REJECT_CTRL);
1397 }
1398
1399 /***********************************************************************
1400  *
1401  * pop3_parse_custom_request()
1402  *
1403  * Parse the custom request.
1404  */
1405 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1406 {
1407   CURLcode result = CURLE_OK;
1408   struct Curl_easy *data = conn->data;
1409   struct POP3 *pop3 = data->req.protop;
1410   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1411
1412   /* URL decode the custom request */
1413   if(custom)
1414     result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, REJECT_CTRL);
1415
1416   return result;
1417 }
1418
1419 /***********************************************************************
1420  *
1421  * Curl_pop3_write()
1422  *
1423  * This function scans the body after the end-of-body and writes everything
1424  * until the end is found.
1425  */
1426 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1427 {
1428   /* This code could be made into a special function in the handler struct */
1429   CURLcode result = CURLE_OK;
1430   struct Curl_easy *data = conn->data;
1431   struct SingleRequest *k = &data->req;
1432
1433   struct pop3_conn *pop3c = &conn->proto.pop3c;
1434   bool strip_dot = FALSE;
1435   size_t last = 0;
1436   size_t i;
1437
1438   /* Search through the buffer looking for the end-of-body marker which is
1439      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1440      the eob so the server will have prefixed it with an extra dot which we
1441      need to strip out. Additionally the marker could of course be spread out
1442      over 5 different data chunks. */
1443   for(i = 0; i < nread; i++) {
1444     size_t prev = pop3c->eob;
1445
1446     switch(str[i]) {
1447     case 0x0d:
1448       if(pop3c->eob == 0) {
1449         pop3c->eob++;
1450
1451         if(i) {
1452           /* Write out the body part that didn't match */
1453           result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1454                                      i - last);
1455
1456           if(result)
1457             return result;
1458
1459           last = i;
1460         }
1461       }
1462       else if(pop3c->eob == 3)
1463         pop3c->eob++;
1464       else
1465         /* If the character match wasn't at position 0 or 3 then restart the
1466            pattern matching */
1467         pop3c->eob = 1;
1468       break;
1469
1470     case 0x0a:
1471       if(pop3c->eob == 1 || pop3c->eob == 4)
1472         pop3c->eob++;
1473       else
1474         /* If the character match wasn't at position 1 or 4 then start the
1475            search again */
1476         pop3c->eob = 0;
1477       break;
1478
1479     case 0x2e:
1480       if(pop3c->eob == 2)
1481         pop3c->eob++;
1482       else if(pop3c->eob == 3) {
1483         /* We have an extra dot after the CRLF which we need to strip off */
1484         strip_dot = TRUE;
1485         pop3c->eob = 0;
1486       }
1487       else
1488         /* If the character match wasn't at position 2 then start the search
1489            again */
1490         pop3c->eob = 0;
1491       break;
1492
1493     default:
1494       pop3c->eob = 0;
1495       break;
1496     }
1497
1498     /* Did we have a partial match which has subsequently failed? */
1499     if(prev && prev >= pop3c->eob) {
1500       /* Strip can only be non-zero for the very first mismatch after CRLF
1501          and then both prev and strip are equal and nothing will be output
1502          below */
1503       while(prev && pop3c->strip) {
1504         prev--;
1505         pop3c->strip--;
1506       }
1507
1508       if(prev) {
1509         /* If the partial match was the CRLF and dot then only write the CRLF
1510            as the server would have inserted the dot */
1511         result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB,
1512                                    strip_dot ? prev - 1 : prev);
1513
1514         if(result)
1515           return result;
1516
1517         last = i;
1518         strip_dot = FALSE;
1519       }
1520     }
1521   }
1522
1523   if(pop3c->eob == POP3_EOB_LEN) {
1524     /* We have a full match so the transfer is done, however we must transfer
1525     the CRLF at the start of the EOB as this is considered to be part of the
1526     message as per RFC-1939, sect. 3 */
1527     result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1528
1529     k->keepon &= ~KEEP_RECV;
1530     pop3c->eob = 0;
1531
1532     return result;
1533   }
1534
1535   if(pop3c->eob)
1536     /* While EOB is matching nothing should be output */
1537     return CURLE_OK;
1538
1539   if(nread - last) {
1540     result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1541                                nread - last);
1542   }
1543
1544   return result;
1545 }
1546
1547 #endif /* CURL_DISABLE_POP3 */