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