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