1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, 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 https://curl.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 * 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>
36 ***************************************************************************/
38 #include "curl_setup.h"
40 #ifndef CURL_DISABLE_POP3
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
45 #ifdef HAVE_ARPA_INET_H
46 #include <arpa/inet.h>
49 #include <sys/utsname.h>
59 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
61 #define in_addr_t unsigned long
64 #include <curl/curl.h>
71 #include "http.h" /* for HTTP proxy tunnel stuff */
74 #include "strtoofft.h"
76 #include "vtls/vtls.h"
82 #include "curl_sasl.h"
85 /* The last 3 #include files should be in this order */
86 #include "curl_printf.h"
87 #include "curl_memory.h"
90 /* Local API functions */
91 static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done);
92 static CURLcode pop3_do(struct Curl_easy *data, bool *done);
93 static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
95 static CURLcode pop3_connect(struct Curl_easy *data, bool *done);
96 static CURLcode pop3_disconnect(struct Curl_easy *data,
97 struct connectdata *conn, bool dead);
98 static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done);
99 static int pop3_getsock(struct Curl_easy *data,
100 struct connectdata *conn, curl_socket_t *socks);
101 static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done);
102 static CURLcode pop3_setup_connection(struct Curl_easy *data,
103 struct connectdata *conn);
104 static CURLcode pop3_parse_url_options(struct connectdata *conn);
105 static CURLcode pop3_parse_url_path(struct Curl_easy *data);
106 static CURLcode pop3_parse_custom_request(struct Curl_easy *data);
107 static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech,
108 const struct bufref *initresp);
109 static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech,
110 const struct bufref *resp);
111 static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech);
112 static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out);
115 * POP3 protocol handler.
118 const struct Curl_handler Curl_handler_pop3 = {
120 pop3_setup_connection, /* setup_connection */
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 */
144 * POP3S protocol handler.
147 const struct Curl_handler Curl_handler_pop3s = {
148 "POP3S", /* scheme */
149 pop3_setup_connection, /* setup_connection */
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 */
172 /* SASL parameters for the pop3 protocol */
173 static 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 */
187 static void pop3_to_pop3s(struct connectdata *conn)
189 /* Change the connection handler */
190 conn->handler = &Curl_handler_pop3s;
192 /* Set the connection's upgraded to TLS flag */
193 conn->bits.tls_upgraded = TRUE;
196 #define pop3_to_pop3s(x) Curl_nop_stmt
199 /***********************************************************************
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.
208 static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn,
209 char *line, size_t len, int *resp)
211 struct pop3_conn *pop3c = &conn->proto.pop3c;
214 /* Do we have an error response? */
215 if(len >= 4 && !memcmp("-ERR", line, 4)) {
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 */
228 /* Treat the response as an untagged continuation */
234 /* Do we have a success response? */
235 if(len >= 3 && !memcmp("+OK", line, 3)) {
241 /* Do we have a continuation response? */
242 if(len >= 1 && line[0] == '+') {
248 return FALSE; /* Nothing for us */
251 /***********************************************************************
255 * Gets the authentication message from the response buffer.
257 static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
259 char *message = data->state.buffer;
260 size_t len = strlen(message);
263 /* Find the start of the message */
265 for(message += 2; *message == ' ' || *message == '\t'; message++, len--)
268 /* Find the end of the message */
270 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
271 message[len] != '\t')
274 /* Terminate the message */
275 message[++len] = '\0';
276 Curl_bufref_set(out, message, len, NULL);
279 /* junk input => zero length output */
280 Curl_bufref_set(out, "", 0, NULL);
285 /***********************************************************************
289 * This is the ONLY way to change POP3 state!
291 static void state(struct Curl_easy *data, pop3state newstate)
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[] = {
311 if(pop3c->state != newstate)
312 infof(data, "POP3 %p state change from %s to %s",
313 (void *)pop3c, names[pop3c->state], names[newstate]);
316 pop3c->state = newstate;
319 /***********************************************************************
321 * pop3_perform_capa()
323 * Sends the CAPA command in order to obtain a list of server side supported
326 static CURLcode pop3_perform_capa(struct Curl_easy *data,
327 struct connectdata *conn)
329 CURLcode result = CURLE_OK;
330 struct pop3_conn *pop3c = &conn->proto.pop3c;
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 */
336 /* Send the CAPA command */
337 result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA");
340 state(data, POP3_CAPA);
345 /***********************************************************************
347 * pop3_perform_starttls()
349 * Sends the STLS command to start the upgrade to TLS.
351 static CURLcode pop3_perform_starttls(struct Curl_easy *data,
352 struct connectdata *conn)
354 /* Send the STLS command */
355 CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS");
358 state(data, POP3_STARTTLS);
363 /***********************************************************************
365 * pop3_perform_upgrade_tls()
367 * Performs the upgrade to TLS.
369 static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
370 struct connectdata *conn)
372 /* Start the SSL connection */
373 struct pop3_conn *pop3c = &conn->proto.pop3c;
375 Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET,
379 if(pop3c->state != POP3_UPGRADETLS)
380 state(data, POP3_UPGRADETLS);
384 result = pop3_perform_capa(data, conn);
391 /***********************************************************************
393 * pop3_perform_user()
395 * Sends a clear text USER command to authenticate with.
397 static CURLcode pop3_perform_user(struct Curl_easy *data,
398 struct connectdata *conn)
400 CURLcode result = CURLE_OK;
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);
410 /* Send the USER command */
411 result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s",
412 conn->user ? conn->user : "");
414 state(data, POP3_USER);
419 #ifndef CURL_DISABLE_CRYPTO_AUTH
420 /***********************************************************************
422 * pop3_perform_apop()
424 * Sends an APOP command to authenticate with.
426 static CURLcode pop3_perform_apop(struct Curl_easy *data,
427 struct connectdata *conn)
429 CURLcode result = CURLE_OK;
430 struct pop3_conn *pop3c = &conn->proto.pop3c;
432 struct MD5_context *ctxt;
433 unsigned char digest[MD5_DIGEST_LEN];
434 char secret[2 * MD5_DIGEST_LEN + 1];
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);
444 /* Create the digest */
445 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
447 return CURLE_OUT_OF_MEMORY;
449 Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
450 curlx_uztoui(strlen(pop3c->apoptimestamp)));
452 Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
453 curlx_uztoui(strlen(conn->passwd)));
455 /* Finalise the digest */
456 Curl_MD5_final(ctxt, digest);
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]);
462 result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret);
465 state(data, POP3_APOP);
471 /***********************************************************************
473 * pop3_perform_auth()
475 * Sends an AUTH command allowing the client to login with the given SASL
476 * authentication mechanism.
478 static CURLcode pop3_perform_auth(struct Curl_easy *data,
480 const struct bufref *initresp)
482 CURLcode result = CURLE_OK;
483 struct pop3_conn *pop3c = &data->conn->proto.pop3c;
484 const char *ir = (const char *) Curl_bufref_ptr(initresp);
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);
491 /* Send the AUTH command */
492 result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s", mech);
498 /***********************************************************************
500 * pop3_continue_auth()
502 * Sends SASL continuation data.
504 static CURLcode pop3_continue_auth(struct Curl_easy *data,
506 const struct bufref *resp)
508 struct pop3_conn *pop3c = &data->conn->proto.pop3c;
512 return Curl_pp_sendf(data, &pop3c->pp,
513 "%s", (const char *) Curl_bufref_ptr(resp));
516 /***********************************************************************
520 * Sends SASL cancellation.
522 static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech)
524 struct pop3_conn *pop3c = &data->conn->proto.pop3c;
528 return Curl_pp_sendf(data, &pop3c->pp, "*");
531 /***********************************************************************
533 * pop3_perform_authentication()
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.
539 static CURLcode pop3_perform_authentication(struct Curl_easy *data,
540 struct connectdata *conn)
542 CURLcode result = CURLE_OK;
543 struct pop3_conn *pop3c = &conn->proto.pop3c;
544 saslprogress progress = SASL_IDLE;
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);
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);
558 if(progress == SASL_INPROGRESS)
559 state(data, POP3_AUTH);
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);
569 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
570 /* Perform clear text authentication */
571 result = pop3_perform_user(data, conn);
573 /* Other mechanisms not supported */
574 infof(data, "No known authentication mechanisms supported");
575 result = CURLE_LOGIN_DENIED;
582 /***********************************************************************
584 * pop3_perform_command()
586 * Sends a POP3 based command.
588 static CURLcode pop3_perform_command(struct Curl_easy *data)
590 CURLcode result = CURLE_OK;
591 struct connectdata *conn = data->conn;
592 struct POP3 *pop3 = data->req.p.pop3;
593 const char *command = NULL;
595 /* Calculate the default command */
596 if(pop3->id[0] == '\0' || data->set.list_only) {
599 if(pop3->id[0] != '\0')
600 /* Message specific LIST so skip the BODY transfer */
601 pop3->transfer = PPTRANSFER_INFO;
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);
612 result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s",
613 (pop3->custom && pop3->custom[0] != '\0' ?
614 pop3->custom : command));
617 state(data, POP3_COMMAND);
622 /***********************************************************************
624 * pop3_perform_quit()
626 * Performs the quit action prior to sclose() be called.
628 static CURLcode pop3_perform_quit(struct Curl_easy *data,
629 struct connectdata *conn)
631 /* Send the QUIT command */
632 CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT");
635 state(data, POP3_QUIT);
640 /* For the initial server greeting */
641 static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data,
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);
651 (void)instate; /* no use for this yet */
653 if(pop3code != '+') {
654 failf(data, "Got unexpected pop3-server response");
655 result = CURLE_WEIRD_SERVER_REPLY;
658 /* Does the server support APOP authentication? */
659 if(len >= 4 && line[len - 2] == '>') {
660 /* Look for the APOP timestamp */
662 for(i = 3; i < len - 2; ++i) {
664 /* Calculate the length of the timestamp */
665 size_t timestamplen = len - 1 - i;
670 /* Allocate some memory for the timestamp */
671 pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
673 if(!pop3c->apoptimestamp)
676 /* Copy the timestamp */
677 memcpy(pop3c->apoptimestamp, line + i, timestamplen);
678 pop3c->apoptimestamp[timestamplen] = '\0';
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, '@');
685 Curl_safefree(pop3c->apoptimestamp);
687 /* Store the APOP capability */
688 pop3c->authtypes |= POP3_TYPE_APOP;
694 result = pop3_perform_capa(data, conn);
700 /* For CAPA responses */
701 static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
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);
710 (void)instate; /* no use for this yet */
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;
718 /* Does the server support clear text authentication? */
719 else if(len >= 4 && !memcmp(line, "USER", 4))
720 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
722 /* Does the server support SASL based authentication? */
723 else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
724 pop3c->authtypes |= POP3_TYPE_SASL;
726 /* Advance past the SASL keyword */
730 /* Loop through the data line */
734 unsigned short mechbit;
737 (*line == ' ' || *line == '\t' ||
738 *line == '\r' || *line == '\n')) {
747 /* Extract the word */
748 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
749 line[wordlen] != '\t' && line[wordlen] != '\r' &&
750 line[wordlen] != '\n';)
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;
764 /* Clear text is supported when CAPA isn't recognised */
766 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
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);
777 failf(data, "STLS not supported.");
778 result = CURLE_USE_SSL_FAILED;
785 /* For STARTTLS responses */
786 static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
787 struct connectdata *conn,
791 CURLcode result = CURLE_OK;
792 (void)instate; /* no use for this yet */
794 /* Pipelining in response is forbidden. */
795 if(data->conn->proto.pop3c.pp.cache_size)
796 return CURLE_WEIRD_SERVER_REPLY;
798 if(pop3code != '+') {
799 if(data->set.use_ssl != CURLUSESSL_TRY) {
800 failf(data, "STARTTLS denied");
801 result = CURLE_USE_SSL_FAILED;
804 result = pop3_perform_authentication(data, conn);
807 result = pop3_perform_upgrade_tls(data, conn);
812 /* For SASL authentication responses */
813 static CURLcode pop3_state_auth_resp(struct Curl_easy *data,
817 CURLcode result = CURLE_OK;
818 struct connectdata *conn = data->conn;
819 struct pop3_conn *pop3c = &conn->proto.pop3c;
820 saslprogress progress;
822 (void)instate; /* no use for this yet */
824 result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress);
828 state(data, POP3_STOP); /* Authenticated */
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);
837 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
838 /* Perform clear text authentication */
839 result = pop3_perform_user(data, conn);
841 failf(data, "Authentication cancelled");
842 result = CURLE_LOGIN_DENIED;
852 #ifndef CURL_DISABLE_CRYPTO_AUTH
853 /* For APOP responses */
854 static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code,
857 CURLcode result = CURLE_OK;
858 (void)instate; /* no use for this yet */
860 if(pop3code != '+') {
861 failf(data, "Authentication failed: %d", pop3code);
862 result = CURLE_LOGIN_DENIED;
865 /* End of connect phase */
866 state(data, POP3_STOP);
872 /* For USER responses */
873 static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code,
876 CURLcode result = CURLE_OK;
877 struct connectdata *conn = data->conn;
878 (void)instate; /* no use for this yet */
880 if(pop3code != '+') {
881 failf(data, "Access denied. %c", pop3code);
882 result = CURLE_LOGIN_DENIED;
885 /* Send the PASS command */
886 result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s",
887 conn->passwd ? conn->passwd : "");
889 state(data, POP3_PASS);
894 /* For PASS responses */
895 static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code,
898 CURLcode result = CURLE_OK;
899 (void)instate; /* no use for this yet */
901 if(pop3code != '+') {
902 failf(data, "Access denied. %c", pop3code);
903 result = CURLE_LOGIN_DENIED;
906 /* End of connect phase */
907 state(data, POP3_STOP);
912 /* For command responses */
913 static CURLcode pop3_state_command_resp(struct Curl_easy *data,
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;
923 (void)instate; /* no use for this yet */
925 if(pop3code != '+') {
926 state(data, POP3_STOP);
927 return CURLE_WEIRD_SERVER_REPLY;
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. */
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. */
940 if(pop3->transfer == PPTRANSFER_BODY) {
942 Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
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 */
949 if(!data->set.opt_no_body) {
950 result = Curl_pop3_write(data, pp->cache, pp->cache_size);
956 Curl_safefree(pp->cache);
958 /* Reset the cache size */
963 /* End of DO phase */
964 state(data, POP3_STOP);
969 static CURLcode pop3_statemachine(struct Curl_easy *data,
970 struct connectdata *conn)
972 CURLcode result = CURLE_OK;
973 curl_socket_t sock = conn->sock[FIRSTSOCKET];
975 struct pop3_conn *pop3c = &conn->proto.pop3c;
976 struct pingpong *pp = &pop3c->pp;
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);
984 /* Flush any data that needs to be sent */
986 return Curl_pp_flushsend(data, pp);
989 /* Read the response from the server */
990 result = Curl_pp_readresp(data, sock, pp, &pop3code, &nread);
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);
1004 result = pop3_state_capa_resp(data, pop3code, pop3c->state);
1008 result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state);
1012 result = pop3_state_auth_resp(data, pop3code, pop3c->state);
1015 #ifndef CURL_DISABLE_CRYPTO_AUTH
1017 result = pop3_state_apop_resp(data, pop3code, pop3c->state);
1022 result = pop3_state_user_resp(data, pop3code, pop3c->state);
1026 result = pop3_state_pass_resp(data, pop3code, pop3c->state);
1030 result = pop3_state_command_resp(data, pop3code, pop3c->state);
1034 state(data, POP3_STOP);
1038 /* internal error */
1039 state(data, POP3_STOP);
1042 } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1047 /* Called repeatedly until done from multi.c */
1048 static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
1050 CURLcode result = CURLE_OK;
1051 struct connectdata *conn = data->conn;
1052 struct pop3_conn *pop3c = &conn->proto.pop3c;
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)
1061 result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE);
1062 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1067 static CURLcode pop3_block_statemach(struct Curl_easy *data,
1068 struct connectdata *conn,
1071 CURLcode result = CURLE_OK;
1072 struct pop3_conn *pop3c = &conn->proto.pop3c;
1074 while(pop3c->state != POP3_STOP && !result)
1075 result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting);
1080 /* Allocate and initialize the POP3 struct for the current Curl_easy if
1082 static CURLcode pop3_init(struct Curl_easy *data)
1084 CURLcode result = CURLE_OK;
1087 pop3 = data->req.p.pop3 = calloc(sizeof(struct POP3), 1);
1089 result = CURLE_OUT_OF_MEMORY;
1094 /* For the POP3 "protocol connect" and "doing" phases only */
1095 static int pop3_getsock(struct Curl_easy *data,
1096 struct connectdata *conn, curl_socket_t *socks)
1098 return Curl_pp_getsock(data, &conn->proto.pop3c.pp, socks);
1101 /***********************************************************************
1105 * This function should do everything that is to be considered a part of the
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.
1111 static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
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;
1118 *done = FALSE; /* default to not done yet */
1120 /* We always support persistent connections in POP3 */
1121 connkeep(conn, "POP3 default");
1123 PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp);
1125 /* Set the default preferred authentication type and mechanism */
1126 pop3c->preftype = POP3_TYPE_ANY;
1127 Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
1129 /* Initialise the pingpong layer */
1131 Curl_pp_init(data, pp);
1133 /* Parse the URL options */
1134 result = pop3_parse_url_options(conn);
1138 /* Start off waiting for the server greeting response */
1139 state(data, POP3_SERVERGREET);
1141 result = pop3_multi_statemach(data, done);
1146 /***********************************************************************
1150 * The DONE function. This does what needs to be done after a single DO has
1153 * Input argument is already checked for validity.
1155 static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
1158 CURLcode result = CURLE_OK;
1159 struct POP3 *pop3 = data->req.p.pop3;
1167 connclose(data->conn, "POP3 done with bad status");
1168 result = status; /* use the already set error code */
1171 /* Cleanup our per-request based variables */
1172 Curl_safefree(pop3->id);
1173 Curl_safefree(pop3->custom);
1175 /* Clear the transfer mode for the next request */
1176 pop3->transfer = PPTRANSFER_BODY;
1181 /***********************************************************************
1185 * This is the actual DO function for POP3. Get a message/listing according to
1186 * the options previously setup.
1188 static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
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;
1196 DEBUGF(infof(data, "DO phase starts"));
1198 if(data->set.opt_no_body) {
1199 /* Requested no body means no transfer */
1200 pop3->transfer = PPTRANSFER_INFO;
1203 *dophase_done = FALSE; /* not done yet */
1205 /* Start the first command in the DO phase */
1206 result = pop3_perform_command(data);
1210 /* Run the state-machine */
1211 result = pop3_multi_statemach(data, dophase_done);
1212 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1215 DEBUGF(infof(data, "DO phase is complete"));
1220 /***********************************************************************
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).
1227 * The input argument is already checked for validity.
1229 static CURLcode pop3_do(struct Curl_easy *data, bool *done)
1231 CURLcode result = CURLE_OK;
1232 *done = FALSE; /* default to false */
1234 /* Parse the URL path */
1235 result = pop3_parse_url_path(data);
1239 /* Parse the custom request */
1240 result = pop3_parse_custom_request(data);
1244 result = pop3_regular_transfer(data, done);
1249 /***********************************************************************
1253 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1254 * resources. BLOCKING.
1256 static CURLcode pop3_disconnect(struct Curl_easy *data,
1257 struct connectdata *conn, bool dead_connection)
1259 struct pop3_conn *pop3c = &conn->proto.pop3c;
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. */
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 */
1271 /* Disconnect from the server */
1272 Curl_pp_disconnect(&pop3c->pp);
1274 /* Cleanup the SASL module */
1275 Curl_sasl_cleanup(conn, pop3c->sasl.authused);
1277 /* Cleanup our connection based variables */
1278 Curl_safefree(pop3c->apoptimestamp);
1283 /* Call this when the DO phase has completed */
1284 static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected)
1292 /* Called from multi.c while DOing */
1293 static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done)
1295 CURLcode result = pop3_multi_statemach(data, dophase_done);
1298 DEBUGF(infof(data, "DO phase failed"));
1299 else if(*dophase_done) {
1300 result = pop3_dophase_done(data, FALSE /* not connected */);
1302 DEBUGF(infof(data, "DO phase is complete"));
1308 /***********************************************************************
1310 * pop3_regular_transfer()
1312 * The input argument is already checked for validity.
1314 * Performs all commands done before a regular transfer between a local and a
1317 static CURLcode pop3_regular_transfer(struct Curl_easy *data,
1320 CURLcode result = CURLE_OK;
1321 bool connected = FALSE;
1323 /* Make sure size is unknown at this point */
1324 data->req.size = -1;
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);
1332 /* Carry out the perform */
1333 result = pop3_perform(data, &connected, dophase_done);
1335 /* Perform post DO phase operations if necessary */
1336 if(!result && *dophase_done)
1337 result = pop3_dophase_done(data, connected);
1342 static CURLcode pop3_setup_connection(struct Curl_easy *data,
1343 struct connectdata *conn)
1345 /* Initialise the POP3 layer */
1346 CURLcode result = pop3_init(data);
1350 /* Clear the TLS upgraded flag */
1351 conn->bits.tls_upgraded = FALSE;
1356 /***********************************************************************
1358 * pop3_parse_url_options()
1360 * Parse the URL login options.
1362 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1364 CURLcode result = CURLE_OK;
1365 struct pop3_conn *pop3c = &conn->proto.pop3c;
1366 const char *ptr = conn->options;
1368 while(!result && ptr && *ptr) {
1369 const char *key = ptr;
1372 while(*ptr && *ptr != '=')
1377 while(*ptr && *ptr != ';')
1380 if(strncasecompare(key, "AUTH=", 5)) {
1381 result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
1382 value, ptr - value);
1384 if(result && strncasecompare(value, "+APOP", ptr - value)) {
1385 pop3c->preftype = POP3_TYPE_APOP;
1386 pop3c->sasl.prefmech = SASL_AUTH_NONE;
1391 result = CURLE_URL_MALFORMAT;
1397 if(pop3c->preftype != POP3_TYPE_APOP)
1398 switch(pop3c->sasl.prefmech) {
1399 case SASL_AUTH_NONE:
1400 pop3c->preftype = POP3_TYPE_NONE;
1402 case SASL_AUTH_DEFAULT:
1403 pop3c->preftype = POP3_TYPE_ANY;
1406 pop3c->preftype = POP3_TYPE_SASL;
1413 /***********************************************************************
1415 * pop3_parse_url_path()
1417 * Parse the URL path into separate path components.
1419 static CURLcode pop3_parse_url_path(struct Curl_easy *data)
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 */
1425 /* URL decode the path for the message ID */
1426 return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL);
1429 /***********************************************************************
1431 * pop3_parse_custom_request()
1433 * Parse the custom request.
1435 static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
1437 CURLcode result = CURLE_OK;
1438 struct POP3 *pop3 = data->req.p.pop3;
1439 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1441 /* URL decode the custom request */
1443 result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL);
1448 /***********************************************************************
1452 * This function scans the body after the end-of-body and writes everything
1453 * until the end is found.
1455 CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread)
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;
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;
1476 if(pop3c->eob == 0) {
1480 /* Write out the body part that didn't match */
1481 result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
1490 else if(pop3c->eob == 3)
1493 /* If the character match wasn't at position 0 or 3 then restart the
1499 if(pop3c->eob == 1 || pop3c->eob == 4)
1502 /* If the character match wasn't at position 1 or 4 then start the
1510 else if(pop3c->eob == 3) {
1511 /* We have an extra dot after the CRLF which we need to strip off */
1516 /* If the character match wasn't at position 2 then start the search
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
1531 while(prev && pop3c->strip) {
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,
1543 else if(!strip_dot) {
1544 result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
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);
1566 k->keepon &= ~KEEP_RECV;
1573 /* While EOB is matching nothing should be output */
1577 result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
1584 #endif /* CURL_DISABLE_POP3 */