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