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