1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2014, 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_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  (void)instate; /* no use for this yet */
982
983  if(pop3code != '+') {
984    failf(data, "Access denied: %d", pop3code);
985    return CURLE_LOGIN_DENIED;
986  }
987
988  /* Get the challenge message */
989  pop3_get_message(data->state.buffer, &chlg64);
990
991  /* Create the response message */
992  result = Curl_sasl_create_digest_md5_message(data, chlg64,
993                                               conn->user, conn->passwd,
994                                               "pop", &rplyb64, &len);
995  if(result) {
996    if(result == CURLE_BAD_CONTENT_ENCODING) {
997      /* Send the cancellation */
998      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
999
1000      if(!result)
1001        state(conn, POP3_AUTH_CANCEL);
1002    }
1003  }
1004  else {
1005    /* Send the response */
1006    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
1007
1008    if(!result)
1009      state(conn, POP3_AUTH_DIGESTMD5_RESP);
1010  }
1011
1012  Curl_safefree(rplyb64);
1013
1014  return result;
1015}
1016
1017/* For AUTH DIGEST-MD5 challenge-response responses */
1018static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
1019                                                 int pop3code,
1020                                                 pop3state instate)
1021{
1022  CURLcode result = CURLE_OK;
1023  struct SessionHandle *data = conn->data;
1024
1025  (void)instate; /* no use for this yet */
1026
1027  if(pop3code != '+') {
1028    failf(data, "Authentication failed: %d", pop3code);
1029    result = CURLE_LOGIN_DENIED;
1030  }
1031  else {
1032    /* Send an empty response */
1033    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "");
1034
1035    if(!result)
1036      state(conn, POP3_AUTH_FINAL);
1037  }
1038
1039  return result;
1040}
1041#endif
1042
1043#ifdef USE_NTLM
1044/* For AUTH NTLM (without initial response) responses */
1045static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
1046                                          int pop3code,
1047                                          pop3state instate)
1048{
1049  CURLcode result = CURLE_OK;
1050  struct SessionHandle *data = conn->data;
1051  size_t len = 0;
1052  char *type1msg = NULL;
1053
1054  (void)instate; /* no use for this yet */
1055
1056  if(pop3code != '+') {
1057    failf(data, "Access denied: %d", pop3code);
1058    result = CURLE_LOGIN_DENIED;
1059  }
1060  else {
1061    /* Create the type-1 message */
1062    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1063                                                 &conn->ntlm,
1064                                                 &type1msg, &len);
1065    if(!result && type1msg) {
1066      /* Send the message */
1067      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
1068
1069      if(!result)
1070        state(conn, POP3_AUTH_NTLM_TYPE2MSG);
1071    }
1072  }
1073
1074  Curl_safefree(type1msg);
1075
1076  return result;
1077}
1078
1079/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1080static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1081                                                   int pop3code,
1082                                                   pop3state instate)
1083{
1084  CURLcode result = CURLE_OK;
1085  struct SessionHandle *data = conn->data;
1086  char *type2msg = NULL;
1087  char *type3msg = NULL;
1088  size_t len = 0;
1089
1090  (void)instate; /* no use for this yet */
1091
1092  if(pop3code != '+') {
1093    failf(data, "Access denied: %d", pop3code);
1094    result = CURLE_LOGIN_DENIED;
1095  }
1096  else {
1097    /* Get the type-2 message */
1098    pop3_get_message(data->state.buffer, &type2msg);
1099
1100    /* Decode the type-2 message */
1101    result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1102    if(result) {
1103      /* Send the cancellation */
1104      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "*");
1105
1106      if(!result)
1107        state(conn, POP3_AUTH_CANCEL);
1108    }
1109    else {
1110      /* Create the type-3 message */
1111      result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1112                                                   conn->passwd, &conn->ntlm,
1113                                                   &type3msg, &len);
1114      if(!result && type3msg) {
1115        /* Send the message */
1116        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
1117
1118        if(!result)
1119          state(conn, POP3_AUTH_FINAL);
1120      }
1121    }
1122  }
1123
1124  Curl_safefree(type3msg);
1125
1126  return result;
1127}
1128#endif
1129
1130/* For AUTH XOAUTH2 (without initial response) responses */
1131static CURLcode pop3_state_auth_xoauth2_resp(struct connectdata *conn,
1132                                             int pop3code, pop3state instate)
1133{
1134  CURLcode result = CURLE_OK;
1135  struct SessionHandle *data = conn->data;
1136  size_t len = 0;
1137  char *xoauth = NULL;
1138
1139  (void)instate; /* no use for this yet */
1140
1141  if(pop3code != '+') {
1142    failf(data, "Access denied: %d", pop3code);
1143    result = CURLE_LOGIN_DENIED;
1144  }
1145  else {
1146    /* Create the authorisation message */
1147    result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1148                                              conn->xoauth2_bearer,
1149                                              &xoauth, &len);
1150    if(!result && xoauth) {
1151      /* Send the message */
1152      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", xoauth);
1153
1154      if(!result)
1155        state(conn, POP3_AUTH_FINAL);
1156    }
1157  }
1158
1159  Curl_safefree(xoauth);
1160
1161  return result;
1162}
1163
1164/* For AUTH cancellation responses */
1165static CURLcode pop3_state_auth_cancel_resp(struct connectdata *conn,
1166                                            int pop3code,
1167                                            pop3state instate)
1168{
1169  CURLcode result = CURLE_OK;
1170  struct SessionHandle *data = conn->data;
1171  struct pop3_conn *pop3c = &conn->proto.pop3c;
1172  const char *mech = NULL;
1173  char *initresp = NULL;
1174  size_t len = 0;
1175  pop3state state1 = POP3_STOP;
1176  pop3state state2 = POP3_STOP;
1177
1178  (void)pop3code;
1179  (void)instate; /* no use for this yet */
1180
1181  /* Remove the offending mechanism from the supported list */
1182  pop3c->authmechs ^= pop3c->authused;
1183
1184  /* Calculate alternative SASL login details */
1185  result = pop3_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1186                                  &state2);
1187
1188  if(!result) {
1189    /* Do we have any mechanisms left or can we fallback to another
1190       authentication type? */
1191    if(mech) {
1192      /* Retry SASL based authentication */
1193      result = pop3_perform_auth(conn, mech, initresp, len, state1, state2);
1194
1195      Curl_safefree(initresp);
1196    }
1197#ifndef CURL_DISABLE_CRYPTO_AUTH
1198    else if((pop3c->authtypes & POP3_TYPE_APOP) &&
1199            (pop3c->preftype & POP3_TYPE_APOP))
1200      /* Perform APOP authentication */
1201      result = pop3_perform_apop(conn);
1202#endif
1203    else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
1204            (pop3c->preftype & POP3_TYPE_CLEARTEXT))
1205      /* Perform clear text authentication */
1206      result = pop3_perform_user(conn);
1207    else {
1208      failf(data, "Authentication cancelled");
1209
1210      result = CURLE_LOGIN_DENIED;
1211    }
1212  }
1213
1214  return result;
1215}
1216
1217/* For final responses in the AUTH sequence */
1218static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
1219                                           int pop3code,
1220                                           pop3state instate)
1221{
1222  CURLcode result = CURLE_OK;
1223  struct SessionHandle *data = conn->data;
1224
1225  (void)instate; /* no use for this yet */
1226
1227  if(pop3code != '+') {
1228    failf(data, "Authentication failed: %d", pop3code);
1229    result = CURLE_LOGIN_DENIED;
1230  }
1231  else
1232    /* End of connect phase */
1233    state(conn, POP3_STOP);
1234
1235  return result;
1236}
1237
1238#ifndef CURL_DISABLE_CRYPTO_AUTH
1239/* For APOP responses */
1240static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
1241                                     pop3state instate)
1242{
1243  CURLcode result = CURLE_OK;
1244  struct SessionHandle *data = conn->data;
1245
1246  (void)instate; /* no use for this yet */
1247
1248  if(pop3code != '+') {
1249    failf(data, "Authentication failed: %d", pop3code);
1250    result = CURLE_LOGIN_DENIED;
1251  }
1252  else
1253    /* End of connect phase */
1254    state(conn, POP3_STOP);
1255
1256  return result;
1257}
1258#endif
1259
1260/* For USER responses */
1261static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
1262                                     pop3state instate)
1263{
1264  CURLcode result = CURLE_OK;
1265  struct SessionHandle *data = conn->data;
1266
1267  (void)instate; /* no use for this yet */
1268
1269  if(pop3code != '+') {
1270    failf(data, "Access denied. %c", pop3code);
1271    result = CURLE_LOGIN_DENIED;
1272  }
1273  else
1274    /* Send the PASS command */
1275    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
1276                           conn->passwd ? conn->passwd : "");
1277  if(!result)
1278    state(conn, POP3_PASS);
1279
1280  return result;
1281}
1282
1283/* For PASS responses */
1284static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
1285                                     pop3state instate)
1286{
1287  CURLcode result = CURLE_OK;
1288  struct SessionHandle *data = conn->data;
1289
1290  (void)instate; /* no use for this yet */
1291
1292  if(pop3code != '+') {
1293    failf(data, "Access denied. %c", pop3code);
1294    result = CURLE_LOGIN_DENIED;
1295  }
1296  else
1297    /* End of connect phase */
1298    state(conn, POP3_STOP);
1299
1300  return result;
1301}
1302
1303/* For command responses */
1304static CURLcode pop3_state_command_resp(struct connectdata *conn,
1305                                        int pop3code,
1306                                        pop3state instate)
1307{
1308  CURLcode result = CURLE_OK;
1309  struct SessionHandle *data = conn->data;
1310  struct POP3 *pop3 = data->req.protop;
1311  struct pop3_conn *pop3c = &conn->proto.pop3c;
1312  struct pingpong *pp = &pop3c->pp;
1313
1314  (void)instate; /* no use for this yet */
1315
1316  if(pop3code != '+') {
1317    state(conn, POP3_STOP);
1318    return CURLE_RECV_ERROR;
1319  }
1320
1321  /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1322     EOB string so count this is two matching bytes. This is necessary to make
1323     the code detect the EOB if the only data than comes now is %2e CR LF like
1324     when there is no body to return. */
1325  pop3c->eob = 2;
1326
1327  /* But since this initial CR LF pair is not part of the actual body, we set
1328     the strip counter here so that these bytes won't be delivered. */
1329  pop3c->strip = 2;
1330
1331  if(pop3->transfer == FTPTRANSFER_BODY) {
1332    /* POP3 download */
1333    Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
1334
1335    if(pp->cache) {
1336      /* The header "cache" contains a bunch of data that is actually body
1337         content so send it as such. Note that there may even be additional
1338         "headers" after the body */
1339
1340      if(!data->set.opt_no_body) {
1341        result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
1342        if(result)
1343          return result;
1344      }
1345
1346      /* Free the cache */
1347      Curl_safefree(pp->cache);
1348
1349      /* Reset the cache size */
1350      pp->cache_size = 0;
1351    }
1352  }
1353
1354  /* End of DO phase */
1355  state(conn, POP3_STOP);
1356
1357  return result;
1358}
1359
1360static CURLcode pop3_statemach_act(struct connectdata *conn)
1361{
1362  CURLcode result = CURLE_OK;
1363  curl_socket_t sock = conn->sock[FIRSTSOCKET];
1364  int pop3code;
1365  struct pop3_conn *pop3c = &conn->proto.pop3c;
1366  struct pingpong *pp = &pop3c->pp;
1367  size_t nread = 0;
1368
1369  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1370  if(pop3c->state == POP3_UPGRADETLS)
1371    return pop3_perform_upgrade_tls(conn);
1372
1373  /* Flush any data that needs to be sent */
1374  if(pp->sendleft)
1375    return Curl_pp_flushsend(pp);
1376
1377 do {
1378    /* Read the response from the server */
1379    result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1380    if(result)
1381      return result;
1382
1383    if(!pop3code)
1384      break;
1385
1386    /* We have now received a full POP3 server response */
1387    switch(pop3c->state) {
1388    case POP3_SERVERGREET:
1389      result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1390      break;
1391
1392    case POP3_CAPA:
1393      result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1394      break;
1395
1396    case POP3_STARTTLS:
1397      result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1398      break;
1399
1400    case POP3_AUTH_PLAIN:
1401      result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
1402      break;
1403
1404    case POP3_AUTH_LOGIN:
1405      result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
1406      break;
1407
1408    case POP3_AUTH_LOGIN_PASSWD:
1409      result = pop3_state_auth_login_password_resp(conn, pop3code,
1410                                                   pop3c->state);
1411      break;
1412
1413#ifndef CURL_DISABLE_CRYPTO_AUTH
1414    case POP3_AUTH_CRAMMD5:
1415      result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
1416      break;
1417
1418    case POP3_AUTH_DIGESTMD5:
1419      result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
1420      break;
1421
1422    case POP3_AUTH_DIGESTMD5_RESP:
1423      result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
1424      break;
1425#endif
1426
1427#ifdef USE_NTLM
1428    case POP3_AUTH_NTLM:
1429      result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
1430      break;
1431
1432    case POP3_AUTH_NTLM_TYPE2MSG:
1433      result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
1434                                                  pop3c->state);
1435      break;
1436#endif
1437
1438    case POP3_AUTH_XOAUTH2:
1439      result = pop3_state_auth_xoauth2_resp(conn, pop3code, pop3c->state);
1440      break;
1441
1442    case POP3_AUTH_CANCEL:
1443      result = pop3_state_auth_cancel_resp(conn, pop3code, pop3c->state);
1444      break;
1445
1446    case POP3_AUTH_FINAL:
1447      result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
1448      break;
1449
1450#ifndef CURL_DISABLE_CRYPTO_AUTH
1451    case POP3_APOP:
1452      result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1453      break;
1454#endif
1455
1456    case POP3_USER:
1457      result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1458      break;
1459
1460    case POP3_PASS:
1461      result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1462      break;
1463
1464    case POP3_COMMAND:
1465      result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1466      break;
1467
1468    case POP3_QUIT:
1469      /* fallthrough, just stop! */
1470    default:
1471      /* internal error */
1472      state(conn, POP3_STOP);
1473      break;
1474    }
1475  } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1476
1477  return result;
1478}
1479
1480/* Called repeatedly until done from multi.c */
1481static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1482{
1483  CURLcode result = CURLE_OK;
1484  struct pop3_conn *pop3c = &conn->proto.pop3c;
1485
1486  if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1487    result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1488    if(result || !pop3c->ssldone)
1489      return result;
1490  }
1491
1492  result = Curl_pp_statemach(&pop3c->pp, FALSE);
1493  *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1494
1495  return result;
1496}
1497
1498static CURLcode pop3_block_statemach(struct connectdata *conn)
1499{
1500  CURLcode result = CURLE_OK;
1501  struct pop3_conn *pop3c = &conn->proto.pop3c;
1502
1503  while(pop3c->state != POP3_STOP && !result)
1504    result = Curl_pp_statemach(&pop3c->pp, TRUE);
1505
1506  return result;
1507}
1508
1509/* Allocate and initialize the POP3 struct for the current SessionHandle if
1510   required */
1511static CURLcode pop3_init(struct connectdata *conn)
1512{
1513  CURLcode result = CURLE_OK;
1514  struct SessionHandle *data = conn->data;
1515  struct POP3 *pop3;
1516
1517  pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
1518  if(!pop3)
1519    result = CURLE_OUT_OF_MEMORY;
1520
1521  return result;
1522}
1523
1524/* For the POP3 "protocol connect" and "doing" phases only */
1525static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1526                        int numsocks)
1527{
1528  return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1529}
1530
1531/***********************************************************************
1532 *
1533 * pop3_connect()
1534 *
1535 * This function should do everything that is to be considered a part of the
1536 * connection phase.
1537 *
1538 * The variable 'done' points to will be TRUE if the protocol-layer connect
1539 * phase is done when this function returns, or FALSE if not.
1540 */
1541static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1542{
1543  CURLcode result = CURLE_OK;
1544  struct pop3_conn *pop3c = &conn->proto.pop3c;
1545  struct pingpong *pp = &pop3c->pp;
1546
1547  *done = FALSE; /* default to not done yet */
1548
1549  /* We always support persistent connections in POP3 */
1550  connkeep(conn, "POP3 default");
1551
1552  /* Set the default response time-out */
1553  pp->response_time = RESP_TIMEOUT;
1554  pp->statemach_act = pop3_statemach_act;
1555  pp->endofresp = pop3_endofresp;
1556  pp->conn = conn;
1557
1558  /* Set the default preferred authentication type and mechanism */
1559  pop3c->preftype = POP3_TYPE_ANY;
1560  pop3c->prefmech = SASL_AUTH_ANY;
1561
1562  /* Initialise the pingpong layer */
1563  Curl_pp_init(pp);
1564
1565  /* Parse the URL options */
1566  result = pop3_parse_url_options(conn);
1567  if(result)
1568    return result;
1569
1570  /* Start off waiting for the server greeting response */
1571  state(conn, POP3_SERVERGREET);
1572
1573  result = pop3_multi_statemach(conn, done);
1574
1575  return result;
1576}
1577
1578/***********************************************************************
1579 *
1580 * pop3_done()
1581 *
1582 * The DONE function. This does what needs to be done after a single DO has
1583 * performed.
1584 *
1585 * Input argument is already checked for validity.
1586 */
1587static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1588                          bool premature)
1589{
1590  CURLcode result = CURLE_OK;
1591  struct SessionHandle *data = conn->data;
1592  struct POP3 *pop3 = data->req.protop;
1593
1594  (void)premature;
1595
1596  if(!pop3)
1597    /* When the easy handle is removed from the multi interface while libcurl
1598       is still trying to resolve the host name, the POP3 struct is not yet
1599       initialized. However, the removal action calls Curl_done() which in
1600       turn calls this function, so we simply return success. */
1601    return CURLE_OK;
1602
1603  if(status) {
1604    connclose(conn, "POP3 done with bad status");
1605    result = status;         /* use the already set error code */
1606  }
1607
1608  /* Cleanup our per-request based variables */
1609  Curl_safefree(pop3->id);
1610  Curl_safefree(pop3->custom);
1611
1612  /* Clear the transfer mode for the next request */
1613  pop3->transfer = FTPTRANSFER_BODY;
1614
1615  return result;
1616}
1617
1618/***********************************************************************
1619 *
1620 * pop3_perform()
1621 *
1622 * This is the actual DO function for POP3. Get a message/listing according to
1623 * the options previously setup.
1624 */
1625static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1626                             bool *dophase_done)
1627{
1628  /* This is POP3 and no proxy */
1629  CURLcode result = CURLE_OK;
1630  struct POP3 *pop3 = conn->data->req.protop;
1631
1632  DEBUGF(infof(conn->data, "DO phase starts\n"));
1633
1634  if(conn->data->set.opt_no_body) {
1635    /* Requested no body means no transfer */
1636    pop3->transfer = FTPTRANSFER_INFO;
1637  }
1638
1639  *dophase_done = FALSE; /* not done yet */
1640
1641  /* Start the first command in the DO phase */
1642  result = pop3_perform_command(conn);
1643  if(result)
1644    return result;
1645
1646  /* Run the state-machine */
1647  result = pop3_multi_statemach(conn, dophase_done);
1648
1649  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1650
1651  if(*dophase_done)
1652    DEBUGF(infof(conn->data, "DO phase is complete\n"));
1653
1654  return result;
1655}
1656
1657/***********************************************************************
1658 *
1659 * pop3_do()
1660 *
1661 * This function is registered as 'curl_do' function. It decodes the path
1662 * parts etc as a wrapper to the actual DO function (pop3_perform).
1663 *
1664 * The input argument is already checked for validity.
1665 */
1666static CURLcode pop3_do(struct connectdata *conn, bool *done)
1667{
1668  CURLcode result = CURLE_OK;
1669
1670  *done = FALSE; /* default to false */
1671
1672  /* Parse the URL path */
1673  result = pop3_parse_url_path(conn);
1674  if(result)
1675    return result;
1676
1677  /* Parse the custom request */
1678  result = pop3_parse_custom_request(conn);
1679  if(result)
1680    return result;
1681
1682  result = pop3_regular_transfer(conn, done);
1683
1684  return result;
1685}
1686
1687/***********************************************************************
1688 *
1689 * pop3_disconnect()
1690 *
1691 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1692 * resources. BLOCKING.
1693 */
1694static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
1695{
1696  struct pop3_conn *pop3c = &conn->proto.pop3c;
1697
1698  /* We cannot send quit unconditionally. If this connection is stale or
1699     bad in any way, sending quit and waiting around here will make the
1700     disconnect wait in vain and cause more problems than we need to. */
1701
1702  /* The POP3 session may or may not have been allocated/setup at this
1703     point! */
1704  if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
1705    if(!pop3_perform_quit(conn))
1706      (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1707
1708  /* Disconnect from the server */
1709  Curl_pp_disconnect(&pop3c->pp);
1710
1711  /* Cleanup the SASL module */
1712  Curl_sasl_cleanup(conn, pop3c->authused);
1713
1714  /* Cleanup our connection based variables */
1715  Curl_safefree(pop3c->apoptimestamp);
1716
1717  return CURLE_OK;
1718}
1719
1720/* Call this when the DO phase has completed */
1721static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1722{
1723  (void)conn;
1724  (void)connected;
1725
1726  return CURLE_OK;
1727}
1728
1729/* Called from multi.c while DOing */
1730static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1731{
1732  CURLcode result = pop3_multi_statemach(conn, dophase_done);
1733
1734  if(result)
1735    DEBUGF(infof(conn->data, "DO phase failed\n"));
1736  else if(*dophase_done) {
1737    result = pop3_dophase_done(conn, FALSE /* not connected */);
1738
1739    DEBUGF(infof(conn->data, "DO phase is complete\n"));
1740  }
1741
1742  return result;
1743}
1744
1745/***********************************************************************
1746 *
1747 * pop3_regular_transfer()
1748 *
1749 * The input argument is already checked for validity.
1750 *
1751 * Performs all commands done before a regular transfer between a local and a
1752 * remote host.
1753 */
1754static CURLcode pop3_regular_transfer(struct connectdata *conn,
1755                                      bool *dophase_done)
1756{
1757  CURLcode result = CURLE_OK;
1758  bool connected = FALSE;
1759  struct SessionHandle *data = conn->data;
1760
1761  /* Make sure size is unknown at this point */
1762  data->req.size = -1;
1763
1764  /* Set the progress data */
1765  Curl_pgrsSetUploadCounter(data, 0);
1766  Curl_pgrsSetDownloadCounter(data, 0);
1767  Curl_pgrsSetUploadSize(data, 0);
1768  Curl_pgrsSetDownloadSize(data, 0);
1769
1770  /* Carry out the perform */
1771  result = pop3_perform(conn, &connected, dophase_done);
1772
1773  /* Perform post DO phase operations if necessary */
1774  if(!result && *dophase_done)
1775    result = pop3_dophase_done(conn, connected);
1776
1777  return result;
1778}
1779
1780static CURLcode pop3_setup_connection(struct connectdata *conn)
1781{
1782  struct SessionHandle *data = conn->data;
1783
1784  /* Initialise the POP3 layer */
1785  CURLcode result = pop3_init(conn);
1786  if(result)
1787    return result;
1788
1789  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1790    /* Unless we have asked to tunnel POP3 operations through the proxy, we
1791       switch and use HTTP operations only */
1792#ifndef CURL_DISABLE_HTTP
1793    if(conn->handler == &Curl_handler_pop3)
1794      conn->handler = &Curl_handler_pop3_proxy;
1795    else {
1796#ifdef USE_SSL
1797      conn->handler = &Curl_handler_pop3s_proxy;
1798#else
1799      failf(data, "POP3S not supported!");
1800      return CURLE_UNSUPPORTED_PROTOCOL;
1801#endif
1802    }
1803
1804    /* set it up as an HTTP connection instead */
1805    return conn->handler->setup_connection(conn);
1806#else
1807    failf(data, "POP3 over http proxy requires HTTP support built-in!");
1808    return CURLE_UNSUPPORTED_PROTOCOL;
1809#endif
1810  }
1811
1812  data->state.path++;   /* don't include the initial slash */
1813
1814  return CURLE_OK;
1815}
1816
1817/***********************************************************************
1818 *
1819 * pop3_parse_url_options()
1820 *
1821 * Parse the URL login options.
1822 */
1823static CURLcode pop3_parse_url_options(struct connectdata *conn)
1824{
1825  CURLcode result = CURLE_OK;
1826  struct pop3_conn *pop3c = &conn->proto.pop3c;
1827  const char *options = conn->options;
1828  const char *ptr = options;
1829  bool reset = TRUE;
1830
1831  while(ptr && *ptr) {
1832    const char *key = ptr;
1833
1834    while(*ptr && *ptr != '=')
1835        ptr++;
1836
1837    if(strnequal(key, "AUTH", 4)) {
1838      size_t len = 0;
1839      const char *value = ++ptr;
1840
1841      if(reset) {
1842        reset = FALSE;
1843        pop3c->preftype = POP3_TYPE_NONE;
1844        pop3c->prefmech = SASL_AUTH_NONE;
1845      }
1846
1847      while(*ptr && *ptr != ';') {
1848        ptr++;
1849        len++;
1850      }
1851
1852      if(strnequal(value, "*", len)) {
1853        pop3c->preftype = POP3_TYPE_ANY;
1854        pop3c->prefmech = SASL_AUTH_ANY;
1855      }
1856      else if(strnequal(value, "+APOP", len)) {
1857        pop3c->preftype = POP3_TYPE_APOP;
1858        pop3c->prefmech = SASL_AUTH_NONE;
1859      }
1860      else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
1861        pop3c->preftype = POP3_TYPE_SASL;
1862        pop3c->prefmech |= SASL_MECH_LOGIN;
1863      }
1864      else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
1865        pop3c->preftype = POP3_TYPE_SASL;
1866        pop3c->prefmech |= SASL_MECH_PLAIN;
1867      }
1868      else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
1869        pop3c->preftype = POP3_TYPE_SASL;
1870        pop3c->prefmech |= SASL_MECH_CRAM_MD5;
1871      }
1872      else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
1873        pop3c->preftype = POP3_TYPE_SASL;
1874        pop3c->prefmech |= SASL_MECH_DIGEST_MD5;
1875      }
1876      else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
1877        pop3c->preftype = POP3_TYPE_SASL;
1878        pop3c->prefmech |= SASL_MECH_GSSAPI;
1879      }
1880      else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
1881        pop3c->preftype = POP3_TYPE_SASL;
1882        pop3c->prefmech |= SASL_MECH_NTLM;
1883      }
1884      else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
1885        pop3c->preftype = POP3_TYPE_SASL;
1886        pop3c->prefmech |= SASL_MECH_XOAUTH2;
1887      }
1888
1889      if(*ptr == ';')
1890        ptr++;
1891    }
1892    else
1893      result = CURLE_URL_MALFORMAT;
1894  }
1895
1896  return result;
1897}
1898
1899/***********************************************************************
1900 *
1901 * pop3_parse_url_path()
1902 *
1903 * Parse the URL path into separate path components.
1904 */
1905static CURLcode pop3_parse_url_path(struct connectdata *conn)
1906{
1907  /* The POP3 struct is already initialised in pop3_connect() */
1908  struct SessionHandle *data = conn->data;
1909  struct POP3 *pop3 = data->req.protop;
1910  const char *path = data->state.path;
1911
1912  /* URL decode the path for the message ID */
1913  return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1914}
1915
1916/***********************************************************************
1917 *
1918 * pop3_parse_custom_request()
1919 *
1920 * Parse the custom request.
1921 */
1922static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1923{
1924  CURLcode result = CURLE_OK;
1925  struct SessionHandle *data = conn->data;
1926  struct POP3 *pop3 = data->req.protop;
1927  const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1928
1929  /* URL decode the custom request */
1930  if(custom)
1931    result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1932
1933  return result;
1934}
1935
1936/***********************************************************************
1937 *
1938 * pop3_calc_sasl_details()
1939 *
1940 * Calculate the required login details for SASL authentication.
1941 */
1942static CURLcode pop3_calc_sasl_details(struct connectdata *conn,
1943                                       const char **mech,
1944                                       char **initresp, size_t *len,
1945                                       pop3state *state1, pop3state *state2)
1946{
1947  CURLcode result = CURLE_OK;
1948  struct SessionHandle *data = conn->data;
1949  struct pop3_conn *pop3c = &conn->proto.pop3c;
1950
1951  /* Calculate the supported authentication mechanism, by decreasing order of
1952     security, as well as the initial response where appropriate */
1953#ifndef CURL_DISABLE_CRYPTO_AUTH
1954  if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) &&
1955      (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) {
1956    *mech = SASL_MECH_STRING_DIGEST_MD5;
1957    *state1 = POP3_AUTH_DIGESTMD5;
1958    pop3c->authused = SASL_MECH_DIGEST_MD5;
1959  }
1960  else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) &&
1961          (pop3c->prefmech & SASL_MECH_CRAM_MD5)) {
1962    *mech = SASL_MECH_STRING_CRAM_MD5;
1963    *state1 = POP3_AUTH_CRAMMD5;
1964    pop3c->authused = SASL_MECH_CRAM_MD5;
1965  }
1966  else
1967#endif
1968#ifdef USE_NTLM
1969  if((pop3c->authmechs & SASL_MECH_NTLM) &&
1970      (pop3c->prefmech & SASL_MECH_NTLM)) {
1971    *mech = SASL_MECH_STRING_NTLM;
1972    *state1 = POP3_AUTH_NTLM;
1973    *state2 = POP3_AUTH_NTLM_TYPE2MSG;
1974    pop3c->authused = SASL_MECH_NTLM;
1975
1976    if(data->set.sasl_ir)
1977      result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1978                                                    &conn->ntlm,
1979                                                    initresp, len);
1980  }
1981  else
1982#endif
1983  if(((pop3c->authmechs & SASL_MECH_XOAUTH2) &&
1984      (pop3c->prefmech & SASL_MECH_XOAUTH2) &&
1985      (pop3c->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
1986    *mech = SASL_MECH_STRING_XOAUTH2;
1987    *state1 = POP3_AUTH_XOAUTH2;
1988    *state2 = POP3_AUTH_FINAL;
1989    pop3c->authused = SASL_MECH_XOAUTH2;
1990
1991    if(data->set.sasl_ir)
1992      result = Curl_sasl_create_xoauth2_message(data, conn->user,
1993                                                conn->xoauth2_bearer,
1994                                                initresp, len);
1995  }
1996  else if((pop3c->authmechs & SASL_MECH_LOGIN) &&
1997          (pop3c->prefmech & SASL_MECH_LOGIN)) {
1998    *mech = SASL_MECH_STRING_LOGIN;
1999    *state1 = POP3_AUTH_LOGIN;
2000    *state2 = POP3_AUTH_LOGIN_PASSWD;
2001    pop3c->authused = SASL_MECH_LOGIN;
2002
2003    if(data->set.sasl_ir)
2004      result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2005  }
2006  else if((pop3c->authmechs & SASL_MECH_PLAIN) &&
2007          (pop3c->prefmech & SASL_MECH_PLAIN)) {
2008    *mech = SASL_MECH_STRING_PLAIN;
2009    *state1 = POP3_AUTH_PLAIN;
2010    *state2 = POP3_AUTH_FINAL;
2011    pop3c->authused = SASL_MECH_PLAIN;
2012
2013    if(data->set.sasl_ir)
2014      result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2015                                              initresp, len);
2016  }
2017
2018  return result;
2019}
2020
2021/***********************************************************************
2022 *
2023 * Curl_pop3_write()
2024 *
2025 * This function scans the body after the end-of-body and writes everything
2026 * until the end is found.
2027 */
2028CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
2029{
2030  /* This code could be made into a special function in the handler struct */
2031  CURLcode result = CURLE_OK;
2032  struct SessionHandle *data = conn->data;
2033  struct SingleRequest *k = &data->req;
2034
2035  struct pop3_conn *pop3c = &conn->proto.pop3c;
2036  bool strip_dot = FALSE;
2037  size_t last = 0;
2038  size_t i;
2039
2040  /* Search through the buffer looking for the end-of-body marker which is
2041     5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
2042     the eob so the server will have prefixed it with an extra dot which we
2043     need to strip out. Additionally the marker could of course be spread out
2044     over 5 different data chunks. */
2045  for(i = 0; i < nread; i++) {
2046    size_t prev = pop3c->eob;
2047
2048    switch(str[i]) {
2049    case 0x0d:
2050      if(pop3c->eob == 0) {
2051        pop3c->eob++;
2052
2053        if(i) {
2054          /* Write out the body part that didn't match */
2055          result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
2056                                     i - last);
2057
2058          if(result)
2059            return result;
2060
2061          last = i;
2062        }
2063      }
2064      else if(pop3c->eob == 3)
2065        pop3c->eob++;
2066      else
2067        /* If the character match wasn't at position 0 or 3 then restart the
2068           pattern matching */
2069        pop3c->eob = 1;
2070      break;
2071
2072    case 0x0a:
2073      if(pop3c->eob == 1 || pop3c->eob == 4)
2074        pop3c->eob++;
2075      else
2076        /* If the character match wasn't at position 1 or 4 then start the
2077           search again */
2078        pop3c->eob = 0;
2079      break;
2080
2081    case 0x2e:
2082      if(pop3c->eob == 2)
2083        pop3c->eob++;
2084      else if(pop3c->eob == 3) {
2085        /* We have an extra dot after the CRLF which we need to strip off */
2086        strip_dot = TRUE;
2087        pop3c->eob = 0;
2088      }
2089      else
2090        /* If the character match wasn't at position 2 then start the search
2091           again */
2092        pop3c->eob = 0;
2093      break;
2094
2095    default:
2096      pop3c->eob = 0;
2097      break;
2098    }
2099
2100    /* Did we have a partial match which has subsequently failed? */
2101    if(prev && prev >= pop3c->eob) {
2102      /* Strip can only be non-zero for the very first mismatch after CRLF
2103         and then both prev and strip are equal and nothing will be output
2104         below */
2105      while(prev && pop3c->strip) {
2106        prev--;
2107        pop3c->strip--;
2108      }
2109
2110      if(prev) {
2111        /* If the partial match was the CRLF and dot then only write the CRLF
2112           as the server would have inserted the dot */
2113        result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
2114                                   strip_dot ? prev - 1 : prev);
2115
2116        if(result)
2117          return result;
2118
2119        last = i;
2120        strip_dot = FALSE;
2121      }
2122    }
2123  }
2124
2125  if(pop3c->eob == POP3_EOB_LEN) {
2126    /* We have a full match so the transfer is done, however we must transfer
2127    the CRLF at the start of the EOB as this is considered to be part of the
2128    message as per RFC-1939, sect. 3 */
2129    result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
2130
2131    k->keepon &= ~KEEP_RECV;
2132    pop3c->eob = 0;
2133
2134    return result;
2135  }
2136
2137  if(pop3c->eob)
2138    /* While EOB is matching nothing should be output */
2139    return CURLE_OK;
2140
2141  if(nread - last) {
2142    result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
2143                               nread - last);
2144  }
2145
2146  return result;
2147}
2148
2149#endif /* CURL_DISABLE_POP3 */
2150