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