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