1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
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 * RFC5034 POP3 SASL Authentication Mechanism
32 ***************************************************************************/
34 #include "curl_setup.h"
36 #ifndef CURL_DISABLE_POP3
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
41 #ifdef HAVE_ARPA_INET_H
42 #include <arpa/inet.h>
45 #include <sys/utsname.h>
55 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57 #define in_addr_t unsigned long
60 #include <curl/curl.h>
68 #include "http.h" /* for HTTP proxy tunnel stuff */
72 #include "strtoofft.h"
81 #include "curl_sasl.h"
85 #define _MPRINTF_REPLACE /* use our functions only */
86 #include <curl/mprintf.h>
88 #include "curl_memory.h"
89 /* The last #include file should be: */
92 /* Local API functions */
93 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
94 static CURLcode pop3_do(struct connectdata *conn, bool *done);
95 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
97 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
98 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
99 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
100 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
102 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
103 static CURLcode pop3_setup_connection(struct connectdata *conn);
104 static CURLcode pop3_parse_url_options(struct connectdata *conn);
105 static CURLcode pop3_parse_url_path(struct connectdata *conn);
106 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
109 * POP3 protocol handler.
112 const struct Curl_handler Curl_handler_pop3 = {
114 pop3_setup_connection, /* setup_connection */
116 pop3_done, /* done */
117 ZERO_NULL, /* do_more */
118 pop3_connect, /* connect_it */
119 pop3_multi_statemach, /* connecting */
120 pop3_doing, /* doing */
121 pop3_getsock, /* proto_getsock */
122 pop3_getsock, /* doing_getsock */
123 ZERO_NULL, /* domore_getsock */
124 ZERO_NULL, /* perform_getsock */
125 pop3_disconnect, /* disconnect */
126 ZERO_NULL, /* readwrite */
127 PORT_POP3, /* defport */
128 CURLPROTO_POP3, /* protocol */
129 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
134 * POP3S protocol handler.
137 const struct Curl_handler Curl_handler_pop3s = {
138 "POP3S", /* scheme */
139 pop3_setup_connection, /* setup_connection */
141 pop3_done, /* done */
142 ZERO_NULL, /* do_more */
143 pop3_connect, /* connect_it */
144 pop3_multi_statemach, /* connecting */
145 pop3_doing, /* doing */
146 pop3_getsock, /* proto_getsock */
147 pop3_getsock, /* doing_getsock */
148 ZERO_NULL, /* domore_getsock */
149 ZERO_NULL, /* perform_getsock */
150 pop3_disconnect, /* disconnect */
151 ZERO_NULL, /* readwrite */
152 PORT_POP3S, /* defport */
153 CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */
154 PROTOPT_CLOSEACTION | PROTOPT_SSL
155 | PROTOPT_NOURLQUERY /* flags */
159 #ifndef CURL_DISABLE_HTTP
161 * HTTP-proxyed POP3 protocol handler.
164 static const struct Curl_handler Curl_handler_pop3_proxy = {
166 ZERO_NULL, /* setup_connection */
167 Curl_http, /* do_it */
168 Curl_http_done, /* done */
169 ZERO_NULL, /* do_more */
170 ZERO_NULL, /* connect_it */
171 ZERO_NULL, /* connecting */
172 ZERO_NULL, /* doing */
173 ZERO_NULL, /* proto_getsock */
174 ZERO_NULL, /* doing_getsock */
175 ZERO_NULL, /* domore_getsock */
176 ZERO_NULL, /* perform_getsock */
177 ZERO_NULL, /* disconnect */
178 ZERO_NULL, /* readwrite */
179 PORT_POP3, /* defport */
180 CURLPROTO_HTTP, /* protocol */
181 PROTOPT_NONE /* flags */
186 * HTTP-proxyed POP3S protocol handler.
189 static const struct Curl_handler Curl_handler_pop3s_proxy = {
190 "POP3S", /* scheme */
191 ZERO_NULL, /* setup_connection */
192 Curl_http, /* do_it */
193 Curl_http_done, /* done */
194 ZERO_NULL, /* do_more */
195 ZERO_NULL, /* connect_it */
196 ZERO_NULL, /* connecting */
197 ZERO_NULL, /* doing */
198 ZERO_NULL, /* proto_getsock */
199 ZERO_NULL, /* doing_getsock */
200 ZERO_NULL, /* domore_getsock */
201 ZERO_NULL, /* perform_getsock */
202 ZERO_NULL, /* disconnect */
203 ZERO_NULL, /* readwrite */
204 PORT_POP3S, /* defport */
205 CURLPROTO_HTTP, /* protocol */
206 PROTOPT_NONE /* flags */
212 static void pop3_to_pop3s(struct connectdata *conn)
214 conn->handler = &Curl_handler_pop3s;
217 #define pop3_to_pop3s(x) Curl_nop_stmt
220 /***********************************************************************
224 * Checks for an ending POP3 status code at the start of the given string, but
225 * also detects the APOP timestamp from the server greeting and various
226 * capabilities from the CAPA response including the supported authentication
227 * types and allowed SASL mechanisms.
229 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
232 struct pop3_conn *pop3c = &conn->proto.pop3c;
236 /* Do we have an error response? */
237 if(len >= 4 && !memcmp("-ERR", line, 4)) {
243 /* Are we processing servergreet responses? */
244 if(pop3c->state == POP3_SERVERGREET) {
245 /* Look for the APOP timestamp */
246 if(len >= 3 && line[len - 3] == '>') {
247 for(i = 0; i < len - 3; ++i) {
249 /* Calculate the length of the timestamp */
250 size_t timestamplen = len - 2 - i;
252 /* Allocate some memory for the timestamp */
253 pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
255 if(!pop3c->apoptimestamp)
258 /* Copy the timestamp */
259 memcpy(pop3c->apoptimestamp, line + i, timestamplen);
260 pop3c->apoptimestamp[timestamplen] = '\0';
266 /* Are we processing CAPA command responses? */
267 else if(pop3c->state == POP3_CAPA) {
268 /* Do we have the terminating line? */
269 if(len >= 1 && !memcmp(line, ".", 1)) {
275 /* Does the server support the STLS capability? */
276 if(len >= 4 && !memcmp(line, "STLS", 4))
277 pop3c->tls_supported = TRUE;
279 /* Does the server support clear text authentication? */
280 else if(len >= 4 && !memcmp(line, "USER", 4))
281 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
283 /* Does the server support APOP authentication? */
284 else if(len >= 4 && !memcmp(line, "APOP", 4))
285 pop3c->authtypes |= POP3_TYPE_APOP;
287 /* Does the server support SASL based authentication? */
288 else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
289 pop3c->authtypes |= POP3_TYPE_SASL;
291 /* Advance past the SASL keyword */
295 /* Loop through the data line */
298 (*line == ' ' || *line == '\t' ||
299 *line == '\r' || *line == '\n')) {
308 /* Extract the word */
309 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
310 line[wordlen] != '\t' && line[wordlen] != '\r' &&
311 line[wordlen] != '\n';)
314 /* Test the word for a matching authentication mechanism */
315 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
316 pop3c->authmechs |= SASL_MECH_LOGIN;
317 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
318 pop3c->authmechs |= SASL_MECH_PLAIN;
319 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
320 pop3c->authmechs |= SASL_MECH_CRAM_MD5;
321 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
322 pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
323 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
324 pop3c->authmechs |= SASL_MECH_GSSAPI;
325 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
326 pop3c->authmechs |= SASL_MECH_EXTERNAL;
327 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
328 pop3c->authmechs |= SASL_MECH_NTLM;
338 /* Do we have a command or continuation response? */
339 if((len >= 3 && !memcmp("+OK", line, 3)) ||
340 (len >= 1 && !memcmp("+", line, 1))) {
346 return FALSE; /* Nothing for us */
349 /***********************************************************************
353 * This is the ONLY way to change POP3 state!
355 static void state(struct connectdata *conn, pop3state newstate)
357 struct pop3_conn *pop3c = &conn->proto.pop3c;
358 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
359 /* for debug purposes */
360 static const char * const names[] = {
371 "AUTH_DIGESTMD5_RESP",
373 "AUTH_NTLM_TYPE2MSG",
383 if(pop3c->state != newstate)
384 infof(conn->data, "POP3 %p state change from %s to %s\n",
385 pop3c, names[pop3c->state], names[newstate]);
388 pop3c->state = newstate;
391 /***********************************************************************
393 * pop3_perform_capa()
395 * Sends the CAPA command in order to obtain a list of server side supported
398 static CURLcode pop3_perform_capa(struct connectdata *conn)
400 CURLcode result = CURLE_OK;
401 struct pop3_conn *pop3c = &conn->proto.pop3c;
403 pop3c->authmechs = 0; /* No known authentication mechanisms yet */
404 pop3c->authused = 0; /* Clear the authentication mechanism used */
405 pop3c->tls_supported = FALSE; /* Clear the TLS capability */
407 /* Send the CAPA command */
408 result = Curl_pp_sendf(&pop3c->pp, "CAPA");
411 state(conn, POP3_CAPA);
416 /***********************************************************************
418 * pop3_perform_starttls()
420 * Sends the STLS command to start the upgrade to TLS.
422 static CURLcode pop3_perform_starttls(struct connectdata *conn)
424 CURLcode result = CURLE_OK;
426 /* Send the STLS command */
427 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "STLS");
430 state(conn, POP3_STARTTLS);
435 /***********************************************************************
437 * pop3_perform_upgrade_tls()
439 * Performs the upgrade to TLS.
441 static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
443 CURLcode result = CURLE_OK;
444 struct pop3_conn *pop3c = &conn->proto.pop3c;
446 /* Start the SSL connection */
447 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
450 if(pop3c->state != POP3_UPGRADETLS)
451 state(conn, POP3_UPGRADETLS);
455 result = pop3_perform_capa(conn);
462 /***********************************************************************
464 * pop3_perform_user()
466 * Sends a clear text USER command to authenticate with.
468 static CURLcode pop3_perform_user(struct connectdata *conn)
470 CURLcode result = CURLE_OK;
472 /* Check we have a username and password to authenticate with and end the
473 connect phase if we don't */
474 if(!conn->bits.user_passwd) {
475 state(conn, POP3_STOP);
480 /* Send the USER command */
481 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
482 conn->user ? conn->user : "");
484 state(conn, POP3_USER);
489 #ifndef CURL_DISABLE_CRYPTO_AUTH
490 /***********************************************************************
492 * pop3_perform_apop()
494 * Sends an APOP command to authenticate with.
496 static CURLcode pop3_perform_apop(struct connectdata *conn)
498 CURLcode result = CURLE_OK;
499 struct pop3_conn *pop3c = &conn->proto.pop3c;
502 unsigned char digest[MD5_DIGEST_LEN];
503 char secret[2 * MD5_DIGEST_LEN + 1];
505 /* Check we have a username and password to authenticate with and end the
506 connect phase if we don't */
507 if(!conn->bits.user_passwd) {
508 state(conn, POP3_STOP);
513 /* Create the digest */
514 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
516 return CURLE_OUT_OF_MEMORY;
518 Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
519 curlx_uztoui(strlen(pop3c->apoptimestamp)));
521 Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
522 curlx_uztoui(strlen(conn->passwd)));
524 /* Finalise the digest */
525 Curl_MD5_final(ctxt, digest);
527 /* Convert the calculated 16 octet digest into a 32 byte hex string */
528 for(i = 0; i < MD5_DIGEST_LEN; i++)
529 snprintf(&secret[2 * i], 3, "%02x", digest[i]);
531 result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
534 state(conn, POP3_APOP);
540 /***********************************************************************
542 * pop3_perform_authenticate()
544 * Sends an AUTH command allowing the client to login with the appropriate
545 * SASL authentication mechanism.
547 * Additionally, the function will perform fallback to APOP and USER commands
548 * should a common mechanism not be available between the client and server.
550 static CURLcode pop3_perform_authenticate(struct connectdata *conn)
552 CURLcode result = CURLE_OK;
553 struct SessionHandle *data = conn->data;
554 struct pop3_conn *pop3c = &conn->proto.pop3c;
555 const char *mech = NULL;
556 char *initresp = NULL;
558 pop3state state1 = POP3_STOP;
559 pop3state state2 = POP3_STOP;
561 /* Check we have a username and password to authenticate with and end the
562 connect phase if we don't */
563 if(!conn->bits.user_passwd) {
564 state(conn, POP3_STOP);
569 /* Calculate the supported authentication mechanism by decreasing order of
571 if(pop3c->authtypes & POP3_TYPE_SASL) {
572 #ifndef CURL_DISABLE_CRYPTO_AUTH
573 if((pop3c->authmechs & SASL_MECH_DIGEST_MD5) &&
574 (pop3c->prefmech & SASL_MECH_DIGEST_MD5)) {
576 state1 = POP3_AUTH_DIGESTMD5;
577 pop3c->authused = SASL_MECH_DIGEST_MD5;
579 else if((pop3c->authmechs & SASL_MECH_CRAM_MD5) &&
580 (pop3c->prefmech & SASL_MECH_CRAM_MD5)) {
582 state1 = POP3_AUTH_CRAMMD5;
583 pop3c->authused = SASL_MECH_CRAM_MD5;
588 if((pop3c->authmechs & SASL_MECH_NTLM) &&
589 (pop3c->prefmech & SASL_MECH_NTLM)) {
591 state1 = POP3_AUTH_NTLM;
592 state2 = POP3_AUTH_NTLM_TYPE2MSG;
593 pop3c->authused = SASL_MECH_NTLM;
595 if(data->set.sasl_ir)
596 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
602 if((pop3c->authmechs & SASL_MECH_LOGIN) &&
603 (pop3c->prefmech & SASL_MECH_LOGIN)) {
605 state1 = POP3_AUTH_LOGIN;
606 state2 = POP3_AUTH_LOGIN_PASSWD;
607 pop3c->authused = SASL_MECH_LOGIN;
609 if(data->set.sasl_ir)
610 result = Curl_sasl_create_login_message(conn->data, conn->user,
613 else if((pop3c->authmechs & SASL_MECH_PLAIN) &&
614 (pop3c->prefmech & SASL_MECH_PLAIN)) {
616 state1 = POP3_AUTH_PLAIN;
617 state2 = POP3_AUTH_FINAL;
618 pop3c->authused = SASL_MECH_PLAIN;
620 if(data->set.sasl_ir)
621 result = Curl_sasl_create_plain_message(conn->data, conn->user,
622 conn->passwd, &initresp,
630 if(mech && (pop3c->preftype & POP3_TYPE_SASL)) {
631 /* Perform SASL based authentication */
633 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
639 /* Perform SASL based authentication */
640 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
646 Curl_safefree(initresp);
648 #ifndef CURL_DISABLE_CRYPTO_AUTH
649 else if((pop3c->authtypes & POP3_TYPE_APOP) &&
650 (pop3c->preftype & POP3_TYPE_APOP))
651 /* Perform APOP authentication */
652 result = pop3_perform_apop(conn);
654 else if((pop3c->authtypes & POP3_TYPE_CLEARTEXT) &&
655 (pop3c->preftype & POP3_TYPE_CLEARTEXT))
656 /* Perform clear text authentication */
657 result = pop3_perform_user(conn);
659 /* Other mechanisms not supported */
660 infof(conn->data, "No known authentication mechanisms supported!\n");
661 result = CURLE_LOGIN_DENIED;
667 /***********************************************************************
669 * pop3_perform_command()
671 * Sends a POP3 based command.
673 static CURLcode pop3_perform_command(struct connectdata *conn)
675 CURLcode result = CURLE_OK;
676 struct SessionHandle *data = conn->data;
677 struct POP3 *pop3 = data->state.proto.pop3;
678 const char *command = NULL;
680 /* Calculate the default command */
681 if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
684 if(pop3->id[0] != '\0')
685 /* Message specific LIST so skip the BODY transfer */
686 pop3->transfer = FTPTRANSFER_INFO;
691 /* Send the command */
692 if(pop3->id[0] != '\0')
693 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
694 (pop3->custom && pop3->custom[0] != '\0' ?
695 pop3->custom : command), pop3->id);
697 result = Curl_pp_sendf(&conn->proto.pop3c.pp,
698 (pop3->custom && pop3->custom[0] != '\0' ?
699 pop3->custom : command));
702 state(conn, POP3_COMMAND);
707 /***********************************************************************
709 * pop3_perform_quit()
711 * Performs the quit action prior to sclose() be called.
713 static CURLcode pop3_perform_quit(struct connectdata *conn)
715 CURLcode result = CURLE_OK;
717 /* Send the QUIT command */
718 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT");
721 state(conn, POP3_QUIT);
726 /* For the initial server greeting */
727 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
731 CURLcode result = CURLE_OK;
732 struct SessionHandle *data = conn->data;
734 (void)instate; /* no use for this yet */
736 if(pop3code != '+') {
737 failf(data, "Got unexpected pop3-server response");
738 result = CURLE_FTP_WEIRD_SERVER_REPLY;
741 result = pop3_perform_capa(conn);
746 /* For CAPA responses */
747 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
750 CURLcode result = CURLE_OK;
751 struct SessionHandle *data = conn->data;
752 struct pop3_conn *pop3c = &conn->proto.pop3c;
754 (void)instate; /* no use for this yet */
757 result = pop3_perform_user(conn);
758 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
759 /* We don't have a SSL/TLS connection yet, but SSL is requested */
760 if(pop3c->tls_supported)
761 /* Switch to TLS connection now */
762 result = pop3_perform_starttls(conn);
763 else if(data->set.use_ssl == CURLUSESSL_TRY)
764 /* Fallback and carry on with authentication */
765 result = pop3_perform_authenticate(conn);
767 failf(data, "STLS not supported.");
768 result = CURLE_USE_SSL_FAILED;
772 result = pop3_perform_authenticate(conn);
777 /* For STARTTLS responses */
778 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
782 CURLcode result = CURLE_OK;
783 struct SessionHandle *data = conn->data;
785 (void)instate; /* no use for this yet */
787 if(pop3code != '+') {
788 if(data->set.use_ssl != CURLUSESSL_TRY) {
789 failf(data, "STARTTLS denied. %c", pop3code);
790 result = CURLE_USE_SSL_FAILED;
793 result = pop3_perform_authenticate(conn);
796 result = pop3_perform_upgrade_tls(conn);
801 /* For AUTH PLAIN (without initial response) responses */
802 static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
806 CURLcode result = CURLE_OK;
807 struct SessionHandle *data = conn->data;
809 char *plainauth = NULL;
811 (void)instate; /* no use for this yet */
813 if(pop3code != '+') {
814 failf(data, "Access denied. %c", pop3code);
815 result = CURLE_LOGIN_DENIED;
818 /* Create the authorisation message */
819 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
822 /* Send the message */
825 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
828 state(conn, POP3_AUTH_FINAL);
831 Curl_safefree(plainauth);
838 /* For AUTH LOGIN (without initial response) responses */
839 static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
843 CURLcode result = CURLE_OK;
844 struct SessionHandle *data = conn->data;
846 char *authuser = NULL;
848 (void)instate; /* no use for this yet */
850 if(pop3code != '+') {
851 failf(data, "Access denied: %d", pop3code);
852 result = CURLE_LOGIN_DENIED;
855 /* Create the user message */
856 result = Curl_sasl_create_login_message(data, conn->user,
862 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
865 state(conn, POP3_AUTH_LOGIN_PASSWD);
868 Curl_safefree(authuser);
875 /* For AUTH LOGIN user entry responses */
876 static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
880 CURLcode result = CURLE_OK;
881 struct SessionHandle *data = conn->data;
883 char *authpasswd = NULL;
885 (void)instate; /* no use for this yet */
887 if(pop3code != '+') {
888 failf(data, "Access denied: %d", pop3code);
889 result = CURLE_LOGIN_DENIED;
892 /* Create the password message */
893 result = Curl_sasl_create_login_message(data, conn->passwd,
896 /* Send the password */
899 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
902 state(conn, POP3_AUTH_FINAL);
905 Curl_safefree(authpasswd);
912 #ifndef CURL_DISABLE_CRYPTO_AUTH
913 /* For AUTH CRAM-MD5 responses */
914 static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
918 CURLcode result = CURLE_OK;
919 struct SessionHandle *data = conn->data;
920 char *chlg64 = data->state.buffer;
922 char *rplyb64 = NULL;
924 (void)instate; /* no use for this yet */
926 if(pop3code != '+') {
927 failf(data, "Access denied: %d", pop3code);
928 return CURLE_LOGIN_DENIED;
931 /* Get the challenge */
932 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
935 /* Terminate the challenge */
937 for(len = strlen(chlg64); len--;)
938 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
947 /* Create the response message */
948 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
949 conn->passwd, &rplyb64, &len);
951 /* Send the response */
954 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
957 state(conn, POP3_AUTH_FINAL);
960 Curl_safefree(rplyb64);
966 /* For AUTH DIGEST-MD5 challenge responses */
967 static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
971 CURLcode result = CURLE_OK;
972 struct SessionHandle *data = conn->data;
973 char *chlg64 = data->state.buffer;
975 char *rplyb64 = NULL;
977 (void)instate; /* no use for this yet */
979 if(pop3code != '+') {
980 failf(data, "Access denied: %d", pop3code);
981 return CURLE_LOGIN_DENIED;
984 /* Get the challenge */
985 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
988 /* Create the response message */
989 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
993 /* Send the response */
996 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
999 state(conn, POP3_AUTH_DIGESTMD5_RESP);
1002 Curl_safefree(rplyb64);
1008 /* For AUTH DIGEST-MD5 challenge-response responses */
1009 static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
1013 CURLcode result = CURLE_OK;
1014 struct SessionHandle *data = conn->data;
1016 (void)instate; /* no use for this yet */
1018 if(pop3code != '+') {
1019 failf(data, "Authentication failed: %d", pop3code);
1020 result = CURLE_LOGIN_DENIED;
1023 /* Send an empty response */
1024 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "");
1027 state(conn, POP3_AUTH_FINAL);
1035 /* For AUTH NTLM (without initial response) responses */
1036 static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
1040 CURLcode result = CURLE_OK;
1041 struct SessionHandle *data = conn->data;
1043 char *type1msg = NULL;
1045 (void)instate; /* no use for this yet */
1047 if(pop3code != '+') {
1048 failf(data, "Access denied: %d", pop3code);
1049 result = CURLE_LOGIN_DENIED;
1052 /* Create the type-1 message */
1053 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1057 /* Send the message */
1060 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
1063 state(conn, POP3_AUTH_NTLM_TYPE2MSG);
1066 Curl_safefree(type1msg);
1073 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1074 static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1078 CURLcode result = CURLE_OK;
1079 struct SessionHandle *data = conn->data;
1081 char *type3msg = NULL;
1083 (void)instate; /* no use for this yet */
1085 if(pop3code != '+') {
1086 failf(data, "Access denied: %d", pop3code);
1087 result = CURLE_LOGIN_DENIED;
1090 /* Create the type-3 message */
1091 result = Curl_sasl_create_ntlm_type3_message(data,
1092 data->state.buffer + 2,
1093 conn->user, conn->passwd,
1097 /* Send the message */
1100 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
1103 state(conn, POP3_AUTH_FINAL);
1106 Curl_safefree(type3msg);
1114 /* For final responses to the AUTH sequence */
1115 static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
1119 CURLcode result = CURLE_OK;
1120 struct SessionHandle *data = conn->data;
1122 (void)instate; /* no use for this yet */
1124 if(pop3code != '+') {
1125 failf(data, "Authentication failed: %d", pop3code);
1126 result = CURLE_LOGIN_DENIED;
1129 /* End of connect phase */
1130 state(conn, POP3_STOP);
1135 #ifndef CURL_DISABLE_CRYPTO_AUTH
1136 /* For APOP responses */
1137 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
1140 CURLcode result = CURLE_OK;
1141 struct SessionHandle *data = conn->data;
1143 (void)instate; /* no use for this yet */
1145 if(pop3code != '+') {
1146 failf(data, "Authentication failed: %d", pop3code);
1147 result = CURLE_LOGIN_DENIED;
1150 /* End of connect phase */
1151 state(conn, POP3_STOP);
1157 /* For USER responses */
1158 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
1161 CURLcode result = CURLE_OK;
1162 struct SessionHandle *data = conn->data;
1164 (void)instate; /* no use for this yet */
1166 if(pop3code != '+') {
1167 failf(data, "Access denied. %c", pop3code);
1168 result = CURLE_LOGIN_DENIED;
1171 /* Send the PASS command */
1172 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
1173 conn->passwd ? conn->passwd : "");
1175 state(conn, POP3_PASS);
1180 /* For PASS responses */
1181 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
1184 CURLcode result = CURLE_OK;
1185 struct SessionHandle *data = conn->data;
1187 (void)instate; /* no use for this yet */
1189 if(pop3code != '+') {
1190 failf(data, "Access denied. %c", pop3code);
1191 result = CURLE_LOGIN_DENIED;
1194 /* End of connect phase */
1195 state(conn, POP3_STOP);
1200 /* For command responses */
1201 static CURLcode pop3_state_command_resp(struct connectdata *conn,
1205 CURLcode result = CURLE_OK;
1206 struct SessionHandle *data = conn->data;
1207 struct POP3 *pop3 = data->state.proto.pop3;
1208 struct pop3_conn *pop3c = &conn->proto.pop3c;
1209 struct pingpong *pp = &pop3c->pp;
1211 (void)instate; /* no use for this yet */
1213 if(pop3code != '+') {
1214 state(conn, POP3_STOP);
1215 return CURLE_RECV_ERROR;
1218 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1219 EOB string so count this is two matching bytes. This is necessary to make
1220 the code detect the EOB if the only data than comes now is %2e CR LF like
1221 when there is no body to return. */
1224 /* But since this initial CR LF pair is not part of the actual body, we set
1225 the strip counter here so that these bytes won't be delivered. */
1228 if(pop3->transfer == FTPTRANSFER_BODY) {
1230 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
1233 /* The header "cache" contains a bunch of data that is actually body
1234 content so send it as such. Note that there may even be additional
1235 "headers" after the body */
1237 if(!data->set.opt_no_body) {
1238 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
1243 /* Free the cache */
1244 Curl_safefree(pp->cache);
1246 /* Reset the cache size */
1251 /* End of DO phase */
1252 state(conn, POP3_STOP);
1257 static CURLcode pop3_statemach_act(struct connectdata *conn)
1259 CURLcode result = CURLE_OK;
1260 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1262 struct pop3_conn *pop3c = &conn->proto.pop3c;
1263 struct pingpong *pp = &pop3c->pp;
1266 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1267 if(pop3c->state == POP3_UPGRADETLS)
1268 return pop3_perform_upgrade_tls(conn);
1270 /* Flush any data that needs to be sent */
1272 return Curl_pp_flushsend(pp);
1274 /* Read the response from the server */
1275 result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1280 /* We have now received a full POP3 server response */
1281 switch(pop3c->state) {
1282 case POP3_SERVERGREET:
1283 result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1287 result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1291 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1294 case POP3_AUTH_PLAIN:
1295 result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
1298 case POP3_AUTH_LOGIN:
1299 result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
1302 case POP3_AUTH_LOGIN_PASSWD:
1303 result = pop3_state_auth_login_password_resp(conn, pop3code,
1307 #ifndef CURL_DISABLE_CRYPTO_AUTH
1308 case POP3_AUTH_CRAMMD5:
1309 result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
1312 case POP3_AUTH_DIGESTMD5:
1313 result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
1316 case POP3_AUTH_DIGESTMD5_RESP:
1317 result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
1322 case POP3_AUTH_NTLM:
1323 result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
1326 case POP3_AUTH_NTLM_TYPE2MSG:
1327 result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
1332 case POP3_AUTH_FINAL:
1333 result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
1336 #ifndef CURL_DISABLE_CRYPTO_AUTH
1338 result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1343 result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1347 result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1351 result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1355 /* fallthrough, just stop! */
1357 /* internal error */
1358 state(conn, POP3_STOP);
1366 /* Called repeatedly until done from multi.c */
1367 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1369 CURLcode result = CURLE_OK;
1370 struct pop3_conn *pop3c = &conn->proto.pop3c;
1372 if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone)
1373 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1375 result = Curl_pp_statemach(&pop3c->pp, FALSE);
1377 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1382 static CURLcode pop3_block_statemach(struct connectdata *conn)
1384 CURLcode result = CURLE_OK;
1385 struct pop3_conn *pop3c = &conn->proto.pop3c;
1387 while(pop3c->state != POP3_STOP && !result)
1388 result = Curl_pp_statemach(&pop3c->pp, TRUE);
1393 /* Allocate and initialize the POP3 struct for the current SessionHandle if
1395 static CURLcode pop3_init(struct connectdata *conn)
1397 CURLcode result = CURLE_OK;
1398 struct SessionHandle *data = conn->data;
1399 struct POP3 *pop3 = data->state.proto.pop3;
1402 pop3 = data->state.proto.pop3 = calloc(sizeof(struct POP3), 1);
1404 result = CURLE_OUT_OF_MEMORY;
1410 /* For the POP3 "protocol connect" and "doing" phases only */
1411 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1414 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1417 /***********************************************************************
1421 * This function should do everything that is to be considered a part of the
1424 * The variable 'done' points to will be TRUE if the protocol-layer connect
1425 * phase is done when this function returns, or FALSE is not. When called as
1426 * a part of the easy interface, it will always be TRUE.
1428 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1430 CURLcode result = CURLE_OK;
1431 struct pop3_conn *pop3c = &conn->proto.pop3c;
1432 struct pingpong *pp = &pop3c->pp;
1434 *done = FALSE; /* default to not done yet */
1436 /* If there already is a protocol-specific struct allocated for this
1437 sessionhandle, deal with it */
1438 Curl_reset_reqproto(conn);
1440 /* Initialise the POP3 layer */
1441 result = pop3_init(conn);
1445 /* We always support persistent connections in POP3 */
1446 conn->bits.close = FALSE;
1448 /* Set the default response time-out */
1449 pp->response_time = RESP_TIMEOUT;
1450 pp->statemach_act = pop3_statemach_act;
1451 pp->endofresp = pop3_endofresp;
1454 /* Set the default preferred authentication type and mechanism */
1455 pop3c->preftype = POP3_TYPE_ANY;
1456 pop3c->prefmech = SASL_AUTH_ANY;
1458 /* Initialise the pingpong layer */
1461 /* Parse the URL options */
1462 result = pop3_parse_url_options(conn);
1466 /* Start off waiting for the server greeting response */
1467 state(conn, POP3_SERVERGREET);
1469 result = pop3_multi_statemach(conn, done);
1474 /***********************************************************************
1478 * The DONE function. This does what needs to be done after a single DO has
1481 * Input argument is already checked for validity.
1483 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1486 CURLcode result = CURLE_OK;
1487 struct SessionHandle *data = conn->data;
1488 struct POP3 *pop3 = data->state.proto.pop3;
1493 /* When the easy handle is removed from the multi interface while libcurl
1494 is still trying to resolve the host name, the POP3 struct is not yet
1495 initialized. However, the removal action calls Curl_done() which in
1496 turn calls this function, so we simply return success. */
1500 conn->bits.close = TRUE; /* marked for closure */
1501 result = status; /* use the already set error code */
1504 /* Cleanup our per-request based variables */
1505 Curl_safefree(pop3->id);
1506 Curl_safefree(pop3->custom);
1508 /* Clear the transfer mode for the next request */
1509 pop3->transfer = FTPTRANSFER_BODY;
1514 /***********************************************************************
1518 * This is the actual DO function for POP3. Get a message/listing according to
1519 * the options previously setup.
1521 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1524 /* This is POP3 and no proxy */
1525 CURLcode result = CURLE_OK;
1527 DEBUGF(infof(conn->data, "DO phase starts\n"));
1529 if(conn->data->set.opt_no_body) {
1530 /* Requested no body means no transfer */
1531 struct POP3 *pop3 = conn->data->state.proto.pop3;
1532 pop3->transfer = FTPTRANSFER_INFO;
1535 *dophase_done = FALSE; /* not done yet */
1537 /* Start the first command in the DO phase */
1538 result = pop3_perform_command(conn);
1542 /* Run the state-machine */
1543 result = pop3_multi_statemach(conn, dophase_done);
1545 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1548 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1553 /***********************************************************************
1557 * This function is registered as 'curl_do' function. It decodes the path
1558 * parts etc as a wrapper to the actual DO function (pop3_perform).
1560 * The input argument is already checked for validity.
1562 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1564 CURLcode result = CURLE_OK;
1566 *done = FALSE; /* default to false */
1568 /* Since connections can be re-used between SessionHandles, there might be a
1569 connection already existing but on a fresh SessionHandle struct. As such
1570 we make sure we have a good POP3 struct to play with. For new connections
1571 the POP3 struct is allocated and setup in the pop3_connect() function. */
1572 Curl_reset_reqproto(conn);
1573 result = pop3_init(conn);
1577 /* Parse the URL path */
1578 result = pop3_parse_url_path(conn);
1582 /* Parse the custom request */
1583 result = pop3_parse_custom_request(conn);
1587 result = pop3_regular_transfer(conn, done);
1592 /***********************************************************************
1596 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1597 * resources. BLOCKING.
1599 static CURLcode pop3_disconnect(struct connectdata *conn,
1600 bool dead_connection)
1602 struct pop3_conn *pop3c = &conn->proto.pop3c;
1604 /* We cannot send quit unconditionally. If this connection is stale or
1605 bad in any way, sending quit and waiting around here will make the
1606 disconnect wait in vain and cause more problems than we need to. */
1608 /* The POP3 session may or may not have been allocated/setup at this
1610 if(!dead_connection && pop3c->pp.conn)
1611 if(!pop3_perform_quit(conn))
1612 (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1614 /* Disconnect from the server */
1615 Curl_pp_disconnect(&pop3c->pp);
1617 /* Cleanup the SASL module */
1618 Curl_sasl_cleanup(conn, pop3c->authused);
1620 /* Cleanup our connection based variables */
1621 Curl_safefree(pop3c->apoptimestamp);
1626 /* Call this when the DO phase has completed */
1627 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1635 /* Called from multi.c while DOing */
1636 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1638 CURLcode result = pop3_multi_statemach(conn, dophase_done);
1641 DEBUGF(infof(conn->data, "DO phase failed\n"));
1642 else if(*dophase_done) {
1643 result = pop3_dophase_done(conn, FALSE /* not connected */);
1645 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1651 /***********************************************************************
1653 * pop3_regular_transfer()
1655 * The input argument is already checked for validity.
1657 * Performs all commands done before a regular transfer between a local and a
1660 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1663 CURLcode result = CURLE_OK;
1664 bool connected = FALSE;
1665 struct SessionHandle *data = conn->data;
1667 /* Make sure size is unknown at this point */
1668 data->req.size = -1;
1670 /* Set the progress data */
1671 Curl_pgrsSetUploadCounter(data, 0);
1672 Curl_pgrsSetDownloadCounter(data, 0);
1673 Curl_pgrsSetUploadSize(data, 0);
1674 Curl_pgrsSetDownloadSize(data, 0);
1676 /* Carry out the perform */
1677 result = pop3_perform(conn, &connected, dophase_done);
1679 /* Perform post DO phase operations if necessary */
1680 if(!result && *dophase_done)
1681 result = pop3_dophase_done(conn, connected);
1686 static CURLcode pop3_setup_connection(struct connectdata *conn)
1688 struct SessionHandle *data = conn->data;
1690 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1691 /* Unless we have asked to tunnel POP3 operations through the proxy, we
1692 switch and use HTTP operations only */
1693 #ifndef CURL_DISABLE_HTTP
1694 if(conn->handler == &Curl_handler_pop3)
1695 conn->handler = &Curl_handler_pop3_proxy;
1698 conn->handler = &Curl_handler_pop3s_proxy;
1700 failf(data, "POP3S not supported!");
1701 return CURLE_UNSUPPORTED_PROTOCOL;
1705 /* We explicitly mark this connection as persistent here as we're doing
1706 POP3 over HTTP and thus we accidentally avoid setting this value
1708 conn->bits.close = FALSE;
1710 failf(data, "POP3 over http proxy requires HTTP support built-in!");
1711 return CURLE_UNSUPPORTED_PROTOCOL;
1715 data->state.path++; /* don't include the initial slash */
1720 /***********************************************************************
1722 * pop3_parse_url_options()
1724 * Parse the URL login options.
1726 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1728 CURLcode result = CURLE_OK;
1729 struct pop3_conn *pop3c = &conn->proto.pop3c;
1730 const char *options = conn->options;
1731 const char *ptr = options;
1734 const char *key = ptr;
1736 while(*ptr && *ptr != '=')
1739 if(strnequal(key, "AUTH", 4)) {
1740 const char *value = ptr + 1;
1742 if(strequal(value, "*")) {
1743 pop3c->preftype = POP3_TYPE_ANY;
1744 pop3c->prefmech = SASL_AUTH_ANY;
1746 else if(strequal(value, "+APOP")) {
1747 pop3c->preftype = POP3_TYPE_APOP;
1748 pop3c->prefmech = SASL_AUTH_NONE;
1750 else if(strequal(value, "LOGIN")) {
1751 pop3c->preftype = POP3_TYPE_SASL;
1752 pop3c->prefmech = SASL_MECH_LOGIN;
1754 else if(strequal(value, "PLAIN")) {
1755 pop3c->preftype = POP3_TYPE_SASL;
1756 pop3c->prefmech = SASL_MECH_PLAIN;
1758 else if(strequal(value, "CRAM-MD5")) {
1759 pop3c->preftype = POP3_TYPE_SASL;
1760 pop3c->prefmech = SASL_MECH_CRAM_MD5;
1762 else if(strequal(value, "DIGEST-MD5")) {
1763 pop3c->preftype = POP3_TYPE_SASL;
1764 pop3c->prefmech = SASL_MECH_DIGEST_MD5;
1766 else if(strequal(value, "GSSAPI")) {
1767 pop3c->preftype = POP3_TYPE_SASL;
1768 pop3c->prefmech = SASL_MECH_GSSAPI;
1770 else if(strequal(value, "NTLM")) {
1771 pop3c->preftype = POP3_TYPE_SASL;
1772 pop3c->prefmech = SASL_MECH_NTLM;
1775 pop3c->preftype = POP3_TYPE_NONE;
1776 pop3c->prefmech = SASL_AUTH_NONE;
1780 result = CURLE_URL_MALFORMAT;
1786 /***********************************************************************
1788 * pop3_parse_url_path()
1790 * Parse the URL path into separate path components.
1792 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1794 /* The POP3 struct is already initialised in pop3_connect() */
1795 struct SessionHandle *data = conn->data;
1796 struct POP3 *pop3 = data->state.proto.pop3;
1797 const char *path = data->state.path;
1799 /* URL decode the path for the message ID */
1800 return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1803 /***********************************************************************
1805 * pop3_parse_custom_request()
1807 * Parse the custom request.
1809 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1811 CURLcode result = CURLE_OK;
1812 struct SessionHandle *data = conn->data;
1813 struct POP3 *pop3 = data->state.proto.pop3;
1814 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1816 /* URL decode the custom request */
1818 result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1823 /***********************************************************************
1827 * This function scans the body after the end-of-body and writes everything
1828 * until the end is found.
1830 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1832 /* This code could be made into a special function in the handler struct */
1833 CURLcode result = CURLE_OK;
1834 struct SessionHandle *data = conn->data;
1835 struct SingleRequest *k = &data->req;
1837 struct pop3_conn *pop3c = &conn->proto.pop3c;
1838 bool strip_dot = FALSE;
1842 /* Search through the buffer looking for the end-of-body marker which is
1843 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1844 the eob so the server will have prefixed it with an extra dot which we
1845 need to strip out. Additionally the marker could of course be spread out
1846 over 5 different data chunks. */
1847 for(i = 0; i < nread; i++) {
1848 size_t prev = pop3c->eob;
1852 if(pop3c->eob == 0) {
1856 /* Write out the body part that didn't match */
1857 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1866 else if(pop3c->eob == 3)
1869 /* If the character match wasn't at position 0 or 3 then restart the
1875 if(pop3c->eob == 1 || pop3c->eob == 4)
1878 /* If the character match wasn't at position 1 or 4 then start the
1886 else if(pop3c->eob == 3) {
1887 /* We have an extra dot after the CRLF which we need to strip off */
1892 /* If the character match wasn't at position 2 then start the search
1902 /* Did we have a partial match which has subsequently failed? */
1903 if(prev && prev >= pop3c->eob) {
1904 /* Strip can only be non-zero for the very first mismatch after CRLF
1905 and then both prev and strip are equal and nothing will be output
1907 while(prev && pop3c->strip) {
1913 /* If the partial match was the CRLF and dot then only write the CRLF
1914 as the server would have inserted the dot */
1915 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
1916 strip_dot ? prev - 1 : prev);
1927 if(pop3c->eob == POP3_EOB_LEN) {
1928 /* We have a full match so the transfer is done, however we must transfer
1929 the CRLF at the start of the EOB as this is considered to be part of the
1930 message as per RFC-1939, sect. 3 */
1931 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1933 k->keepon &= ~KEEP_RECV;
1940 /* While EOB is matching nothing should be output */
1944 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1951 #endif /* CURL_DISABLE_POP3 */