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