1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2013, 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 * RFC5034 POP3 SASL Authentication Mechanism
31 * RFC6749 OAuth 2.0 Authorization Framework
32 * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
33 *
34 ***************************************************************************/
35
36#include "curl_setup.h"
37
38#ifndef CURL_DISABLE_POP3
39
40#ifdef HAVE_NETINET_IN_H
41#include <netinet/in.h>
42#endif
43#ifdef HAVE_ARPA_INET_H
44#include <arpa/inet.h>
45#endif
46#ifdef HAVE_UTSNAME_H
47#include <sys/utsname.h>
48#endif
49#ifdef HAVE_NETDB_H
50#include <netdb.h>
51#endif
52#ifdef __VMS
53#include <in.h>
54#include <inet.h>
55#endif
56
57#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
58#undef in_addr_t
59#define in_addr_t unsigned long
60#endif
61
62#include <curl/curl.h>
63#include "urldata.h"
64#include "sendf.h"
65#include "if2ip.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
87#define _MPRINTF_REPLACE /* use our functions only */
88#include <curl/mprintf.h>
89
90#include "curl_memory.h"
91/* The last #include file should be: */
92#include "memdebug.h"
93
94/* Local API functions */
95static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
96static CURLcode pop3_do(struct connectdata *conn, bool *done);
97static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
98                          bool premature);
99static CURLcode pop3_connect(struct connectdata *conn, bool *done);
100static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
101static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
102static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
103                        int numsocks);
104static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
105static CURLcode pop3_setup_connection(struct connectdata *conn);
106static CURLcode pop3_parse_url_options(struct connectdata *conn);
107static CURLcode pop3_parse_url_path(struct connectdata *conn);
108static CURLcode pop3_parse_custom_request(struct connectdata *conn);
109static CURLcode pop3_calc_sasl_details(struct connectdata *conn,
110                                       const char **mech,
111                                       char **initresp, size_t *len,
112                                       pop3state *state1, pop3state *state2);
113
114/*
115 * POP3 protocol handler.
116 */
117
118const struct Curl_handler Curl_handler_pop3 = {
119  "POP3",                           /* scheme */
120  pop3_setup_connection,            /* setup_connection */
121  pop3_do,                          /* do_it */
122  pop3_done,                        /* done */
123  ZERO_NULL,                        /* do_more */
124  pop3_connect,                     /* connect_it */
125  pop3_multi_statemach,             /* connecting */
126  pop3_doing,                       /* doing */
127  pop3_getsock,                     /* proto_getsock */
128  pop3_getsock,                     /* doing_getsock */
129  ZERO_NULL,                        /* domore_getsock */
130  ZERO_NULL,                        /* perform_getsock */
131  pop3_disconnect,                  /* disconnect */
132  ZERO_NULL,                        /* readwrite */
133  PORT_POP3,                        /* defport */
134  CURLPROTO_POP3,                   /* protocol */
135  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
136};
137
138#ifdef USE_SSL
139/*
140 * POP3S protocol handler.
141 */
142
143const struct Curl_handler Curl_handler_pop3s = {
144  "POP3S",                          /* scheme */
145  pop3_setup_connection,            /* setup_connection */
146  pop3_do,                          /* do_it */
147  pop3_done,                        /* done */
148  ZERO_NULL,                        /* do_more */
149  pop3_connect,                     /* connect_it */
150  pop3_multi_statemach,             /* connecting */
151  pop3_doing,                       /* doing */
152  pop3_getsock,                     /* proto_getsock */
153  pop3_getsock,                     /* doing_getsock */
154  ZERO_NULL,                        /* domore_getsock */
155  ZERO_NULL,                        /* perform_getsock */
156  pop3_disconnect,                  /* disconnect */
157  ZERO_NULL,                        /* readwrite */
158  PORT_POP3S,                       /* defport */
159  CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */
160  PROTOPT_CLOSEACTION | PROTOPT_SSL
161  | PROTOPT_NOURLQUERY              /* flags */
162};
163#endif
164
165#ifndef CURL_DISABLE_HTTP
166/*
167 * HTTP-proxyed POP3 protocol handler.
168 */
169
170static const struct Curl_handler Curl_handler_pop3_proxy = {
171  "POP3",                               /* scheme */
172  Curl_http_setup_conn,                 /* setup_connection */
173  Curl_http,                            /* do_it */
174  Curl_http_done,                       /* done */
175  ZERO_NULL,                            /* do_more */
176  ZERO_NULL,                            /* connect_it */
177  ZERO_NULL,                            /* connecting */
178  ZERO_NULL,                            /* doing */
179  ZERO_NULL,                            /* proto_getsock */
180  ZERO_NULL,                            /* doing_getsock */
181  ZERO_NULL,                            /* domore_getsock */
182  ZERO_NULL,                            /* perform_getsock */
183  ZERO_NULL,                            /* disconnect */
184  ZERO_NULL,                            /* readwrite */
185  PORT_POP3,                            /* defport */
186  CURLPROTO_HTTP,                       /* protocol */
187  PROTOPT_NONE                          /* flags */
188};
189
190#ifdef USE_SSL
191/*
192 * HTTP-proxyed POP3S protocol handler.
193 */
194
195static const struct Curl_handler Curl_handler_pop3s_proxy = {
196  "POP3S",                              /* scheme */
197  Curl_http_setup_conn,                 /* setup_connection */
198  Curl_http,                            /* do_it */
199  Curl_http_done,                       /* done */
200  ZERO_NULL,                            /* do_more */
201  ZERO_NULL,                            /* connect_it */
202  ZERO_NULL,                            /* connecting */
203  ZERO_NULL,                            /* doing */
204  ZERO_NULL,                            /* proto_getsock */
205  ZERO_NULL,                            /* doing_getsock */
206  ZERO_NULL,                            /* domore_getsock */
207  ZERO_NULL,                            /* perform_getsock */
208  ZERO_NULL,                            /* disconnect */
209  ZERO_NULL,                            /* readwrite */
210  PORT_POP3S,                           /* defport */
211  CURLPROTO_HTTP,                       /* protocol */
212  PROTOPT_NONE                          /* flags */
213};
214#endif
215#endif
216
217#ifdef USE_SSL
218static void pop3_to_pop3s(struct connectdata *conn)
219{
220  conn->handler = &Curl_handler_pop3s;
221}
222#else
223#define pop3_to_pop3s(x) Curl_nop_stmt
224#endif
225
226/***********************************************************************
227 *
228 * pop3_endofresp()
229 *
230 * Checks for an ending POP3 status code at the start of the given string, but
231 * also detects the APOP timestamp from the server greeting and various
232 * capabilities from the CAPA response including the supported authentication
233 * types and allowed SASL mechanisms.
234 */
235static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
236                           int *resp)
237{
238  struct pop3_conn *pop3c = &conn->proto.pop3c;
239
240  /* Do we have an error response? */
241  if(len >= 4 && !memcmp("-ERR", line, 4)) {
242    *resp = '-';
243
244    return TRUE;
245  }
246
247  /* Are we processing CAPA command responses? */
248  if(pop3c->state == POP3_CAPA) {
249    /* Do we have the terminating line? */
250    if(len >= 1 && !memcmp(line, ".", 1))
251      *resp = '+';
252    else
253      *resp = '*';
254
255    return TRUE;
256  }
257
258  /* Do we have a command or continuation response? */
259  if((len >= 3 && !memcmp("+OK", line, 3)) ||
260     (len >= 1 && !memcmp("+", line, 1))) {
261    *resp = '+';
262
263    return TRUE;
264  }
265
266  return FALSE; /* Nothing for us */
267}
268
269/***********************************************************************
270 *
271 * pop3_get_message()
272 *
273 * Gets the authentication message from the response buffer.
274 */
275static void pop3_get_message(char *buffer, char** outptr)
276{
277  size_t len = 0;
278  char* message = NULL;
279
280  /* Find the start of the message */
281  for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
282    ;
283
284  /* Find the end of the message */
285  for(len = strlen(message); len--;)
286    if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
287        message[len] != '\t')
288      break;
289
290  /* Terminate the message */
291  if(++len) {
292    message[len] = '\0';
293  }
294
295  *outptr = message;
296}
297
298/***********************************************************************
299 *
300 * state()
301 *
302 * This is the ONLY way to change POP3 state!
303 */
304static void state(struct connectdata *conn, pop3state newstate)
305{
306  struct pop3_conn *pop3c = &conn->proto.pop3c;
307#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
308  /* for debug purposes */
309  static const char * const names[] = {
310    "STOP",
311    "SERVERGREET",
312    "CAPA",
313    "STARTTLS",
314    "UPGRADETLS",
315    "AUTH_PLAIN",
316    "AUTH_LOGIN",
317    "AUTH_LOGIN_PASSWD",
318    "AUTH_CRAMMD5",
319    "AUTH_DIGESTMD5",
320    "AUTH_DIGESTMD5_RESP",
321    "AUTH_NTLM",
322    "AUTH_NTLM_TYPE2MSG",
323    "AUTH_XOAUTH2",
324    "AUTH_CANCEL",
325    "AUTH_FINAL",
326    "APOP",
327    "USER",
328    "PASS",
329    "COMMAND",
330    "QUIT",
331    /* LAST */
332  };
333
334  if(pop3c->state != newstate)
335    infof(conn->data, "POP3 %p state change from %s to %s\n",
336          (void *)pop3c, names[pop3c->state], names[newstate]);
337#endif
338
339  pop3c->state = newstate;
340}
341
342/***********************************************************************
343 *
344 * pop3_perform_capa()
345 *
346 * Sends the CAPA command in order to obtain a list of server side supported
347 * capabilities.
348 */
349static CURLcode pop3_perform_capa(struct connectdata *conn)
350{
351  CURLcode result = CURLE_OK;
352  struct pop3_conn *pop3c = &conn->proto.pop3c;
353
354  pop3c->authmechs = 0;         /* No known authentication mechanisms yet */
355  pop3c->authused = 0;          /* Clear the authentication mechanism used */
356  pop3c->tls_supported = FALSE; /* Clear the TLS capability */
357
358  /* Send the CAPA command */
359  result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA");
360
361  if(!result)
362    state(conn, POP3_CAPA);
363
364  return result;
365}
366
367/***********************************************************************
368 *
369 * pop3_perform_starttls()
370 *
371 * Sends the STLS command to start the upgrade to TLS.
372 */
373static CURLcode pop3_perform_starttls(struct connectdata *conn)
374{
375  CURLcode result = CURLE_OK;
376
377  /* Send the STLS command */
378  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS");
379
380  if(!result)
381    state(conn, POP3_STARTTLS);
382
383  return result;
384}
385
386/***********************************************************************
387 *
388 * pop3_perform_upgrade_tls()
389 *
390 * Performs the upgrade to TLS.
391 */
392static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
393{
394  CURLcode result = CURLE_OK;
395  struct pop3_conn *pop3c = &conn->proto.pop3c;
396
397  /* Start the SSL connection */
398  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
399
400  if(!result) {
401    if(pop3c->state != POP3_UPGRADETLS)
402      state(conn, POP3_UPGRADETLS);
403
404    if(pop3c->ssldone) {
405      pop3_to_pop3s(conn);
406      result = pop3_perform_capa(conn);
407    }
408  }
409
410  return result;
411}
412
413/***********************************************************************
414 *
415 * pop3_perform_user()
416 *
417 * Sends a clear text USER command to authenticate with.
418 */
419static CURLcode pop3_perform_user(struct connectdata *conn)
420{
421  CURLcode result = CURLE_OK;
422
423  /* Check we have a username and password to authenticate with and end the
424     connect phase if we don't */
425  if(!conn->bits.user_passwd) {
426    state(conn, POP3_STOP);
427
428    return result;
429  }
430
431  /* Send the USER command */
432  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
433                         conn->user ? conn->user : "");
434  if(!result)
435    state(conn, POP3_USER);
436
437  return result;
438}
439
440#ifndef CURL_DISABLE_CRYPTO_AUTH
441/***********************************************************************
442 *
443 * pop3_perform_apop()
444 *
445 * Sends an APOP command to authenticate with.
446 */
447static CURLcode pop3_perform_apop(struct connectdata *conn)
448{
449  CURLcode result = CURLE_OK;
450  struct pop3_conn *pop3c = &conn->proto.pop3c;
451  size_t i;
452  MD5_context *ctxt;
453  unsigned char digest[MD5_DIGEST_LEN];
454  char secret[2 * MD5_DIGEST_LEN + 1];
455
456  /* Check we have a username and password to authenticate with and end the
457     connect phase if we don't */
458  if(!conn->bits.user_passwd) {
459    state(conn, POP3_STOP);
460
461    return result;
462  }
463
464  /* Create the digest */
465  ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
466  if(!ctxt)
467    return CURLE_OUT_OF_MEMORY;
468
469  Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
470                  curlx_uztoui(strlen(pop3c->apoptimestamp)));
471
472  Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
473                  curlx_uztoui(strlen(conn->passwd)));
474
475  /* Finalise the digest */
476  Curl_MD5_final(ctxt, digest);
477
478  /* Convert the calculated 16 octet digest into a 32 byte hex string */
479  for(i = 0; i < MD5_DIGEST_LEN; i++)
480    snprintf(&secret[2 * i], 3, "%02x", digest[i]);
481
482  result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
483
484  if(!result)
485    state(conn, POP3_APOP);
486
487  return result;
488}
489#endif
490
491/***********************************************************************
492 *
493 * pop3_perform_auth()
494 *
495 * Sends an AUTH command allowing the client to login with the given SASL
496 * authentication mechanism.
497 */
498static CURLcode pop3_perform_auth(struct connectdata *conn,
499                                  const char *mech,
500                                  const char *initresp, size_t len,
501                                  pop3state state1, pop3state state2)
502{
503  CURLcode result = CURLE_OK;
504  struct pop3_conn *pop3c = &conn->proto.pop3c;
505
506  if(initresp && 8 + strlen(mech) + len <= 255) { /* AUTH <mech> ...<crlf> */
507    /* Send the AUTH command with the initial response */
508    result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
509
510    if(!result)
511      state(conn, state2);
512  }
513  else {
514    /* Send the AUTH command */
515    result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
516
517    if(!result)
518      state(conn, state1);
519  }
520
521  return result;
522}
523
524/***********************************************************************
525 *
526 * pop3_perform_authentication()
527 *
528 * Initiates the authentication sequence, with the appropriate SASL
529 * authentication mechanism, falling back to APOP and clear text should a
530 * common mechanism not be available between the client and server.
531 */
532static CURLcode pop3_perform_authentication(struct connectdata *conn)
533{
534  CURLcode result = CURLE_OK;
535  struct pop3_conn *pop3c = &conn->proto.pop3c;
536  const char *mech = NULL;
537  char *initresp = NULL;
538  size_t len = 0;
539  pop3state state1 = POP3_STOP;
540  pop3state state2 = POP3_STOP;
541
542  /* Check we have a username and password to authenticate with and end the
543     connect phase if we don't */
544  if(!conn->bits.user_passwd) {
545    state(conn, POP3_STOP);
546
547    return result;
548  }
549
550  /* Calculate the SASL login details */
551  if(pop3c->authtypes & POP3_TYPE_SASL)
552    result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
553                                    &state2);
554
555  if(!result) {
556    if(mech && (pop3c->preftype & POP3_TYPE_SASL)) {
557      /* Perform SASL based authentication */
558      result = pop3_perform_auth(conn, mech, initresp, len, state1, state2);
559
560      Curl_safefree(initresp);
561    }
562#ifndef CURL_DISABLE_CRYPTO_AUTH
563    else if((pop3c->authtypes & POP3_TYPE_APOP) &&
564            (pop3c->preftype & POP3_TYPE_APOP))
565      /* Perform APOP authentication */
566      result = pop3_perform_apop(conn);
567#endif
568    else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
569            (pop3c->preftype & POP3_TYPE_CLEARTEXT))
570      /* Perform clear text authentication */
571      result = pop3_perform_user(conn);
572    else {
573      /* Other mechanisms not supported */
574      infof(conn->data, "No known authentication mechanisms supported!\n");
575      result = CURLE_LOGIN_DENIED;
576    }
577  }
578
579  return result;
580}
581
582/***********************************************************************
583 *
584 * pop3_perform_command()
585 *
586 * Sends a POP3 based command.
587 */
588static CURLcode pop3_perform_command(struct connectdata *conn)
589{
590  CURLcode result = CURLE_OK;
591  struct SessionHandle *data = conn->data;
592  struct POP3 *pop3 = data->req.protop;
593  const char *command = NULL;
594
595  /* Calculate the default command */
596  if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
597    command = "LIST";
598
599    if(pop3->id[0] != '\0')
600      /* Message specific LIST so skip the BODY transfer */
601      pop3->transfer = FTPTRANSFER_INFO;
602  }
603  else
604    command = "RETR";
605
606  /* Send the command */
607  if(pop3->id[0] != '\0')
608    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
609                           (pop3->custom && pop3->custom[0] != '\0' ?
610                            pop3->custom : command), pop3->id);
611  else
612    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s",
613                           (pop3->custom && pop3->custom[0] != '\0' ?
614                            pop3->custom : command));
615
616  if(!result)
617    state(conn, POP3_COMMAND);
618
619  return result;
620}
621
622/***********************************************************************
623 *
624 * pop3_perform_quit()
625 *
626 * Performs the quit action prior to sclose() be called.
627 */
628static CURLcode pop3_perform_quit(struct connectdata *conn)
629{
630  CURLcode result = CURLE_OK;
631
632  /* Send the QUIT command */
633  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT");
634
635  if(!result)
636    state(conn, POP3_QUIT);
637
638  return result;
639}
640
641/* For the initial server greeting */
642static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
643                                            int pop3code,
644                                            pop3state instate)
645{
646  CURLcode result = CURLE_OK;
647  struct SessionHandle *data = conn->data;
648  struct pop3_conn *pop3c = &conn->proto.pop3c;
649  const char *line = data->state.buffer;
650  size_t len = strlen(line);
651  size_t i;
652
653  (void)instate; /* no use for this yet */
654
655  if(pop3code != '+') {
656    failf(data, "Got unexpected pop3-server response");
657    result = CURLE_FTP_WEIRD_SERVER_REPLY;
658  }
659  else {
660    /* Does the server support APOP authentication? */
661    if(len >= 4 && line[len - 2] == '>') {
662      /* Look for the APOP timestamp */
663      for(i = 3; i < len - 2; ++i) {
664        if(line[i] == '<') {
665          /* Calculate the length of the timestamp */
666          size_t timestamplen = len - 1 - i;
667          if(!timestamplen)
668            break;
669
670          /* Allocate some memory for the timestamp */
671          pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
672
673          if(!pop3c->apoptimestamp)
674            break;
675
676          /* Copy the timestamp */
677          memcpy(pop3c->apoptimestamp, line + i, timestamplen);
678          pop3c->apoptimestamp[timestamplen] = '\0';
679
680          /* Store the APOP capability */
681          pop3c->authtypes |= POP3_TYPE_APOP;
682          break;
683        }
684      }
685    }
686
687    result = pop3_perform_capa(conn);
688  }
689
690  return result;
691}
692
693/* For CAPA responses */
694static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
695                                     pop3state instate)
696{
697  CURLcode result = CURLE_OK;
698  struct SessionHandle *data = conn->data;
699  struct pop3_conn *pop3c = &conn->proto.pop3c;
700  const char *line = data->state.buffer;
701  size_t len = strlen(line);
702  size_t wordlen;
703
704  (void)instate; /* no use for this yet */
705
706  /* Do we have a untagged response? */
707  if(pop3code == '*') {
708    /* Does the server support the STLS capability? */
709    if(len >= 4 && !memcmp(line, "STLS", 4))
710      pop3c->tls_supported = TRUE;
711
712    /* Does the server support clear text authentication? */
713    else if(len >= 4 && !memcmp(line, "USER", 4))
714      pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
715
716    /* Does the server support SASL based authentication? */
717    else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
718      pop3c->authtypes |= POP3_TYPE_SASL;
719
720      /* Advance past the SASL keyword */
721      line += 5;
722      len -= 5;
723
724      /* Loop through the data line */
725      for(;;) {
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(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
745          pop3c->authmechs |= SASL_MECH_LOGIN;
746        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
747          pop3c->authmechs |= SASL_MECH_PLAIN;
748        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
749          pop3c->authmechs |= SASL_MECH_CRAM_MD5;
750        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
751          pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
752        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
753          pop3c->authmechs |= SASL_MECH_GSSAPI;
754        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
755          pop3c->authmechs |= SASL_MECH_EXTERNAL;
756        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
757          pop3c->authmechs |= SASL_MECH_NTLM;
758        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
759          pop3c->authmechs |= SASL_MECH_XOAUTH2;
760
761        line += wordlen;
762        len -= wordlen;
763      }
764    }
765  }
766  else if(pop3code == '+') {
767    if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
768      /* We don't have a SSL/TLS connection yet, but SSL is requested */
769      if(pop3c->tls_supported)
770        /* Switch to TLS connection now */
771        result = pop3_perform_starttls(conn);
772      else if(data->set.use_ssl == CURLUSESSL_TRY)
773        /* Fallback and carry on with authentication */
774        result = pop3_perform_authentication(conn);
775      else {
776        failf(data, "STLS not supported.");
777        result = CURLE_USE_SSL_FAILED;
778      }
779    }
780    else
781      result = pop3_perform_authentication(conn);
782  }
783  else {
784    /* Clear text is supported when CAPA isn't recognised */
785    pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
786
787    result = pop3_perform_authentication(conn);
788  }
789
790  return result;
791}
792
793/* For STARTTLS responses */
794static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
795                                         int pop3code,
796                                         pop3state instate)
797{
798  CURLcode result = CURLE_OK;
799  struct SessionHandle *data = conn->data;
800
801  (void)instate; /* no use for this yet */
802
803  if(pop3code != '+') {
804    if(data->set.use_ssl != CURLUSESSL_TRY) {
805      failf(data, "STARTTLS denied. %c", pop3code);
806      result = CURLE_USE_SSL_FAILED;
807    }
808    else
809      result = pop3_perform_authentication(conn);
810  }
811  else
812    result = pop3_perform_upgrade_tls(conn);
813
814  return result;
815}
816
817/* For AUTH PLAIN (without initial response) responses */
818static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
819                                           int pop3code,
820                                           pop3state instate)
821{
822  CURLcode result = CURLE_OK;
823  struct SessionHandle *data = conn->data;
824  size_t len = 0;
825  char *plainauth = NULL;
826
827  (void)instate; /* no use for this yet */
828
829  if(pop3code != '+') {
830    failf(data, "Access denied. %c", pop3code);
831    result = CURLE_LOGIN_DENIED;
832  }
833  else {
834    /* Create the authorisation message */
835    result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
836                                            &plainauth, &len);
837    if(!result && plainauth) {
838      /* Send the message */
839      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
840
841      if(!result)
842        state(conn, POP3_AUTH_FINAL);
843    }
844  }
845
846  Curl_safefree(plainauth);
847
848  return result;
849}
850
851/* For AUTH LOGIN (without initial response) responses */
852static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
853                                           int pop3code,
854                                           pop3state instate)
855{
856  CURLcode result = CURLE_OK;
857  struct SessionHandle *data = conn->data;
858  size_t len = 0;
859  char *authuser = NULL;
860
861  (void)instate; /* no use for this yet */
862
863  if(pop3code != '+') {
864    failf(data, "Access denied: %d", pop3code);
865    result = CURLE_LOGIN_DENIED;
866  }
867  else {
868    /* Create the user message */
869    result = Curl_sasl_create_login_message(data, conn->user,
870                                            &authuser, &len);
871    if(!result && authuser) {
872      /* Send the user */
873      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
874
875      if(!result)
876        state(conn, POP3_AUTH_LOGIN_PASSWD);
877    }
878  }
879
880  Curl_safefree(authuser);
881
882  return result;
883}
884
885/* For AUTH LOGIN user entry responses */
886static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
887                                                    int pop3code,
888                                                    pop3state instate)
889{
890  CURLcode result = CURLE_OK;
891  struct SessionHandle *data = conn->data;
892  size_t len = 0;
893  char *authpasswd = NULL;
894
895  (void)instate; /* no use for this yet */
896
897  if(pop3code != '+') {
898    failf(data, "Access denied: %d", pop3code);
899    result = CURLE_LOGIN_DENIED;
900  }
901  else {
902    /* Create the password message */
903    result = Curl_sasl_create_login_message(data, conn->passwd,
904                                            &authpasswd, &len);
905    if(!result && authpasswd) {
906      /* Send the password */
907      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
908
909      if(!result)
910        state(conn, POP3_AUTH_FINAL);
911    }
912  }
913
914  Curl_safefree(authpasswd);
915
916  return result;
917}
918
919#ifndef CURL_DISABLE_CRYPTO_AUTH
920/* For AUTH CRAM-MD5 responses */
921static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
922                                          int pop3code,
923                                          pop3state instate)
924{
925  CURLcode result = CURLE_OK;
926  struct SessionHandle *data = conn->data;
927  char *chlg = NULL;
928  char *chlg64 = NULL;
929  char *rplyb64 = NULL;
930  size_t len = 0;
931
932  (void)instate; /* no use for this yet */
933
934  if(pop3code != '+') {
935    failf(data, "Access denied: %d", pop3code);
936    return CURLE_LOGIN_DENIED;
937  }
938
939  /* Get the challenge message */
940  pop3_get_message(data->state.buffer, &chlg64);
941
942  /* Decode the challenge message */
943  result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
944  if(result) {
945    /* Send the cancellation */
946    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
947
948    if(!result)
949      state(conn, POP3_AUTH_CANCEL);
950  }
951  else {
952    /* Create the response message */
953    result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
954                                               conn->passwd, &rplyb64, &len);
955    if(!result && rplyb64) {
956      /* Send the response */
957      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
958
959      if(!result)
960        state(conn, POP3_AUTH_FINAL);
961    }
962  }
963
964  Curl_safefree(chlg);
965  Curl_safefree(rplyb64);
966
967  return result;
968}
969
970/* For AUTH DIGEST-MD5 challenge responses */
971static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
972                                            int pop3code,
973                                            pop3state instate)
974{
975  CURLcode result = CURLE_OK;
976  struct SessionHandle *data = conn->data;
977  char *chlg64 = NULL;
978  char *rplyb64 = NULL;
979  size_t len = 0;
980
981  char nonce[64];
982  char realm[128];
983  char algorithm[64];
984
985  (void)instate; /* no use for this yet */
986
987  if(pop3code != '+') {
988    failf(data, "Access denied: %d", pop3code);
989    return CURLE_LOGIN_DENIED;
990  }
991
992  /* Get the challenge message */
993  pop3_get_message(data->state.buffer, &chlg64);
994
995  /* Decode the challange message */
996  result = Curl_sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
997                                               realm, sizeof(realm),
998                                               algorithm, sizeof(algorithm));
999  if(result || strcmp(algorithm, "md5-sess") != 0) {
1000    /* Send the cancellation */
1001    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
1002
1003    if(!result)
1004      state(conn, POP3_AUTH_CANCEL);
1005  }
1006  else {
1007    /* Create the response message */
1008    result = Curl_sasl_create_digest_md5_message(data, nonce, realm,
1009                                                 conn->user, conn->passwd,
1010                                                 "pop", &rplyb64, &len);
1011    if(!result && rplyb64) {
1012      /* Send the response */
1013      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
1014
1015      if(!result)
1016        state(conn, POP3_AUTH_DIGESTMD5_RESP);
1017    }
1018  }
1019
1020  Curl_safefree(rplyb64);
1021
1022  return result;
1023}
1024
1025/* For AUTH DIGEST-MD5 challenge-response responses */
1026static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
1027                                                 int pop3code,
1028                                                 pop3state instate)
1029{
1030  CURLcode result = CURLE_OK;
1031  struct SessionHandle *data = conn->data;
1032
1033  (void)instate; /* no use for this yet */
1034
1035  if(pop3code != '+') {
1036    failf(data, "Authentication failed: %d", pop3code);
1037    result = CURLE_LOGIN_DENIED;
1038  }
1039  else {
1040    /* Send an empty response */
1041    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "");
1042
1043    if(!result)
1044      state(conn, POP3_AUTH_FINAL);
1045  }
1046
1047  return result;
1048}
1049#endif
1050
1051#ifdef USE_NTLM
1052/* For AUTH NTLM (without initial response) responses */
1053static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
1054                                          int pop3code,
1055                                          pop3state instate)
1056{
1057  CURLcode result = CURLE_OK;
1058  struct SessionHandle *data = conn->data;
1059  size_t len = 0;
1060  char *type1msg = NULL;
1061
1062  (void)instate; /* no use for this yet */
1063
1064  if(pop3code != '+') {
1065    failf(data, "Access denied: %d", pop3code);
1066    result = CURLE_LOGIN_DENIED;
1067  }
1068  else {
1069    /* Create the type-1 message */
1070    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1071                                                 &conn->ntlm,
1072                                                 &type1msg, &len);
1073    if(!result && type1msg) {
1074      /* Send the message */
1075      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
1076
1077      if(!result)
1078        state(conn, POP3_AUTH_NTLM_TYPE2MSG);
1079    }
1080  }
1081
1082  Curl_safefree(type1msg);
1083
1084  return result;
1085}
1086
1087/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1088static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1089                                                   int pop3code,
1090                                                   pop3state instate)
1091{
1092  CURLcode result = CURLE_OK;
1093  struct SessionHandle *data = conn->data;
1094  char *type2msg = NULL;
1095  char *type3msg = NULL;
1096  size_t len = 0;
1097
1098  (void)instate; /* no use for this yet */
1099
1100  if(pop3code != '+') {
1101    failf(data, "Access denied: %d", pop3code);
1102    result = CURLE_LOGIN_DENIED;
1103  }
1104  else {
1105    /* Get the type-2 message */
1106    pop3_get_message(data->state.buffer, &type2msg);
1107
1108    /* Decode the type-2 message */
1109    result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1110    if(result) {
1111      /* Send the cancellation */
1112      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
1113
1114      if(!result)
1115        state(conn, POP3_AUTH_CANCEL);
1116    }
1117    else {
1118      /* Create the type-3 message */
1119      result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1120                                                   conn->passwd, &conn->ntlm,
1121                                                   &type3msg, &len);
1122      if(!result && type3msg) {
1123        /* Send the message */
1124        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
1125
1126        if(!result)
1127          state(conn, POP3_AUTH_FINAL);
1128      }
1129    }
1130  }
1131
1132  Curl_safefree(type3msg);
1133
1134  return result;
1135}
1136#endif
1137
1138/* For AUTH XOAUTH2 (without initial response) responses */
1139static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn,
1140                                             int pop3code, pop3state instate)
1141{
1142  CURLcode result = CURLE_OK;
1143  struct SessionHandle *data = conn->data;
1144  size_t len = 0;
1145  char *xoauth = NULL;
1146
1147  (void)instate; /* no use for this yet */
1148
1149  if(pop3code != '+') {
1150    failf(data, "Access denied: %d", pop3code);
1151    result = CURLE_LOGIN_DENIED;
1152  }
1153  else {
1154    /* Create the authorisation message */
1155    result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1156                                              conn->xoauth2_bearer,
1157                                              &xoauth, &len);
1158    if(!result && xoauth) {
1159      /* Send the message */
1160      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth);
1161
1162      if(!result)
1163        state(conn, POP3_AUTH_FINAL);
1164    }
1165  }
1166
1167  Curl_safefree(xoauth);
1168
1169  return result;
1170}
1171
1172/* For AUTH cancellation responses */
1173static CURLcode pop3_state_auth_cancel_resp(struct connectdata *conn,
1174                                            int pop3code,
1175                                            pop3state instate)
1176{
1177  CURLcode result = CURLE_OK;
1178  struct SessionHandle *data = conn->data;
1179  struct pop3_conn *pop3c = &conn->proto.pop3c;
1180  const char *mech = NULL;
1181  char *initresp = NULL;
1182  size_t len = 0;
1183  pop3state state1 = POP3_STOP;
1184  pop3state state2 = POP3_STOP;
1185
1186  (void)pop3code;
1187  (void)instate; /* no use for this yet */
1188
1189  /* Remove the offending mechanism from the supported list */
1190  pop3c->authmechs ^= pop3c->authused;
1191
1192  /* Calculate alternative SASL login details */
1193  result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1194                                  &state2);
1195
1196  if(!result) {
1197    /* Do we have any mechanisms left or can we fallback to another
1198       authentication type? */
1199    if(mech) {
1200      /* Retry SASL based authentication */
1201      result = pop3_perform_auth(conn, mech, initresp, len, state1, state2);
1202
1203      Curl_safefree(initresp);
1204    }
1205#ifndef CURL_DISABLE_CRYPTO_AUTH
1206    else if((pop3c->authtypes & POP3_TYPE_APOP) &&
1207            (pop3c->preftype & POP3_TYPE_APOP))
1208      /* Perform APOP authentication */
1209      result = pop3_perform_apop(conn);
1210#endif
1211    else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
1212            (pop3c->preftype & POP3_TYPE_CLEARTEXT))
1213      /* Perform clear text authentication */
1214      result = pop3_perform_user(conn);
1215    else {
1216      failf(data, "Authentication cancelled");
1217
1218      result = CURLE_LOGIN_DENIED;
1219    }
1220  }
1221
1222  return result;
1223}
1224
1225/* For final responses in the AUTH sequence */
1226static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
1227                                           int pop3code,
1228                                           pop3state instate)
1229{
1230  CURLcode result = CURLE_OK;
1231  struct SessionHandle *data = conn->data;
1232
1233  (void)instate; /* no use for this yet */
1234
1235  if(pop3code != '+') {
1236    failf(data, "Authentication failed: %d", pop3code);
1237    result = CURLE_LOGIN_DENIED;
1238  }
1239  else
1240    /* End of connect phase */
1241    state(conn, POP3_STOP);
1242
1243  return result;
1244}
1245
1246#ifndef CURL_DISABLE_CRYPTO_AUTH
1247/* For APOP responses */
1248static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
1249                                     pop3state instate)
1250{
1251  CURLcode result = CURLE_OK;
1252  struct SessionHandle *data = conn->data;
1253
1254  (void)instate; /* no use for this yet */
1255
1256  if(pop3code != '+') {
1257    failf(data, "Authentication failed: %d", pop3code);
1258    result = CURLE_LOGIN_DENIED;
1259  }
1260  else
1261    /* End of connect phase */
1262    state(conn, POP3_STOP);
1263
1264  return result;
1265}
1266#endif
1267
1268/* For USER responses */
1269static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
1270                                     pop3state instate)
1271{
1272  CURLcode result = CURLE_OK;
1273  struct SessionHandle *data = conn->data;
1274
1275  (void)instate; /* no use for this yet */
1276
1277  if(pop3code != '+') {
1278    failf(data, "Access denied. %c", pop3code);
1279    result = CURLE_LOGIN_DENIED;
1280  }
1281  else
1282    /* Send the PASS command */
1283    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
1284                           conn->passwd ? conn->passwd : "");
1285  if(!result)
1286    state(conn, POP3_PASS);
1287
1288  return result;
1289}
1290
1291/* For PASS responses */
1292static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
1293                                     pop3state instate)
1294{
1295  CURLcode result = CURLE_OK;
1296  struct SessionHandle *data = conn->data;
1297
1298  (void)instate; /* no use for this yet */
1299
1300  if(pop3code != '+') {
1301    failf(data, "Access denied. %c", pop3code);
1302    result = CURLE_LOGIN_DENIED;
1303  }
1304  else
1305    /* End of connect phase */
1306    state(conn, POP3_STOP);
1307
1308  return result;
1309}
1310
1311/* For command responses */
1312static CURLcode pop3_state_command_resp(struct connectdata *conn,
1313                                        int pop3code,
1314                                        pop3state instate)
1315{
1316  CURLcode result = CURLE_OK;
1317  struct SessionHandle *data = conn->data;
1318  struct POP3 *pop3 = data->req.protop;
1319  struct pop3_conn *pop3c = &conn->proto.pop3c;
1320  struct pingpong *pp = &pop3c->pp;
1321
1322  (void)instate; /* no use for this yet */
1323
1324  if(pop3code != '+') {
1325    state(conn, POP3_STOP);
1326    return CURLE_RECV_ERROR;
1327  }
1328
1329  /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1330     EOB string so count this is two matching bytes. This is necessary to make
1331     the code detect the EOB if the only data than comes now is %2e CR LF like
1332     when there is no body to return. */
1333  pop3c->eob = 2;
1334
1335  /* But since this initial CR LF pair is not part of the actual body, we set
1336     the strip counter here so that these bytes won't be delivered. */
1337  pop3c->strip = 2;
1338
1339  if(pop3->transfer == FTPTRANSFER_BODY) {
1340    /* POP3 download */
1341    Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
1342
1343    if(pp->cache) {
1344      /* The header "cache" contains a bunch of data that is actually body
1345         content so send it as such. Note that there may even be additional
1346         "headers" after the body */
1347
1348      if(!data->set.opt_no_body) {
1349        result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
1350        if(result)
1351          return result;
1352      }
1353
1354      /* Free the cache */
1355      Curl_safefree(pp->cache);
1356
1357      /* Reset the cache size */
1358      pp->cache_size = 0;
1359    }
1360  }
1361
1362  /* End of DO phase */
1363  state(conn, POP3_STOP);
1364
1365  return result;
1366}
1367
1368static CURLcode pop3_statemach_act(struct connectdata *conn)
1369{
1370  CURLcode result = CURLE_OK;
1371  curl_socket_t sock = conn->sock[FIRSTSOCKET];
1372  int pop3code;
1373  struct pop3_conn *pop3c = &conn->proto.pop3c;
1374  struct pingpong *pp = &pop3c->pp;
1375  size_t nread = 0;
1376
1377  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1378  if(pop3c->state == POP3_UPGRADETLS)
1379    return pop3_perform_upgrade_tls(conn);
1380
1381  /* Flush any data that needs to be sent */
1382  if(pp->sendleft)
1383    return Curl_pp_flushsend(pp);
1384
1385 do {
1386    /* Read the response from the server */
1387    result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1388    if(result)
1389      return result;
1390
1391    if(!pop3code)
1392      break;
1393
1394    /* We have now received a full POP3 server response */
1395    switch(pop3c->state) {
1396    case POP3_SERVERGREET:
1397      result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1398      break;
1399
1400    case POP3_CAPA:
1401      result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1402      break;
1403
1404    case POP3_STARTTLS:
1405      result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1406      break;
1407
1408    case POP3_AUTH_PLAIN:
1409      result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
1410      break;
1411
1412    case POP3_AUTH_LOGIN:
1413      result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
1414      break;
1415
1416    case POP3_AUTH_LOGIN_PASSWD:
1417      result = pop3_state_auth_login_password_resp(conn, pop3code,
1418                                                   pop3c->state);
1419      break;
1420
1421#ifndef CURL_DISABLE_CRYPTO_AUTH
1422    case POP3_AUTH_CRAMMD5:
1423      result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
1424      break;
1425
1426    case POP3_AUTH_DIGESTMD5:
1427      result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
1428      break;
1429
1430    case POP3_AUTH_DIGESTMD5_RESP:
1431      result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
1432      break;
1433#endif
1434
1435#ifdef USE_NTLM
1436    case POP3_AUTH_NTLM:
1437      result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
1438      break;
1439
1440    case POP3_AUTH_NTLM_TYPE2MSG:
1441      result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
1442                                                  pop3c->state);
1443      break;
1444#endif
1445
1446    case POP3_AUTH_XOAUTH2:
1447      result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state);
1448      break;
1449
1450    case POP3_AUTH_CANCEL:
1451      result = pop3_state_auth_cancel_resp(conn, pop3code, pop3c->state);
1452      break;
1453
1454    case POP3_AUTH_FINAL:
1455      result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
1456      break;
1457
1458#ifndef CURL_DISABLE_CRYPTO_AUTH
1459    case POP3_APOP:
1460      result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1461      break;
1462#endif
1463
1464    case POP3_USER:
1465      result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1466      break;
1467
1468    case POP3_PASS:
1469      result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1470      break;
1471
1472    case POP3_COMMAND:
1473      result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1474      break;
1475
1476    case POP3_QUIT:
1477      /* fallthrough, just stop! */
1478    default:
1479      /* internal error */
1480      state(conn, POP3_STOP);
1481      break;
1482    }
1483  } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1484
1485  return result;
1486}
1487
1488/* Called repeatedly until done from multi.c */
1489static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1490{
1491  CURLcode result = CURLE_OK;
1492  struct pop3_conn *pop3c = &conn->proto.pop3c;
1493
1494  if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1495    result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1496    if(result || !pop3c->ssldone)
1497      return result;
1498  }
1499
1500  result = Curl_pp_statemach(&pop3c->pp, FALSE);
1501  *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1502
1503  return result;
1504}
1505
1506static CURLcode pop3_block_statemach(struct connectdata *conn)
1507{
1508  CURLcode result = CURLE_OK;
1509  struct pop3_conn *pop3c = &conn->proto.pop3c;
1510
1511  while(pop3c->state != POP3_STOP && !result)
1512    result = Curl_pp_statemach(&pop3c->pp, TRUE);
1513
1514  return result;
1515}
1516
1517/* Allocate and initialize the POP3 struct for the current SessionHandle if
1518   required */
1519static CURLcode pop3_init(struct connectdata *conn)
1520{
1521  CURLcode result = CURLE_OK;
1522  struct SessionHandle *data = conn->data;
1523  struct POP3 *pop3;
1524
1525  pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
1526  if(!pop3)
1527    result = CURLE_OUT_OF_MEMORY;
1528
1529  return result;
1530}
1531
1532/* For the POP3 "protocol connect" and "doing" phases only */
1533static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1534                        int numsocks)
1535{
1536  return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1537}
1538
1539/***********************************************************************
1540 *
1541 * pop3_connect()
1542 *
1543 * This function should do everything that is to be considered a part of the
1544 * connection phase.
1545 *
1546 * The variable 'done' points to will be TRUE if the protocol-layer connect
1547 * phase is done when this function returns, or FALSE if not.
1548 */
1549static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1550{
1551  CURLcode result = CURLE_OK;
1552  struct pop3_conn *pop3c = &conn->proto.pop3c;
1553  struct pingpong *pp = &pop3c->pp;
1554
1555  *done = FALSE; /* default to not done yet */
1556
1557  /* We always support persistent connections in POP3 */
1558  conn->bits.close = FALSE;
1559
1560  /* Set the default response time-out */
1561  pp->response_time = RESP_TIMEOUT;
1562  pp->statemach_act = pop3_statemach_act;
1563  pp->endofresp = pop3_endofresp;
1564  pp->conn = conn;
1565
1566  /* Set the default preferred authentication type and mechanism */
1567  pop3c->preftype = POP3_TYPE_ANY;
1568  pop3c->prefmech = SASL_AUTH_ANY;
1569
1570  /* Initialise the pingpong layer */
1571  Curl_pp_init(pp);
1572
1573  /* Parse the URL options */
1574  result = pop3_parse_url_options(conn);
1575  if(result)
1576    return result;
1577
1578  /* Start off waiting for the server greeting response */
1579  state(conn, POP3_SERVERGREET);
1580
1581  result = pop3_multi_statemach(conn, done);
1582
1583  return result;
1584}
1585
1586/***********************************************************************
1587 *
1588 * pop3_done()
1589 *
1590 * The DONE function. This does what needs to be done after a single DO has
1591 * performed.
1592 *
1593 * Input argument is already checked for validity.
1594 */
1595static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1596                          bool premature)
1597{
1598  CURLcode result = CURLE_OK;
1599  struct SessionHandle *data = conn->data;
1600  struct POP3 *pop3 = data->req.protop;
1601
1602  (void)premature;
1603
1604  if(!pop3)
1605    /* When the easy handle is removed from the multi interface while libcurl
1606       is still trying to resolve the host name, the POP3 struct is not yet
1607       initialized. However, the removal action calls Curl_done() which in
1608       turn calls this function, so we simply return success. */
1609    return CURLE_OK;
1610
1611  if(status) {
1612    conn->bits.close = TRUE; /* marked for closure */
1613    result = status;         /* use the already set error code */
1614  }
1615
1616  /* Cleanup our per-request based variables */
1617  Curl_safefree(pop3->id);
1618  Curl_safefree(pop3->custom);
1619
1620  /* Clear the transfer mode for the next request */
1621  pop3->transfer = FTPTRANSFER_BODY;
1622
1623  return result;
1624}
1625
1626/***********************************************************************
1627 *
1628 * pop3_perform()
1629 *
1630 * This is the actual DO function for POP3. Get a message/listing according to
1631 * the options previously setup.
1632 */
1633static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1634                             bool *dophase_done)
1635{
1636  /* This is POP3 and no proxy */
1637  CURLcode result = CURLE_OK;
1638  struct POP3 *pop3 = conn->data->req.protop;
1639
1640  DEBUGF(infof(conn->data, "DO phase starts\n"));
1641
1642  if(conn->data->set.opt_no_body) {
1643    /* Requested no body means no transfer */
1644    pop3->transfer = FTPTRANSFER_INFO;
1645  }
1646
1647  *dophase_done = FALSE; /* not done yet */
1648
1649  /* Start the first command in the DO phase */
1650  result = pop3_perform_command(conn);
1651  if(result)
1652    return result;
1653
1654  /* Run the state-machine */
1655  result = pop3_multi_statemach(conn, dophase_done);
1656
1657  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1658
1659  if(*dophase_done)
1660    DEBUGF(infof(conn->data, "DO phase is complete\n"));
1661
1662  return result;
1663}
1664
1665/***********************************************************************
1666 *
1667 * pop3_do()
1668 *
1669 * This function is registered as 'curl_do' function. It decodes the path
1670 * parts etc as a wrapper to the actual DO function (pop3_perform).
1671 *
1672 * The input argument is already checked for validity.
1673 */
1674static CURLcode pop3_do(struct connectdata *conn, bool *done)
1675{
1676  CURLcode result = CURLE_OK;
1677
1678  *done = FALSE; /* default to false */
1679
1680  /* Parse the URL path */
1681  result = pop3_parse_url_path(conn);
1682  if(result)
1683    return result;
1684
1685  /* Parse the custom request */
1686  result = pop3_parse_custom_request(conn);
1687  if(result)
1688    return result;
1689
1690  result = pop3_regular_transfer(conn, done);
1691
1692  return result;
1693}
1694
1695/***********************************************************************
1696 *
1697 * pop3_disconnect()
1698 *
1699 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1700 * resources. BLOCKING.
1701 */
1702static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
1703{
1704  struct pop3_conn *pop3c = &conn->proto.pop3c;
1705
1706  /* We cannot send quit unconditionally. If this connection is stale or
1707     bad in any way, sending quit and waiting around here will make the
1708     disconnect wait in vain and cause more problems than we need to. */
1709
1710  /* The POP3 session may or may not have been allocated/setup at this
1711     point! */
1712  if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
1713    if(!pop3_perform_quit(conn))
1714      (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1715
1716  /* Disconnect from the server */
1717  Curl_pp_disconnect(&pop3c->pp);
1718
1719  /* Cleanup the SASL module */
1720  Curl_sasl_cleanup(conn, pop3c->authused);
1721
1722  /* Cleanup our connection based variables */
1723  Curl_safefree(pop3c->apoptimestamp);
1724
1725  return CURLE_OK;
1726}
1727
1728/* Call this when the DO phase has completed */
1729static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1730{
1731  (void)conn;
1732  (void)connected;
1733
1734  return CURLE_OK;
1735}
1736
1737/* Called from multi.c while DOing */
1738static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1739{
1740  CURLcode result = pop3_multi_statemach(conn, dophase_done);
1741
1742  if(result)
1743    DEBUGF(infof(conn->data, "DO phase failed\n"));
1744  else if(*dophase_done) {
1745    result = pop3_dophase_done(conn, FALSE /* not connected */);
1746
1747    DEBUGF(infof(conn->data, "DO phase is complete\n"));
1748  }
1749
1750  return result;
1751}
1752
1753/***********************************************************************
1754 *
1755 * pop3_regular_transfer()
1756 *
1757 * The input argument is already checked for validity.
1758 *
1759 * Performs all commands done before a regular transfer between a local and a
1760 * remote host.
1761 */
1762static CURLcode pop3_regular_transfer(struct connectdata *conn,
1763                                      bool *dophase_done)
1764{
1765  CURLcode result = CURLE_OK;
1766  bool connected = FALSE;
1767  struct SessionHandle *data = conn->data;
1768
1769  /* Make sure size is unknown at this point */
1770  data->req.size = -1;
1771
1772  /* Set the progress data */
1773  Curl_pgrsSetUploadCounter(data, 0);
1774  Curl_pgrsSetDownloadCounter(data, 0);
1775  Curl_pgrsSetUploadSize(data, 0);
1776  Curl_pgrsSetDownloadSize(data, 0);
1777
1778  /* Carry out the perform */
1779  result = pop3_perform(conn, &connected, dophase_done);
1780
1781  /* Perform post DO phase operations if necessary */
1782  if(!result && *dophase_done)
1783    result = pop3_dophase_done(conn, connected);
1784
1785  return result;
1786}
1787
1788static CURLcode pop3_setup_connection(struct connectdata *conn)
1789{
1790  struct SessionHandle *data = conn->data;
1791
1792  /* Initialise the POP3 layer */
1793  CURLcode result = pop3_init(conn);
1794  if(result)
1795    return result;
1796
1797  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1798    /* Unless we have asked to tunnel POP3 operations through the proxy, we
1799       switch and use HTTP operations only */
1800#ifndef CURL_DISABLE_HTTP
1801    if(conn->handler == &Curl_handler_pop3)
1802      conn->handler = &Curl_handler_pop3_proxy;
1803    else {
1804#ifdef USE_SSL
1805      conn->handler = &Curl_handler_pop3s_proxy;
1806#else
1807      failf(data, "POP3S not supported!");
1808      return CURLE_UNSUPPORTED_PROTOCOL;
1809#endif
1810    }
1811
1812    /* set it up as an HTTP connection instead */
1813    return conn->handler->setup_connection(conn);
1814#else
1815    failf(data, "POP3 over http proxy requires HTTP support built-in!");
1816    return CURLE_UNSUPPORTED_PROTOCOL;
1817#endif
1818  }
1819
1820  data->state.path++;   /* don't include the initial slash */
1821
1822  return CURLE_OK;
1823}
1824
1825/***********************************************************************
1826 *
1827 * pop3_parse_url_options()
1828 *
1829 * Parse the URL login options.
1830 */
1831static CURLcode pop3_parse_url_options(struct connectdata *conn)
1832{
1833  CURLcode result = CURLE_OK;
1834  struct pop3_conn *pop3c = &conn->proto.pop3c;
1835  const char *options = conn->options;
1836  const char *ptr = options;
1837  bool reset = TRUE;
1838
1839  while(ptr && *ptr) {
1840    const char *key = ptr;
1841
1842    while(*ptr && *ptr != '=')
1843        ptr++;
1844
1845    if(strnequal(key, "AUTH", 4)) {
1846      size_t len = 0;
1847      const char *value = ++ptr;
1848
1849      if(reset) {
1850        reset = FALSE;
1851        pop3c->preftype = POP3_TYPE_NONE;
1852        pop3c->prefmech = SASL_AUTH_NONE;
1853      }
1854
1855      while(*ptr && *ptr != ';') {
1856        ptr++;
1857        len++;
1858      }
1859
1860      if(strnequal(value, "*", len)) {
1861        pop3c->preftype = POP3_TYPE_ANY;
1862        pop3c->prefmech = SASL_AUTH_ANY;
1863      }
1864      else if(strnequal(value, "+APOP", len)) {
1865        pop3c->preftype = POP3_TYPE_APOP;
1866        pop3c->prefmech = SASL_AUTH_NONE;
1867      }
1868      else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
1869        pop3c->preftype = POP3_TYPE_SASL;
1870        pop3c->prefmech |= SASL_MECH_LOGIN;
1871      }
1872      else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
1873        pop3c->preftype = POP3_TYPE_SASL;
1874        pop3c->prefmech |= SASL_MECH_PLAIN;
1875      }
1876      else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
1877        pop3c->preftype = POP3_TYPE_SASL;
1878        pop3c->prefmech |= SASL_MECH_CRAM_MD5;
1879      }
1880      else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
1881        pop3c->preftype = POP3_TYPE_SASL;
1882        pop3c->prefmech |= SASL_MECH_DIGEST_MD5;
1883      }
1884      else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
1885        pop3c->preftype = POP3_TYPE_SASL;
1886        pop3c->prefmech |= SASL_MECH_GSSAPI;
1887      }
1888      else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
1889        pop3c->preftype = POP3_TYPE_SASL;
1890        pop3c->prefmech |= SASL_MECH_NTLM;
1891      }
1892      else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
1893        pop3c->preftype = POP3_TYPE_SASL;
1894        pop3c->prefmech |= SASL_MECH_XOAUTH2;
1895      }
1896
1897      if(*ptr == ';')
1898        ptr++;
1899    }
1900    else
1901      result = CURLE_URL_MALFORMAT;
1902  }
1903
1904  return result;
1905}
1906
1907/***********************************************************************
1908 *
1909 * pop3_parse_url_path()
1910 *
1911 * Parse the URL path into separate path components.
1912 */
1913static CURLcode pop3_parse_url_path(struct connectdata *conn)
1914{
1915  /* The POP3 struct is already initialised in pop3_connect() */
1916  struct SessionHandle *data = conn->data;
1917  struct POP3 *pop3 = data->req.protop;
1918  const char *path = data->state.path;
1919
1920  /* URL decode the path for the message ID */
1921  return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1922}
1923
1924/***********************************************************************
1925 *
1926 * pop3_parse_custom_request()
1927 *
1928 * Parse the custom request.
1929 */
1930static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1931{
1932  CURLcode result = CURLE_OK;
1933  struct SessionHandle *data = conn->data;
1934  struct POP3 *pop3 = data->req.protop;
1935  const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1936
1937  /* URL decode the custom request */
1938  if(custom)
1939    result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1940
1941  return result;
1942}
1943
1944/***********************************************************************
1945 *
1946 * pop3_calc_sasl_details()
1947 *
1948 * Calculate the required login details for SASL authentication.
1949 */
1950static CURLcode pop3_calc_sasl_details(struct connectdata *conn,
1951                                       const char **mech,
1952                                       char **initresp, size_t *len,
1953                                       pop3state *state1, pop3state *state2)
1954{
1955  CURLcode result = CURLE_OK;
1956  struct SessionHandle *data = conn->data;
1957  struct pop3_conn *pop3c = &conn->proto.pop3c;
1958
1959  /* Calculate the supported authentication mechanism, by decreasing order of
1960     security, as well as the initial response where appropriate */
1961#ifndef CURL_DISABLE_CRYPTO_AUTH
1962  if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) &&
1963      (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) {
1964    *mech = SASL_MECH_STRING_DIGEST_MD5;
1965    *state1 = POP3_AUTH_DIGESTMD5;
1966    pop3c->authused = SASL_MECH_DIGEST_MD5;
1967  }
1968  else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) &&
1969          (pop3c->prefmech & SASL_MECH_CRAM_MD5)) {
1970    *mech = SASL_MECH_STRING_CRAM_MD5;
1971    *state1 = POP3_AUTH_CRAMMD5;
1972    pop3c->authused = SASL_MECH_CRAM_MD5;
1973  }
1974  else
1975#endif
1976#ifdef USE_NTLM
1977  if((pop3c->authmechs & SASL_MECH_NTLM) &&
1978      (pop3c->prefmech & SASL_MECH_NTLM)) {
1979    *mech = SASL_MECH_STRING_NTLM;
1980    *state1 = POP3_AUTH_NTLM;
1981    *state2 = POP3_AUTH_NTLM_TYPE2MSG;
1982    pop3c->authused = SASL_MECH_NTLM;
1983
1984    if(data->set.sasl_ir)
1985      result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1986                                                    &conn->ntlm,
1987                                                    initresp, len);
1988  }
1989  else
1990#endif
1991  if(((pop3c->authmechs & SASL_MECH_XOAUTH2) &&
1992      (pop3c->prefmech & SASL_MECH_XOAUTH2) &&
1993      (pop3c->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
1994    *mech = SASL_MECH_STRING_XOAUTH2;
1995    *state1 = POP3_AUTH_XOAUTH2;
1996    *state2 = POP3_AUTH_FINAL;
1997    pop3c->authused = SASL_MECH_XOAUTH2;
1998
1999    if(data->set.sasl_ir)
2000      result = Curl_sasl_create_xoauth2_message(data, conn->user,
2001                                                conn->xoauth2_bearer,
2002                                                initresp, len);
2003  }
2004  else if((pop3c->authmechs & SASL_MECH_LOGIN) &&
2005          (pop3c->prefmech & SASL_MECH_LOGIN)) {
2006    *mech = SASL_MECH_STRING_LOGIN;
2007    *state1 = POP3_AUTH_LOGIN;
2008    *state2 = POP3_AUTH_LOGIN_PASSWD;
2009    pop3c->authused = SASL_MECH_LOGIN;
2010
2011    if(data->set.sasl_ir)
2012      result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2013  }
2014  else if((pop3c->authmechs & SASL_MECH_PLAIN) &&
2015          (pop3c->prefmech & SASL_MECH_PLAIN)) {
2016    *mech = SASL_MECH_STRING_PLAIN;
2017    *state1 = POP3_AUTH_PLAIN;
2018    *state2 = POP3_AUTH_FINAL;
2019    pop3c->authused = SASL_MECH_PLAIN;
2020
2021    if(data->set.sasl_ir)
2022      result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2023                                              initresp, len);
2024  }
2025
2026  return result;
2027}
2028
2029/***********************************************************************
2030 *
2031 * Curl_pop3_write()
2032 *
2033 * This function scans the body after the end-of-body and writes everything
2034 * until the end is found.
2035 */
2036CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
2037{
2038  /* This code could be made into a special function in the handler struct */
2039  CURLcode result = CURLE_OK;
2040  struct SessionHandle *data = conn->data;
2041  struct SingleRequest *k = &data->req;
2042
2043  struct pop3_conn *pop3c = &conn->proto.pop3c;
2044  bool strip_dot = FALSE;
2045  size_t last = 0;
2046  size_t i;
2047
2048  /* Search through the buffer looking for the end-of-body marker which is
2049     5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
2050     the eob so the server will have prefixed it with an extra dot which we
2051     need to strip out. Additionally the marker could of course be spread out
2052     over 5 different data chunks. */
2053  for(i = 0; i < nread; i++) {
2054    size_t prev = pop3c->eob;
2055
2056    switch(str[i]) {
2057    case 0x0d:
2058      if(pop3c->eob == 0) {
2059        pop3c->eob++;
2060
2061        if(i) {
2062          /* Write out the body part that didn't match */
2063          result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
2064                                     i - last);
2065
2066          if(result)
2067            return result;
2068
2069          last = i;
2070        }
2071      }
2072      else if(pop3c->eob == 3)
2073        pop3c->eob++;
2074      else
2075        /* If the character match wasn't at position 0 or 3 then restart the
2076           pattern matching */
2077        pop3c->eob = 1;
2078      break;
2079
2080    case 0x0a:
2081      if(pop3c->eob == 1 || pop3c->eob == 4)
2082        pop3c->eob++;
2083      else
2084        /* If the character match wasn't at position 1 or 4 then start the
2085           search again */
2086        pop3c->eob = 0;
2087      break;
2088
2089    case 0x2e:
2090      if(pop3c->eob == 2)
2091        pop3c->eob++;
2092      else if(pop3c->eob == 3) {
2093        /* We have an extra dot after the CRLF which we need to strip off */
2094        strip_dot = TRUE;
2095        pop3c->eob = 0;
2096      }
2097      else
2098        /* If the character match wasn't at position 2 then start the search
2099           again */
2100        pop3c->eob = 0;
2101      break;
2102
2103    default:
2104      pop3c->eob = 0;
2105      break;
2106    }
2107
2108    /* Did we have a partial match which has subsequently failed? */
2109    if(prev && prev >= pop3c->eob) {
2110      /* Strip can only be non-zero for the very first mismatch after CRLF
2111         and then both prev and strip are equal and nothing will be output
2112         below */
2113      while(prev && pop3c->strip) {
2114        prev--;
2115        pop3c->strip--;
2116      }
2117
2118      if(prev) {
2119        /* If the partial match was the CRLF and dot then only write the CRLF
2120           as the server would have inserted the dot */
2121        result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
2122                                   strip_dot ? prev - 1 : prev);
2123
2124        if(result)
2125          return result;
2126
2127        last = i;
2128        strip_dot = FALSE;
2129      }
2130    }
2131  }
2132
2133  if(pop3c->eob == POP3_EOB_LEN) {
2134    /* We have a full match so the transfer is done, however we must transfer
2135    the CRLF at the start of the EOB as this is considered to be part of the
2136    message as per RFC-1939, sect. 3 */
2137    result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
2138
2139    k->keepon &= ~KEEP_RECV;
2140    pop3c->eob = 0;
2141
2142    return result;
2143  }
2144
2145  if(pop3c->eob)
2146    /* While EOB is matching nothing should be output */
2147    return CURLE_OK;
2148
2149  if(nread - last) {
2150    result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
2151                               nread - last);
2152  }
2153
2154  return result;
2155}
2156
2157#endif /* CURL_DISABLE_POP3 */
2158