VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/pop3.c@ 107504

Last change on this file since 107504 was 104083, checked in by vboxsync, 13 months ago

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

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