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