1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, 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.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 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
31 * RFC5034 POP3 SASL Authentication Mechanism
32 * RFC6749 OAuth 2.0 Authorization Framework
33 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
35 ***************************************************************************/
37 #include "curl_setup.h"
39 #ifndef CURL_DISABLE_POP3
41 #ifdef HAVE_NETINET_IN_H
42 #include <netinet/in.h>
44 #ifdef HAVE_ARPA_INET_H
45 #include <arpa/inet.h>
48 #include <sys/utsname.h>
58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
60 #define in_addr_t unsigned long
63 #include <curl/curl.h>
70 #include "http.h" /* for HTTP proxy tunnel stuff */
73 #include "strtoofft.h"
75 #include "vtls/vtls.h"
81 #include "curl_sasl.h"
84 /* The last 3 #include files should be in this order */
85 #include "curl_printf.h"
86 #include "curl_memory.h"
89 /* Local API functions */
90 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
91 static CURLcode pop3_do(struct connectdata *conn, bool *done);
92 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
94 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
95 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
96 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
97 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
99 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode pop3_setup_connection(struct connectdata *conn);
101 static CURLcode pop3_parse_url_options(struct connectdata *conn);
102 static CURLcode pop3_parse_url_path(struct connectdata *conn);
103 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
104 static CURLcode pop3_perform_auth(struct connectdata *conn, const char *mech,
105 const char *initresp);
106 static CURLcode pop3_continue_auth(struct connectdata *conn, const char *resp);
107 static void pop3_get_message(char *buffer, char **outptr);
110 * POP3 protocol handler.
113 const struct Curl_handler Curl_handler_pop3 = {
115 pop3_setup_connection, /* setup_connection */
117 pop3_done, /* done */
118 ZERO_NULL, /* do_more */
119 pop3_connect, /* connect_it */
120 pop3_multi_statemach, /* connecting */
121 pop3_doing, /* doing */
122 pop3_getsock, /* proto_getsock */
123 pop3_getsock, /* doing_getsock */
124 ZERO_NULL, /* domore_getsock */
125 ZERO_NULL, /* perform_getsock */
126 pop3_disconnect, /* disconnect */
127 ZERO_NULL, /* readwrite */
128 PORT_POP3, /* defport */
129 CURLPROTO_POP3, /* protocol */
130 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
136 * POP3S protocol handler.
139 const struct Curl_handler Curl_handler_pop3s = {
140 "POP3S", /* scheme */
141 pop3_setup_connection, /* setup_connection */
143 pop3_done, /* done */
144 ZERO_NULL, /* do_more */
145 pop3_connect, /* connect_it */
146 pop3_multi_statemach, /* connecting */
147 pop3_doing, /* doing */
148 pop3_getsock, /* proto_getsock */
149 pop3_getsock, /* doing_getsock */
150 ZERO_NULL, /* domore_getsock */
151 ZERO_NULL, /* perform_getsock */
152 pop3_disconnect, /* disconnect */
153 ZERO_NULL, /* readwrite */
154 PORT_POP3S, /* defport */
155 CURLPROTO_POP3S, /* protocol */
156 PROTOPT_CLOSEACTION | PROTOPT_SSL
157 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
161 #ifndef CURL_DISABLE_HTTP
163 * HTTP-proxyed POP3 protocol handler.
166 static const struct Curl_handler Curl_handler_pop3_proxy = {
168 Curl_http_setup_conn, /* setup_connection */
169 Curl_http, /* do_it */
170 Curl_http_done, /* done */
171 ZERO_NULL, /* do_more */
172 ZERO_NULL, /* connect_it */
173 ZERO_NULL, /* connecting */
174 ZERO_NULL, /* doing */
175 ZERO_NULL, /* proto_getsock */
176 ZERO_NULL, /* doing_getsock */
177 ZERO_NULL, /* domore_getsock */
178 ZERO_NULL, /* perform_getsock */
179 ZERO_NULL, /* disconnect */
180 ZERO_NULL, /* readwrite */
181 PORT_POP3, /* defport */
182 CURLPROTO_HTTP, /* protocol */
183 PROTOPT_NONE /* flags */
188 * HTTP-proxyed POP3S protocol handler.
191 static const struct Curl_handler Curl_handler_pop3s_proxy = {
192 "POP3S", /* scheme */
193 Curl_http_setup_conn, /* setup_connection */
194 Curl_http, /* do_it */
195 Curl_http_done, /* done */
196 ZERO_NULL, /* do_more */
197 ZERO_NULL, /* connect_it */
198 ZERO_NULL, /* connecting */
199 ZERO_NULL, /* doing */
200 ZERO_NULL, /* proto_getsock */
201 ZERO_NULL, /* doing_getsock */
202 ZERO_NULL, /* domore_getsock */
203 ZERO_NULL, /* perform_getsock */
204 ZERO_NULL, /* disconnect */
205 ZERO_NULL, /* readwrite */
206 PORT_POP3S, /* defport */
207 CURLPROTO_HTTP, /* protocol */
208 PROTOPT_NONE /* flags */
213 /* SASL parameters for the pop3 protocol */
214 static const struct SASLproto saslpop3 = {
215 "pop", /* The service name */
216 '*', /* Code received when continuation is expected */
217 '+', /* Code to receive upon authentication success */
218 255 - 8, /* Maximum initial response length (no max) */
219 pop3_perform_auth, /* Send authentication command */
220 pop3_continue_auth, /* Send authentication continuation */
221 pop3_get_message /* Get SASL response message */
225 static void pop3_to_pop3s(struct connectdata *conn)
227 /* Change the connection handler */
228 conn->handler = &Curl_handler_pop3s;
230 /* Set the connection's upgraded to TLS flag */
231 conn->tls_upgraded = TRUE;
234 #define pop3_to_pop3s(x) Curl_nop_stmt
237 /***********************************************************************
241 * Checks for an ending POP3 status code at the start of the given string, but
242 * also detects the APOP timestamp from the server greeting and various
243 * capabilities from the CAPA response including the supported authentication
244 * types and allowed SASL mechanisms.
246 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
249 struct pop3_conn *pop3c = &conn->proto.pop3c;
251 /* Do we have an error response? */
252 if(len >= 4 && !memcmp("-ERR", line, 4)) {
258 /* Are we processing CAPA command responses? */
259 if(pop3c->state == POP3_CAPA) {
260 /* Do we have the terminating line? */
261 if(len >= 1 && !memcmp(line, ".", 1))
262 /* Treat the response as a success */
265 /* Treat the response as an untagged continuation */
271 /* Do we have a success response? */
272 if(len >= 3 && !memcmp("+OK", line, 3)) {
278 /* Do we have a continuation response? */
279 if(len >= 1 && !memcmp("+", line, 1)) {
285 return FALSE; /* Nothing for us */
288 /***********************************************************************
292 * Gets the authentication message from the response buffer.
294 static void pop3_get_message(char *buffer, char **outptr)
297 char *message = NULL;
299 /* Find the start of the message */
300 for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
303 /* Find the end of the message */
304 for(len = strlen(message); len--;)
305 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
306 message[len] != '\t')
309 /* Terminate the message */
317 /***********************************************************************
321 * This is the ONLY way to change POP3 state!
323 static void state(struct connectdata *conn, pop3state newstate)
325 struct pop3_conn *pop3c = &conn->proto.pop3c;
326 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
327 /* for debug purposes */
328 static const char * const names[] = {
343 if(pop3c->state != newstate)
344 infof(conn->data, "POP3 %p state change from %s to %s\n",
345 (void *)pop3c, names[pop3c->state], names[newstate]);
348 pop3c->state = newstate;
351 /***********************************************************************
353 * pop3_perform_capa()
355 * Sends the CAPA command in order to obtain a list of server side supported
358 static CURLcode pop3_perform_capa(struct connectdata *conn)
360 CURLcode result = CURLE_OK;
361 struct pop3_conn *pop3c = &conn->proto.pop3c;
363 pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
364 pop3c->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
365 pop3c->tls_supported = FALSE; /* Clear the TLS capability */
367 /* Send the CAPA command */
368 result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA");
371 state(conn, POP3_CAPA);
376 /***********************************************************************
378 * pop3_perform_starttls()
380 * Sends the STLS command to start the upgrade to TLS.
382 static CURLcode pop3_perform_starttls(struct connectdata *conn)
384 CURLcode result = CURLE_OK;
386 /* Send the STLS command */
387 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS");
390 state(conn, POP3_STARTTLS);
395 /***********************************************************************
397 * pop3_perform_upgrade_tls()
399 * Performs the upgrade to TLS.
401 static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
403 CURLcode result = CURLE_OK;
404 struct pop3_conn *pop3c = &conn->proto.pop3c;
406 /* Start the SSL connection */
407 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
410 if(pop3c->state != POP3_UPGRADETLS)
411 state(conn, POP3_UPGRADETLS);
415 result = pop3_perform_capa(conn);
422 /***********************************************************************
424 * pop3_perform_user()
426 * Sends a clear text USER command to authenticate with.
428 static CURLcode pop3_perform_user(struct connectdata *conn)
430 CURLcode result = CURLE_OK;
432 /* Check we have a username and password to authenticate with and end the
433 connect phase if we don't */
434 if(!conn->bits.user_passwd) {
435 state(conn, POP3_STOP);
440 /* Send the USER command */
441 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
442 conn->user ? conn->user : "");
444 state(conn, POP3_USER);
449 #ifndef CURL_DISABLE_CRYPTO_AUTH
450 /***********************************************************************
452 * pop3_perform_apop()
454 * Sends an APOP command to authenticate with.
456 static CURLcode pop3_perform_apop(struct connectdata *conn)
458 CURLcode result = CURLE_OK;
459 struct pop3_conn *pop3c = &conn->proto.pop3c;
462 unsigned char digest[MD5_DIGEST_LEN];
463 char secret[2 * MD5_DIGEST_LEN + 1];
465 /* Check we have a username and password to authenticate with and end the
466 connect phase if we don't */
467 if(!conn->bits.user_passwd) {
468 state(conn, POP3_STOP);
473 /* Create the digest */
474 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
476 return CURLE_OUT_OF_MEMORY;
478 Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
479 curlx_uztoui(strlen(pop3c->apoptimestamp)));
481 Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
482 curlx_uztoui(strlen(conn->passwd)));
484 /* Finalise the digest */
485 Curl_MD5_final(ctxt, digest);
487 /* Convert the calculated 16 octet digest into a 32 byte hex string */
488 for(i = 0; i < MD5_DIGEST_LEN; i++)
489 snprintf(&secret[2 * i], 3, "%02x", digest[i]);
491 result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
494 state(conn, POP3_APOP);
500 /***********************************************************************
502 * pop3_perform_auth()
504 * Sends an AUTH command allowing the client to login with the given SASL
505 * authentication mechanism.
507 static CURLcode pop3_perform_auth(struct connectdata *conn,
509 const char *initresp)
511 CURLcode result = CURLE_OK;
512 struct pop3_conn *pop3c = &conn->proto.pop3c;
514 if(initresp) { /* AUTH <mech> ...<crlf> */
515 /* Send the AUTH command with the initial response */
516 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
519 /* Send the AUTH command */
520 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
526 /***********************************************************************
528 * pop3_continue_auth()
530 * Sends SASL continuation data or cancellation.
532 static CURLcode pop3_continue_auth(struct connectdata *conn,
535 struct pop3_conn *pop3c = &conn->proto.pop3c;
537 return Curl_pp_sendf(&pop3c->pp, "%s", resp);
540 /***********************************************************************
542 * pop3_perform_authentication()
544 * Initiates the authentication sequence, with the appropriate SASL
545 * authentication mechanism, falling back to APOP and clear text should a
546 * common mechanism not be available between the client and server.
548 static CURLcode pop3_perform_authentication(struct connectdata *conn)
550 CURLcode result = CURLE_OK;
551 struct pop3_conn *pop3c = &conn->proto.pop3c;
552 saslprogress progress = SASL_IDLE;
554 /* Check we have enough data to authenticate with and end the
555 connect phase if we don't */
556 if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) {
557 state(conn, POP3_STOP);
561 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
562 /* Calculate the SASL login details */
563 result = Curl_sasl_start(&pop3c->sasl, conn, FALSE, &progress);
566 if(progress == SASL_INPROGRESS)
567 state(conn, POP3_AUTH);
570 if(!result && progress == SASL_IDLE) {
571 #ifndef CURL_DISABLE_CRYPTO_AUTH
572 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
573 /* Perform APOP authentication */
574 result = pop3_perform_apop(conn);
577 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
578 /* Perform clear text authentication */
579 result = pop3_perform_user(conn);
581 /* Other mechanisms not supported */
582 infof(conn->data, "No known authentication mechanisms supported!\n");
583 result = CURLE_LOGIN_DENIED;
590 /***********************************************************************
592 * pop3_perform_command()
594 * Sends a POP3 based command.
596 static CURLcode pop3_perform_command(struct connectdata *conn)
598 CURLcode result = CURLE_OK;
599 struct Curl_easy *data = conn->data;
600 struct POP3 *pop3 = data->req.protop;
601 const char *command = NULL;
603 /* Calculate the default command */
604 if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
607 if(pop3->id[0] != '\0')
608 /* Message specific LIST so skip the BODY transfer */
609 pop3->transfer = FTPTRANSFER_INFO;
614 /* Send the command */
615 if(pop3->id[0] != '\0')
616 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
617 (pop3->custom && pop3->custom[0] != '\0' ?
618 pop3->custom : command), pop3->id);
620 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s",
621 (pop3->custom && pop3->custom[0] != '\0' ?
622 pop3->custom : command));
625 state(conn, POP3_COMMAND);
630 /***********************************************************************
632 * pop3_perform_quit()
634 * Performs the quit action prior to sclose() be called.
636 static CURLcode pop3_perform_quit(struct connectdata *conn)
638 CURLcode result = CURLE_OK;
640 /* Send the QUIT command */
641 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT");
644 state(conn, POP3_QUIT);
649 /* For the initial server greeting */
650 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
654 CURLcode result = CURLE_OK;
655 struct Curl_easy *data = conn->data;
656 struct pop3_conn *pop3c = &conn->proto.pop3c;
657 const char *line = data->state.buffer;
658 size_t len = strlen(line);
661 (void)instate; /* no use for this yet */
663 if(pop3code != '+') {
664 failf(data, "Got unexpected pop3-server response");
665 result = CURLE_WEIRD_SERVER_REPLY;
668 /* Does the server support APOP authentication? */
669 if(len >= 4 && line[len - 2] == '>') {
670 /* Look for the APOP timestamp */
671 for(i = 3; i < len - 2; ++i) {
673 /* Calculate the length of the timestamp */
674 size_t timestamplen = len - 1 - i;
678 /* Allocate some memory for the timestamp */
679 pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
681 if(!pop3c->apoptimestamp)
684 /* Copy the timestamp */
685 memcpy(pop3c->apoptimestamp, line + i, timestamplen);
686 pop3c->apoptimestamp[timestamplen] = '\0';
688 /* Store the APOP capability */
689 pop3c->authtypes |= POP3_TYPE_APOP;
695 result = pop3_perform_capa(conn);
701 /* For CAPA responses */
702 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
705 CURLcode result = CURLE_OK;
706 struct Curl_easy *data = conn->data;
707 struct pop3_conn *pop3c = &conn->proto.pop3c;
708 const char *line = data->state.buffer;
709 size_t len = strlen(line);
712 (void)instate; /* no use for this yet */
714 /* Do we have a untagged continuation response? */
715 if(pop3code == '*') {
716 /* Does the server support the STLS capability? */
717 if(len >= 4 && !memcmp(line, "STLS", 4))
718 pop3c->tls_supported = TRUE;
720 /* Does the server support clear text authentication? */
721 else if(len >= 4 && !memcmp(line, "USER", 4))
722 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
724 /* Does the server support SASL based authentication? */
725 else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
726 pop3c->authtypes |= POP3_TYPE_SASL;
728 /* Advance past the SASL keyword */
732 /* Loop through the data line */
735 unsigned int mechbit;
738 (*line == ' ' || *line == '\t' ||
739 *line == '\r' || *line == '\n')) {
748 /* Extract the word */
749 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
750 line[wordlen] != '\t' && line[wordlen] != '\r' &&
751 line[wordlen] != '\n';)
754 /* Test the word for a matching authentication mechanism */
755 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
756 if(mechbit && llen == wordlen)
757 pop3c->sasl.authmechs |= mechbit;
764 else if(pop3code == '+') {
765 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
766 /* We don't have a SSL/TLS connection yet, but SSL is requested */
767 if(pop3c->tls_supported)
768 /* Switch to TLS connection now */
769 result = pop3_perform_starttls(conn);
770 else if(data->set.use_ssl == CURLUSESSL_TRY)
771 /* Fallback and carry on with authentication */
772 result = pop3_perform_authentication(conn);
774 failf(data, "STLS not supported.");
775 result = CURLE_USE_SSL_FAILED;
779 result = pop3_perform_authentication(conn);
782 /* Clear text is supported when CAPA isn't recognised */
783 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
785 result = pop3_perform_authentication(conn);
791 /* For STARTTLS responses */
792 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
796 CURLcode result = CURLE_OK;
797 struct Curl_easy *data = conn->data;
799 (void)instate; /* no use for this yet */
801 if(pop3code != '+') {
802 if(data->set.use_ssl != CURLUSESSL_TRY) {
803 failf(data, "STARTTLS denied");
804 result = CURLE_USE_SSL_FAILED;
807 result = pop3_perform_authentication(conn);
810 result = pop3_perform_upgrade_tls(conn);
815 /* For SASL authentication responses */
816 static CURLcode pop3_state_auth_resp(struct connectdata *conn,
820 CURLcode result = CURLE_OK;
821 struct Curl_easy *data = conn->data;
822 struct pop3_conn *pop3c = &conn->proto.pop3c;
823 saslprogress progress;
825 (void)instate; /* no use for this yet */
827 result = Curl_sasl_continue(&pop3c->sasl, conn, pop3code, &progress);
831 state(conn, POP3_STOP); /* Authenticated */
833 case SASL_IDLE: /* No mechanism left after cancellation */
834 #ifndef CURL_DISABLE_CRYPTO_AUTH
835 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
836 /* Perform APOP authentication */
837 result = pop3_perform_apop(conn);
840 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
841 /* Perform clear text authentication */
842 result = pop3_perform_user(conn);
844 failf(data, "Authentication cancelled");
845 result = CURLE_LOGIN_DENIED;
855 #ifndef CURL_DISABLE_CRYPTO_AUTH
856 /* For APOP responses */
857 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
860 CURLcode result = CURLE_OK;
861 struct Curl_easy *data = conn->data;
863 (void)instate; /* no use for this yet */
865 if(pop3code != '+') {
866 failf(data, "Authentication failed: %d", pop3code);
867 result = CURLE_LOGIN_DENIED;
870 /* End of connect phase */
871 state(conn, POP3_STOP);
877 /* For USER responses */
878 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
881 CURLcode result = CURLE_OK;
882 struct Curl_easy *data = conn->data;
884 (void)instate; /* no use for this yet */
886 if(pop3code != '+') {
887 failf(data, "Access denied. %c", pop3code);
888 result = CURLE_LOGIN_DENIED;
891 /* Send the PASS command */
892 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
893 conn->passwd ? conn->passwd : "");
895 state(conn, POP3_PASS);
900 /* For PASS responses */
901 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
904 CURLcode result = CURLE_OK;
905 struct Curl_easy *data = conn->data;
907 (void)instate; /* no use for this yet */
909 if(pop3code != '+') {
910 failf(data, "Access denied. %c", pop3code);
911 result = CURLE_LOGIN_DENIED;
914 /* End of connect phase */
915 state(conn, POP3_STOP);
920 /* For command responses */
921 static CURLcode pop3_state_command_resp(struct connectdata *conn,
925 CURLcode result = CURLE_OK;
926 struct Curl_easy *data = conn->data;
927 struct POP3 *pop3 = data->req.protop;
928 struct pop3_conn *pop3c = &conn->proto.pop3c;
929 struct pingpong *pp = &pop3c->pp;
931 (void)instate; /* no use for this yet */
933 if(pop3code != '+') {
934 state(conn, POP3_STOP);
935 return CURLE_RECV_ERROR;
938 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
939 EOB string so count this is two matching bytes. This is necessary to make
940 the code detect the EOB if the only data than comes now is %2e CR LF like
941 when there is no body to return. */
944 /* But since this initial CR LF pair is not part of the actual body, we set
945 the strip counter here so that these bytes won't be delivered. */
948 if(pop3->transfer == FTPTRANSFER_BODY) {
950 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
953 /* The header "cache" contains a bunch of data that is actually body
954 content so send it as such. Note that there may even be additional
955 "headers" after the body */
957 if(!data->set.opt_no_body) {
958 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
964 Curl_safefree(pp->cache);
966 /* Reset the cache size */
971 /* End of DO phase */
972 state(conn, POP3_STOP);
977 static CURLcode pop3_statemach_act(struct connectdata *conn)
979 CURLcode result = CURLE_OK;
980 curl_socket_t sock = conn->sock[FIRSTSOCKET];
982 struct pop3_conn *pop3c = &conn->proto.pop3c;
983 struct pingpong *pp = &pop3c->pp;
986 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
987 if(pop3c->state == POP3_UPGRADETLS)
988 return pop3_perform_upgrade_tls(conn);
990 /* Flush any data that needs to be sent */
992 return Curl_pp_flushsend(pp);
995 /* Read the response from the server */
996 result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1003 /* We have now received a full POP3 server response */
1004 switch(pop3c->state) {
1005 case POP3_SERVERGREET:
1006 result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1010 result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1014 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1018 result = pop3_state_auth_resp(conn, pop3code, pop3c->state);
1021 #ifndef CURL_DISABLE_CRYPTO_AUTH
1023 result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1028 result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1032 result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1036 result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1040 /* fallthrough, just stop! */
1042 /* internal error */
1043 state(conn, POP3_STOP);
1046 } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1051 /* Called repeatedly until done from multi.c */
1052 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1054 CURLcode result = CURLE_OK;
1055 struct pop3_conn *pop3c = &conn->proto.pop3c;
1057 if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1058 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1059 if(result || !pop3c->ssldone)
1063 result = Curl_pp_statemach(&pop3c->pp, FALSE);
1064 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1069 static CURLcode pop3_block_statemach(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(&pop3c->pp, TRUE);
1080 /* Allocate and initialize the POP3 struct for the current Curl_easy if
1082 static CURLcode pop3_init(struct connectdata *conn)
1084 CURLcode result = CURLE_OK;
1085 struct Curl_easy *data = conn->data;
1088 pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
1090 result = CURLE_OUT_OF_MEMORY;
1095 /* For the POP3 "protocol connect" and "doing" phases only */
1096 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1099 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1102 /***********************************************************************
1106 * This function should do everything that is to be considered a part of the
1109 * The variable 'done' points to will be TRUE if the protocol-layer connect
1110 * phase is done when this function returns, or FALSE if not.
1112 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1114 CURLcode result = CURLE_OK;
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 /* Set the default response time-out */
1124 pp->response_time = RESP_TIMEOUT;
1125 pp->statemach_act = pop3_statemach_act;
1126 pp->endofresp = pop3_endofresp;
1129 /* Set the default preferred authentication type and mechanism */
1130 pop3c->preftype = POP3_TYPE_ANY;
1131 Curl_sasl_init(&pop3c->sasl, &saslpop3);
1133 /* Initialise the pingpong layer */
1136 /* Parse the URL options */
1137 result = pop3_parse_url_options(conn);
1141 /* Start off waiting for the server greeting response */
1142 state(conn, POP3_SERVERGREET);
1144 result = pop3_multi_statemach(conn, done);
1149 /***********************************************************************
1153 * The DONE function. This does what needs to be done after a single DO has
1156 * Input argument is already checked for validity.
1158 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1161 CURLcode result = CURLE_OK;
1162 struct Curl_easy *data = conn->data;
1163 struct POP3 *pop3 = data->req.protop;
1171 connclose(conn, "POP3 done with bad status");
1172 result = status; /* use the already set error code */
1175 /* Cleanup our per-request based variables */
1176 Curl_safefree(pop3->id);
1177 Curl_safefree(pop3->custom);
1179 /* Clear the transfer mode for the next request */
1180 pop3->transfer = FTPTRANSFER_BODY;
1185 /***********************************************************************
1189 * This is the actual DO function for POP3. Get a message/listing according to
1190 * the options previously setup.
1192 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1195 /* This is POP3 and no proxy */
1196 CURLcode result = CURLE_OK;
1197 struct POP3 *pop3 = conn->data->req.protop;
1199 DEBUGF(infof(conn->data, "DO phase starts\n"));
1201 if(conn->data->set.opt_no_body) {
1202 /* Requested no body means no transfer */
1203 pop3->transfer = FTPTRANSFER_INFO;
1206 *dophase_done = FALSE; /* not done yet */
1208 /* Start the first command in the DO phase */
1209 result = pop3_perform_command(conn);
1213 /* Run the state-machine */
1214 result = pop3_multi_statemach(conn, dophase_done);
1216 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1219 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1224 /***********************************************************************
1228 * This function is registered as 'curl_do' function. It decodes the path
1229 * parts etc as a wrapper to the actual DO function (pop3_perform).
1231 * The input argument is already checked for validity.
1233 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1235 CURLcode result = CURLE_OK;
1237 *done = FALSE; /* default to false */
1239 /* Parse the URL path */
1240 result = pop3_parse_url_path(conn);
1244 /* Parse the custom request */
1245 result = pop3_parse_custom_request(conn);
1249 result = pop3_regular_transfer(conn, done);
1254 /***********************************************************************
1258 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1259 * resources. BLOCKING.
1261 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
1263 struct pop3_conn *pop3c = &conn->proto.pop3c;
1265 /* We cannot send quit unconditionally. If this connection is stale or
1266 bad in any way, sending quit and waiting around here will make the
1267 disconnect wait in vain and cause more problems than we need to. */
1269 /* The POP3 session may or may not have been allocated/setup at this
1271 if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
1272 if(!pop3_perform_quit(conn))
1273 (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1275 /* Disconnect from the server */
1276 Curl_pp_disconnect(&pop3c->pp);
1278 /* Cleanup the SASL module */
1279 Curl_sasl_cleanup(conn, pop3c->sasl.authused);
1281 /* Cleanup our connection based variables */
1282 Curl_safefree(pop3c->apoptimestamp);
1287 /* Call this when the DO phase has completed */
1288 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1296 /* Called from multi.c while DOing */
1297 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1299 CURLcode result = pop3_multi_statemach(conn, dophase_done);
1302 DEBUGF(infof(conn->data, "DO phase failed\n"));
1303 else if(*dophase_done) {
1304 result = pop3_dophase_done(conn, FALSE /* not connected */);
1306 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1312 /***********************************************************************
1314 * pop3_regular_transfer()
1316 * The input argument is already checked for validity.
1318 * Performs all commands done before a regular transfer between a local and a
1321 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1324 CURLcode result = CURLE_OK;
1325 bool connected = FALSE;
1326 struct Curl_easy *data = conn->data;
1328 /* Make sure size is unknown at this point */
1329 data->req.size = -1;
1331 /* Set the progress data */
1332 Curl_pgrsSetUploadCounter(data, 0);
1333 Curl_pgrsSetDownloadCounter(data, 0);
1334 Curl_pgrsSetUploadSize(data, -1);
1335 Curl_pgrsSetDownloadSize(data, -1);
1337 /* Carry out the perform */
1338 result = pop3_perform(conn, &connected, dophase_done);
1340 /* Perform post DO phase operations if necessary */
1341 if(!result && *dophase_done)
1342 result = pop3_dophase_done(conn, connected);
1347 static CURLcode pop3_setup_connection(struct connectdata *conn)
1349 struct Curl_easy *data = conn->data;
1351 /* Initialise the POP3 layer */
1352 CURLcode result = pop3_init(conn);
1356 /* Clear the TLS upgraded flag */
1357 conn->tls_upgraded = FALSE;
1359 /* Set up the proxy if necessary */
1360 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1361 /* Unless we have asked to tunnel POP3 operations through the proxy, we
1362 switch and use HTTP operations only */
1363 #ifndef CURL_DISABLE_HTTP
1364 if(conn->handler == &Curl_handler_pop3)
1365 conn->handler = &Curl_handler_pop3_proxy;
1368 conn->handler = &Curl_handler_pop3s_proxy;
1370 failf(data, "POP3S not supported!");
1371 return CURLE_UNSUPPORTED_PROTOCOL;
1375 /* set it up as an HTTP connection instead */
1376 return conn->handler->setup_connection(conn);
1378 failf(data, "POP3 over http proxy requires HTTP support built-in!");
1379 return CURLE_UNSUPPORTED_PROTOCOL;
1383 data->state.path++; /* don't include the initial slash */
1388 /***********************************************************************
1390 * pop3_parse_url_options()
1392 * Parse the URL login options.
1394 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1396 CURLcode result = CURLE_OK;
1397 struct pop3_conn *pop3c = &conn->proto.pop3c;
1398 const char *ptr = conn->options;
1400 pop3c->sasl.resetprefs = TRUE;
1402 while(!result && ptr && *ptr) {
1403 const char *key = ptr;
1406 while(*ptr && *ptr != '=')
1411 while(*ptr && *ptr != ';')
1414 if(strncasecompare(key, "AUTH=", 5)) {
1415 result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
1416 value, ptr - value);
1418 if(result && strncasecompare(value, "+APOP", ptr - value)) {
1419 pop3c->preftype = POP3_TYPE_APOP;
1420 pop3c->sasl.prefmech = SASL_AUTH_NONE;
1425 result = CURLE_URL_MALFORMAT;
1431 if(pop3c->preftype != POP3_TYPE_APOP)
1432 switch(pop3c->sasl.prefmech) {
1433 case SASL_AUTH_NONE:
1434 pop3c->preftype = POP3_TYPE_NONE;
1436 case SASL_AUTH_DEFAULT:
1437 pop3c->preftype = POP3_TYPE_ANY;
1440 pop3c->preftype = POP3_TYPE_SASL;
1447 /***********************************************************************
1449 * pop3_parse_url_path()
1451 * Parse the URL path into separate path components.
1453 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1455 /* The POP3 struct is already initialised in pop3_connect() */
1456 struct Curl_easy *data = conn->data;
1457 struct POP3 *pop3 = data->req.protop;
1458 const char *path = data->state.path;
1460 /* URL decode the path for the message ID */
1461 return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1464 /***********************************************************************
1466 * pop3_parse_custom_request()
1468 * Parse the custom request.
1470 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1472 CURLcode result = CURLE_OK;
1473 struct Curl_easy *data = conn->data;
1474 struct POP3 *pop3 = data->req.protop;
1475 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1477 /* URL decode the custom request */
1479 result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1484 /***********************************************************************
1488 * This function scans the body after the end-of-body and writes everything
1489 * until the end is found.
1491 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1493 /* This code could be made into a special function in the handler struct */
1494 CURLcode result = CURLE_OK;
1495 struct Curl_easy *data = conn->data;
1496 struct SingleRequest *k = &data->req;
1498 struct pop3_conn *pop3c = &conn->proto.pop3c;
1499 bool strip_dot = FALSE;
1503 /* Search through the buffer looking for the end-of-body marker which is
1504 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1505 the eob so the server will have prefixed it with an extra dot which we
1506 need to strip out. Additionally the marker could of course be spread out
1507 over 5 different data chunks. */
1508 for(i = 0; i < nread; i++) {
1509 size_t prev = pop3c->eob;
1513 if(pop3c->eob == 0) {
1517 /* Write out the body part that didn't match */
1518 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1527 else if(pop3c->eob == 3)
1530 /* If the character match wasn't at position 0 or 3 then restart the
1536 if(pop3c->eob == 1 || pop3c->eob == 4)
1539 /* If the character match wasn't at position 1 or 4 then start the
1547 else if(pop3c->eob == 3) {
1548 /* We have an extra dot after the CRLF which we need to strip off */
1553 /* If the character match wasn't at position 2 then start the search
1563 /* Did we have a partial match which has subsequently failed? */
1564 if(prev && prev >= pop3c->eob) {
1565 /* Strip can only be non-zero for the very first mismatch after CRLF
1566 and then both prev and strip are equal and nothing will be output
1568 while(prev && pop3c->strip) {
1574 /* If the partial match was the CRLF and dot then only write the CRLF
1575 as the server would have inserted the dot */
1576 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB,
1577 strip_dot ? prev - 1 : prev);
1588 if(pop3c->eob == POP3_EOB_LEN) {
1589 /* We have a full match so the transfer is done, however we must transfer
1590 the CRLF at the start of the EOB as this is considered to be part of the
1591 message as per RFC-1939, sect. 3 */
1592 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1594 k->keepon &= ~KEEP_RECV;
1601 /* While EOB is matching nothing should be output */
1605 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1612 #endif /* CURL_DISABLE_POP3 */