VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/pop3.c@ 96762

Last change on this file since 96762 was 95312, checked in by vboxsync, 3 years ago

libs/{curl,libxml2}: OSE export fixes, bugref:8515

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette