1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2018, 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 ZERO_NULL, /* connection_check */
129 PORT_POP3, /* defport */
130 CURLPROTO_POP3, /* protocol */
131 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
137 * POP3S protocol handler.
140 const struct Curl_handler Curl_handler_pop3s = {
141 "POP3S", /* scheme */
142 pop3_setup_connection, /* setup_connection */
144 pop3_done, /* done */
145 ZERO_NULL, /* do_more */
146 pop3_connect, /* connect_it */
147 pop3_multi_statemach, /* connecting */
148 pop3_doing, /* doing */
149 pop3_getsock, /* proto_getsock */
150 pop3_getsock, /* doing_getsock */
151 ZERO_NULL, /* domore_getsock */
152 ZERO_NULL, /* perform_getsock */
153 pop3_disconnect, /* disconnect */
154 ZERO_NULL, /* readwrite */
155 ZERO_NULL, /* connection_check */
156 PORT_POP3S, /* defport */
157 CURLPROTO_POP3S, /* protocol */
158 PROTOPT_CLOSEACTION | PROTOPT_SSL
159 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
163 /* SASL parameters for the pop3 protocol */
164 static const struct SASLproto saslpop3 = {
165 "pop", /* The service name */
166 '*', /* Code received when continuation is expected */
167 '+', /* Code to receive upon authentication success */
168 255 - 8, /* Maximum initial response length (no max) */
169 pop3_perform_auth, /* Send authentication command */
170 pop3_continue_auth, /* Send authentication continuation */
171 pop3_get_message /* Get SASL response message */
175 static void pop3_to_pop3s(struct connectdata *conn)
177 /* Change the connection handler */
178 conn->handler = &Curl_handler_pop3s;
180 /* Set the connection's upgraded to TLS flag */
181 conn->tls_upgraded = TRUE;
184 #define pop3_to_pop3s(x) Curl_nop_stmt
187 /***********************************************************************
191 * Checks for an ending POP3 status code at the start of the given string, but
192 * also detects the APOP timestamp from the server greeting and various
193 * capabilities from the CAPA response including the supported authentication
194 * types and allowed SASL mechanisms.
196 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
199 struct pop3_conn *pop3c = &conn->proto.pop3c;
201 /* Do we have an error response? */
202 if(len >= 4 && !memcmp("-ERR", line, 4)) {
208 /* Are we processing CAPA command responses? */
209 if(pop3c->state == POP3_CAPA) {
210 /* Do we have the terminating line? */
211 if(len >= 1 && !memcmp(line, ".", 1))
212 /* Treat the response as a success */
215 /* Treat the response as an untagged continuation */
221 /* Do we have a success response? */
222 if(len >= 3 && !memcmp("+OK", line, 3)) {
228 /* Do we have a continuation response? */
229 if(len >= 1 && !memcmp("+", line, 1)) {
235 return FALSE; /* Nothing for us */
238 /***********************************************************************
242 * Gets the authentication message from the response buffer.
244 static void pop3_get_message(char *buffer, char **outptr)
246 size_t len = strlen(buffer);
247 char *message = NULL;
250 /* Find the start of the message */
252 for(message = buffer + 2; *message == ' ' || *message == '\t';
256 /* Find the end of the message */
258 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
259 message[len] != '\t')
262 /* Terminate the message */
268 /* junk input => zero length output */
269 message = &buffer[len];
274 /***********************************************************************
278 * This is the ONLY way to change POP3 state!
280 static void state(struct connectdata *conn, pop3state newstate)
282 struct pop3_conn *pop3c = &conn->proto.pop3c;
283 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
284 /* for debug purposes */
285 static const char * const names[] = {
300 if(pop3c->state != newstate)
301 infof(conn->data, "POP3 %p state change from %s to %s\n",
302 (void *)pop3c, names[pop3c->state], names[newstate]);
305 pop3c->state = newstate;
308 /***********************************************************************
310 * pop3_perform_capa()
312 * Sends the CAPA command in order to obtain a list of server side supported
315 static CURLcode pop3_perform_capa(struct connectdata *conn)
317 CURLcode result = CURLE_OK;
318 struct pop3_conn *pop3c = &conn->proto.pop3c;
320 pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
321 pop3c->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
322 pop3c->tls_supported = FALSE; /* Clear the TLS capability */
324 /* Send the CAPA command */
325 result = Curl_pp_sendf(&pop3c->pp, "%s", "CAPA");
328 state(conn, POP3_CAPA);
333 /***********************************************************************
335 * pop3_perform_starttls()
337 * Sends the STLS command to start the upgrade to TLS.
339 static CURLcode pop3_perform_starttls(struct connectdata *conn)
341 CURLcode result = CURLE_OK;
343 /* Send the STLS command */
344 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "STLS");
347 state(conn, POP3_STARTTLS);
352 /***********************************************************************
354 * pop3_perform_upgrade_tls()
356 * Performs the upgrade to TLS.
358 static CURLcode pop3_perform_upgrade_tls(struct connectdata *conn)
360 CURLcode result = CURLE_OK;
361 struct pop3_conn *pop3c = &conn->proto.pop3c;
363 /* Start the SSL connection */
364 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
367 if(pop3c->state != POP3_UPGRADETLS)
368 state(conn, POP3_UPGRADETLS);
372 result = pop3_perform_capa(conn);
379 /***********************************************************************
381 * pop3_perform_user()
383 * Sends a clear text USER command to authenticate with.
385 static CURLcode pop3_perform_user(struct connectdata *conn)
387 CURLcode result = CURLE_OK;
389 /* Check we have a username and password to authenticate with and end the
390 connect phase if we don't */
391 if(!conn->bits.user_passwd) {
392 state(conn, POP3_STOP);
397 /* Send the USER command */
398 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
399 conn->user ? conn->user : "");
401 state(conn, POP3_USER);
406 #ifndef CURL_DISABLE_CRYPTO_AUTH
407 /***********************************************************************
409 * pop3_perform_apop()
411 * Sends an APOP command to authenticate with.
413 static CURLcode pop3_perform_apop(struct connectdata *conn)
415 CURLcode result = CURLE_OK;
416 struct pop3_conn *pop3c = &conn->proto.pop3c;
419 unsigned char digest[MD5_DIGEST_LEN];
420 char secret[2 * MD5_DIGEST_LEN + 1];
422 /* Check we have a username and password to authenticate with and end the
423 connect phase if we don't */
424 if(!conn->bits.user_passwd) {
425 state(conn, POP3_STOP);
430 /* Create the digest */
431 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
433 return CURLE_OUT_OF_MEMORY;
435 Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
436 curlx_uztoui(strlen(pop3c->apoptimestamp)));
438 Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
439 curlx_uztoui(strlen(conn->passwd)));
441 /* Finalise the digest */
442 Curl_MD5_final(ctxt, digest);
444 /* Convert the calculated 16 octet digest into a 32 byte hex string */
445 for(i = 0; i < MD5_DIGEST_LEN; i++)
446 snprintf(&secret[2 * i], 3, "%02x", digest[i]);
448 result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
451 state(conn, POP3_APOP);
457 /***********************************************************************
459 * pop3_perform_auth()
461 * Sends an AUTH command allowing the client to login with the given SASL
462 * authentication mechanism.
464 static CURLcode pop3_perform_auth(struct connectdata *conn,
466 const char *initresp)
468 CURLcode result = CURLE_OK;
469 struct pop3_conn *pop3c = &conn->proto.pop3c;
471 if(initresp) { /* AUTH <mech> ...<crlf> */
472 /* Send the AUTH command with the initial response */
473 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s %s", mech, initresp);
476 /* Send the AUTH command */
477 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
483 /***********************************************************************
485 * pop3_continue_auth()
487 * Sends SASL continuation data or cancellation.
489 static CURLcode pop3_continue_auth(struct connectdata *conn,
492 struct pop3_conn *pop3c = &conn->proto.pop3c;
494 return Curl_pp_sendf(&pop3c->pp, "%s", resp);
497 /***********************************************************************
499 * pop3_perform_authentication()
501 * Initiates the authentication sequence, with the appropriate SASL
502 * authentication mechanism, falling back to APOP and clear text should a
503 * common mechanism not be available between the client and server.
505 static CURLcode pop3_perform_authentication(struct connectdata *conn)
507 CURLcode result = CURLE_OK;
508 struct pop3_conn *pop3c = &conn->proto.pop3c;
509 saslprogress progress = SASL_IDLE;
511 /* Check we have enough data to authenticate with and end the
512 connect phase if we don't */
513 if(!Curl_sasl_can_authenticate(&pop3c->sasl, conn)) {
514 state(conn, POP3_STOP);
518 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
519 /* Calculate the SASL login details */
520 result = Curl_sasl_start(&pop3c->sasl, conn, FALSE, &progress);
523 if(progress == SASL_INPROGRESS)
524 state(conn, POP3_AUTH);
527 if(!result && progress == SASL_IDLE) {
528 #ifndef CURL_DISABLE_CRYPTO_AUTH
529 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
530 /* Perform APOP authentication */
531 result = pop3_perform_apop(conn);
534 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
535 /* Perform clear text authentication */
536 result = pop3_perform_user(conn);
538 /* Other mechanisms not supported */
539 infof(conn->data, "No known authentication mechanisms supported!\n");
540 result = CURLE_LOGIN_DENIED;
547 /***********************************************************************
549 * pop3_perform_command()
551 * Sends a POP3 based command.
553 static CURLcode pop3_perform_command(struct connectdata *conn)
555 CURLcode result = CURLE_OK;
556 struct Curl_easy *data = conn->data;
557 struct POP3 *pop3 = data->req.protop;
558 const char *command = NULL;
560 /* Calculate the default command */
561 if(pop3->id[0] == '\0' || conn->data->set.ftp_list_only) {
564 if(pop3->id[0] != '\0')
565 /* Message specific LIST so skip the BODY transfer */
566 pop3->transfer = FTPTRANSFER_INFO;
571 /* Send the command */
572 if(pop3->id[0] != '\0')
573 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
574 (pop3->custom && pop3->custom[0] != '\0' ?
575 pop3->custom : command), pop3->id);
577 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s",
578 (pop3->custom && pop3->custom[0] != '\0' ?
579 pop3->custom : command));
582 state(conn, POP3_COMMAND);
587 /***********************************************************************
589 * pop3_perform_quit()
591 * Performs the quit action prior to sclose() be called.
593 static CURLcode pop3_perform_quit(struct connectdata *conn)
595 CURLcode result = CURLE_OK;
597 /* Send the QUIT command */
598 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", "QUIT");
601 state(conn, POP3_QUIT);
606 /* For the initial server greeting */
607 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
611 CURLcode result = CURLE_OK;
612 struct Curl_easy *data = conn->data;
613 struct pop3_conn *pop3c = &conn->proto.pop3c;
614 const char *line = data->state.buffer;
615 size_t len = strlen(line);
618 (void)instate; /* no use for this yet */
620 if(pop3code != '+') {
621 failf(data, "Got unexpected pop3-server response");
622 result = CURLE_WEIRD_SERVER_REPLY;
625 /* Does the server support APOP authentication? */
626 if(len >= 4 && line[len - 2] == '>') {
627 /* Look for the APOP timestamp */
628 for(i = 3; i < len - 2; ++i) {
630 /* Calculate the length of the timestamp */
631 size_t timestamplen = len - 1 - i;
635 /* Allocate some memory for the timestamp */
636 pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
638 if(!pop3c->apoptimestamp)
641 /* Copy the timestamp */
642 memcpy(pop3c->apoptimestamp, line + i, timestamplen);
643 pop3c->apoptimestamp[timestamplen] = '\0';
645 /* Store the APOP capability */
646 pop3c->authtypes |= POP3_TYPE_APOP;
652 result = pop3_perform_capa(conn);
658 /* For CAPA responses */
659 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
662 CURLcode result = CURLE_OK;
663 struct Curl_easy *data = conn->data;
664 struct pop3_conn *pop3c = &conn->proto.pop3c;
665 const char *line = data->state.buffer;
666 size_t len = strlen(line);
669 (void)instate; /* no use for this yet */
671 /* Do we have a untagged continuation response? */
672 if(pop3code == '*') {
673 /* Does the server support the STLS capability? */
674 if(len >= 4 && !memcmp(line, "STLS", 4))
675 pop3c->tls_supported = TRUE;
677 /* Does the server support clear text authentication? */
678 else if(len >= 4 && !memcmp(line, "USER", 4))
679 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
681 /* Does the server support SASL based authentication? */
682 else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
683 pop3c->authtypes |= POP3_TYPE_SASL;
685 /* Advance past the SASL keyword */
689 /* Loop through the data line */
692 unsigned int mechbit;
695 (*line == ' ' || *line == '\t' ||
696 *line == '\r' || *line == '\n')) {
705 /* Extract the word */
706 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
707 line[wordlen] != '\t' && line[wordlen] != '\r' &&
708 line[wordlen] != '\n';)
711 /* Test the word for a matching authentication mechanism */
712 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
713 if(mechbit && llen == wordlen)
714 pop3c->sasl.authmechs |= mechbit;
721 else if(pop3code == '+') {
722 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
723 /* We don't have a SSL/TLS connection yet, but SSL is requested */
724 if(pop3c->tls_supported)
725 /* Switch to TLS connection now */
726 result = pop3_perform_starttls(conn);
727 else if(data->set.use_ssl == CURLUSESSL_TRY)
728 /* Fallback and carry on with authentication */
729 result = pop3_perform_authentication(conn);
731 failf(data, "STLS not supported.");
732 result = CURLE_USE_SSL_FAILED;
736 result = pop3_perform_authentication(conn);
739 /* Clear text is supported when CAPA isn't recognised */
740 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
742 result = pop3_perform_authentication(conn);
748 /* For STARTTLS responses */
749 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
753 CURLcode result = CURLE_OK;
754 struct Curl_easy *data = conn->data;
756 (void)instate; /* no use for this yet */
758 if(pop3code != '+') {
759 if(data->set.use_ssl != CURLUSESSL_TRY) {
760 failf(data, "STARTTLS denied");
761 result = CURLE_USE_SSL_FAILED;
764 result = pop3_perform_authentication(conn);
767 result = pop3_perform_upgrade_tls(conn);
772 /* For SASL authentication responses */
773 static CURLcode pop3_state_auth_resp(struct connectdata *conn,
777 CURLcode result = CURLE_OK;
778 struct Curl_easy *data = conn->data;
779 struct pop3_conn *pop3c = &conn->proto.pop3c;
780 saslprogress progress;
782 (void)instate; /* no use for this yet */
784 result = Curl_sasl_continue(&pop3c->sasl, conn, pop3code, &progress);
788 state(conn, POP3_STOP); /* Authenticated */
790 case SASL_IDLE: /* No mechanism left after cancellation */
791 #ifndef CURL_DISABLE_CRYPTO_AUTH
792 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
793 /* Perform APOP authentication */
794 result = pop3_perform_apop(conn);
797 if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
798 /* Perform clear text authentication */
799 result = pop3_perform_user(conn);
801 failf(data, "Authentication cancelled");
802 result = CURLE_LOGIN_DENIED;
812 #ifndef CURL_DISABLE_CRYPTO_AUTH
813 /* For APOP responses */
814 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
817 CURLcode result = CURLE_OK;
818 struct Curl_easy *data = conn->data;
820 (void)instate; /* no use for this yet */
822 if(pop3code != '+') {
823 failf(data, "Authentication failed: %d", pop3code);
824 result = CURLE_LOGIN_DENIED;
827 /* End of connect phase */
828 state(conn, POP3_STOP);
834 /* For USER responses */
835 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
838 CURLcode result = CURLE_OK;
839 struct Curl_easy *data = conn->data;
841 (void)instate; /* no use for this yet */
843 if(pop3code != '+') {
844 failf(data, "Access denied. %c", pop3code);
845 result = CURLE_LOGIN_DENIED;
848 /* Send the PASS command */
849 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
850 conn->passwd ? conn->passwd : "");
852 state(conn, POP3_PASS);
857 /* For PASS responses */
858 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
861 CURLcode result = CURLE_OK;
862 struct Curl_easy *data = conn->data;
864 (void)instate; /* no use for this yet */
866 if(pop3code != '+') {
867 failf(data, "Access denied. %c", pop3code);
868 result = CURLE_LOGIN_DENIED;
871 /* End of connect phase */
872 state(conn, POP3_STOP);
877 /* For command responses */
878 static CURLcode pop3_state_command_resp(struct connectdata *conn,
882 CURLcode result = CURLE_OK;
883 struct Curl_easy *data = conn->data;
884 struct POP3 *pop3 = data->req.protop;
885 struct pop3_conn *pop3c = &conn->proto.pop3c;
886 struct pingpong *pp = &pop3c->pp;
888 (void)instate; /* no use for this yet */
890 if(pop3code != '+') {
891 state(conn, POP3_STOP);
892 return CURLE_RECV_ERROR;
895 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
896 EOB string so count this is two matching bytes. This is necessary to make
897 the code detect the EOB if the only data than comes now is %2e CR LF like
898 when there is no body to return. */
901 /* But since this initial CR LF pair is not part of the actual body, we set
902 the strip counter here so that these bytes won't be delivered. */
905 if(pop3->transfer == FTPTRANSFER_BODY) {
907 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
910 /* The header "cache" contains a bunch of data that is actually body
911 content so send it as such. Note that there may even be additional
912 "headers" after the body */
914 if(!data->set.opt_no_body) {
915 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
921 Curl_safefree(pp->cache);
923 /* Reset the cache size */
928 /* End of DO phase */
929 state(conn, POP3_STOP);
934 static CURLcode pop3_statemach_act(struct connectdata *conn)
936 CURLcode result = CURLE_OK;
937 curl_socket_t sock = conn->sock[FIRSTSOCKET];
939 struct pop3_conn *pop3c = &conn->proto.pop3c;
940 struct pingpong *pp = &pop3c->pp;
943 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
944 if(pop3c->state == POP3_UPGRADETLS)
945 return pop3_perform_upgrade_tls(conn);
947 /* Flush any data that needs to be sent */
949 return Curl_pp_flushsend(pp);
952 /* Read the response from the server */
953 result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
960 /* We have now received a full POP3 server response */
961 switch(pop3c->state) {
962 case POP3_SERVERGREET:
963 result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
967 result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
971 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
975 result = pop3_state_auth_resp(conn, pop3code, pop3c->state);
978 #ifndef CURL_DISABLE_CRYPTO_AUTH
980 result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
985 result = pop3_state_user_resp(conn, pop3code, pop3c->state);
989 result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
993 result = pop3_state_command_resp(conn, pop3code, pop3c->state);
997 /* fallthrough, just stop! */
1000 state(conn, POP3_STOP);
1003 } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
1008 /* Called repeatedly until done from multi.c */
1009 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1011 CURLcode result = CURLE_OK;
1012 struct pop3_conn *pop3c = &conn->proto.pop3c;
1014 if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
1015 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1016 if(result || !pop3c->ssldone)
1020 result = Curl_pp_statemach(&pop3c->pp, FALSE);
1021 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1026 static CURLcode pop3_block_statemach(struct connectdata *conn)
1028 CURLcode result = CURLE_OK;
1029 struct pop3_conn *pop3c = &conn->proto.pop3c;
1031 while(pop3c->state != POP3_STOP && !result)
1032 result = Curl_pp_statemach(&pop3c->pp, TRUE);
1037 /* Allocate and initialize the POP3 struct for the current Curl_easy if
1039 static CURLcode pop3_init(struct connectdata *conn)
1041 CURLcode result = CURLE_OK;
1042 struct Curl_easy *data = conn->data;
1045 pop3 = data->req.protop = calloc(sizeof(struct POP3), 1);
1047 result = CURLE_OUT_OF_MEMORY;
1052 /* For the POP3 "protocol connect" and "doing" phases only */
1053 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1056 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1059 /***********************************************************************
1063 * This function should do everything that is to be considered a part of the
1066 * The variable 'done' points to will be TRUE if the protocol-layer connect
1067 * phase is done when this function returns, or FALSE if not.
1069 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1071 CURLcode result = CURLE_OK;
1072 struct pop3_conn *pop3c = &conn->proto.pop3c;
1073 struct pingpong *pp = &pop3c->pp;
1075 *done = FALSE; /* default to not done yet */
1077 /* We always support persistent connections in POP3 */
1078 connkeep(conn, "POP3 default");
1080 /* Set the default response time-out */
1081 pp->response_time = RESP_TIMEOUT;
1082 pp->statemach_act = pop3_statemach_act;
1083 pp->endofresp = pop3_endofresp;
1086 /* Set the default preferred authentication type and mechanism */
1087 pop3c->preftype = POP3_TYPE_ANY;
1088 Curl_sasl_init(&pop3c->sasl, &saslpop3);
1090 /* Initialise the pingpong layer */
1093 /* Parse the URL options */
1094 result = pop3_parse_url_options(conn);
1098 /* Start off waiting for the server greeting response */
1099 state(conn, POP3_SERVERGREET);
1101 result = pop3_multi_statemach(conn, done);
1106 /***********************************************************************
1110 * The DONE function. This does what needs to be done after a single DO has
1113 * Input argument is already checked for validity.
1115 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1118 CURLcode result = CURLE_OK;
1119 struct Curl_easy *data = conn->data;
1120 struct POP3 *pop3 = data->req.protop;
1128 connclose(conn, "POP3 done with bad status");
1129 result = status; /* use the already set error code */
1132 /* Cleanup our per-request based variables */
1133 Curl_safefree(pop3->id);
1134 Curl_safefree(pop3->custom);
1136 /* Clear the transfer mode for the next request */
1137 pop3->transfer = FTPTRANSFER_BODY;
1142 /***********************************************************************
1146 * This is the actual DO function for POP3. Get a message/listing according to
1147 * the options previously setup.
1149 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1152 /* This is POP3 and no proxy */
1153 CURLcode result = CURLE_OK;
1154 struct POP3 *pop3 = conn->data->req.protop;
1156 DEBUGF(infof(conn->data, "DO phase starts\n"));
1158 if(conn->data->set.opt_no_body) {
1159 /* Requested no body means no transfer */
1160 pop3->transfer = FTPTRANSFER_INFO;
1163 *dophase_done = FALSE; /* not done yet */
1165 /* Start the first command in the DO phase */
1166 result = pop3_perform_command(conn);
1170 /* Run the state-machine */
1171 result = pop3_multi_statemach(conn, dophase_done);
1173 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1176 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1181 /***********************************************************************
1185 * This function is registered as 'curl_do' function. It decodes the path
1186 * parts etc as a wrapper to the actual DO function (pop3_perform).
1188 * The input argument is already checked for validity.
1190 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1192 CURLcode result = CURLE_OK;
1194 *done = FALSE; /* default to false */
1196 /* Parse the URL path */
1197 result = pop3_parse_url_path(conn);
1201 /* Parse the custom request */
1202 result = pop3_parse_custom_request(conn);
1206 result = pop3_regular_transfer(conn, done);
1211 /***********************************************************************
1215 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1216 * resources. BLOCKING.
1218 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead_connection)
1220 struct pop3_conn *pop3c = &conn->proto.pop3c;
1222 /* We cannot send quit unconditionally. If this connection is stale or
1223 bad in any way, sending quit and waiting around here will make the
1224 disconnect wait in vain and cause more problems than we need to. */
1226 /* The POP3 session may or may not have been allocated/setup at this
1228 if(!dead_connection && pop3c->pp.conn && pop3c->pp.conn->bits.protoconnstart)
1229 if(!pop3_perform_quit(conn))
1230 (void)pop3_block_statemach(conn); /* ignore errors on QUIT */
1232 /* Disconnect from the server */
1233 Curl_pp_disconnect(&pop3c->pp);
1235 /* Cleanup the SASL module */
1236 Curl_sasl_cleanup(conn, pop3c->sasl.authused);
1238 /* Cleanup our connection based variables */
1239 Curl_safefree(pop3c->apoptimestamp);
1244 /* Call this when the DO phase has completed */
1245 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1253 /* Called from multi.c while DOing */
1254 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1256 CURLcode result = pop3_multi_statemach(conn, dophase_done);
1259 DEBUGF(infof(conn->data, "DO phase failed\n"));
1260 else if(*dophase_done) {
1261 result = pop3_dophase_done(conn, FALSE /* not connected */);
1263 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1269 /***********************************************************************
1271 * pop3_regular_transfer()
1273 * The input argument is already checked for validity.
1275 * Performs all commands done before a regular transfer between a local and a
1278 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1281 CURLcode result = CURLE_OK;
1282 bool connected = FALSE;
1283 struct Curl_easy *data = conn->data;
1285 /* Make sure size is unknown at this point */
1286 data->req.size = -1;
1288 /* Set the progress data */
1289 Curl_pgrsSetUploadCounter(data, 0);
1290 Curl_pgrsSetDownloadCounter(data, 0);
1291 Curl_pgrsSetUploadSize(data, -1);
1292 Curl_pgrsSetDownloadSize(data, -1);
1294 /* Carry out the perform */
1295 result = pop3_perform(conn, &connected, dophase_done);
1297 /* Perform post DO phase operations if necessary */
1298 if(!result && *dophase_done)
1299 result = pop3_dophase_done(conn, connected);
1304 static CURLcode pop3_setup_connection(struct connectdata *conn)
1306 struct Curl_easy *data = conn->data;
1308 /* Initialise the POP3 layer */
1309 CURLcode result = pop3_init(conn);
1313 /* Clear the TLS upgraded flag */
1314 conn->tls_upgraded = FALSE;
1315 data->state.path++; /* don't include the initial slash */
1320 /***********************************************************************
1322 * pop3_parse_url_options()
1324 * Parse the URL login options.
1326 static CURLcode pop3_parse_url_options(struct connectdata *conn)
1328 CURLcode result = CURLE_OK;
1329 struct pop3_conn *pop3c = &conn->proto.pop3c;
1330 const char *ptr = conn->options;
1332 pop3c->sasl.resetprefs = TRUE;
1334 while(!result && ptr && *ptr) {
1335 const char *key = ptr;
1338 while(*ptr && *ptr != '=')
1343 while(*ptr && *ptr != ';')
1346 if(strncasecompare(key, "AUTH=", 5)) {
1347 result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
1348 value, ptr - value);
1350 if(result && strncasecompare(value, "+APOP", ptr - value)) {
1351 pop3c->preftype = POP3_TYPE_APOP;
1352 pop3c->sasl.prefmech = SASL_AUTH_NONE;
1357 result = CURLE_URL_MALFORMAT;
1363 if(pop3c->preftype != POP3_TYPE_APOP)
1364 switch(pop3c->sasl.prefmech) {
1365 case SASL_AUTH_NONE:
1366 pop3c->preftype = POP3_TYPE_NONE;
1368 case SASL_AUTH_DEFAULT:
1369 pop3c->preftype = POP3_TYPE_ANY;
1372 pop3c->preftype = POP3_TYPE_SASL;
1379 /***********************************************************************
1381 * pop3_parse_url_path()
1383 * Parse the URL path into separate path components.
1385 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1387 /* The POP3 struct is already initialised in pop3_connect() */
1388 struct Curl_easy *data = conn->data;
1389 struct POP3 *pop3 = data->req.protop;
1390 const char *path = data->state.path;
1392 /* URL decode the path for the message ID */
1393 return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
1396 /***********************************************************************
1398 * pop3_parse_custom_request()
1400 * Parse the custom request.
1402 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1404 CURLcode result = CURLE_OK;
1405 struct Curl_easy *data = conn->data;
1406 struct POP3 *pop3 = data->req.protop;
1407 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1409 /* URL decode the custom request */
1411 result = Curl_urldecode(data, custom, 0, &pop3->custom, NULL, TRUE);
1416 /***********************************************************************
1420 * This function scans the body after the end-of-body and writes everything
1421 * until the end is found.
1423 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1425 /* This code could be made into a special function in the handler struct */
1426 CURLcode result = CURLE_OK;
1427 struct Curl_easy *data = conn->data;
1428 struct SingleRequest *k = &data->req;
1430 struct pop3_conn *pop3c = &conn->proto.pop3c;
1431 bool strip_dot = FALSE;
1435 /* Search through the buffer looking for the end-of-body marker which is
1436 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1437 the eob so the server will have prefixed it with an extra dot which we
1438 need to strip out. Additionally the marker could of course be spread out
1439 over 5 different data chunks. */
1440 for(i = 0; i < nread; i++) {
1441 size_t prev = pop3c->eob;
1445 if(pop3c->eob == 0) {
1449 /* Write out the body part that didn't match */
1450 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1459 else if(pop3c->eob == 3)
1462 /* If the character match wasn't at position 0 or 3 then restart the
1468 if(pop3c->eob == 1 || pop3c->eob == 4)
1471 /* If the character match wasn't at position 1 or 4 then start the
1479 else if(pop3c->eob == 3) {
1480 /* We have an extra dot after the CRLF which we need to strip off */
1485 /* If the character match wasn't at position 2 then start the search
1495 /* Did we have a partial match which has subsequently failed? */
1496 if(prev && prev >= pop3c->eob) {
1497 /* Strip can only be non-zero for the very first mismatch after CRLF
1498 and then both prev and strip are equal and nothing will be output
1500 while(prev && pop3c->strip) {
1506 /* If the partial match was the CRLF and dot then only write the CRLF
1507 as the server would have inserted the dot */
1508 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB,
1509 strip_dot ? prev - 1 : prev);
1520 if(pop3c->eob == POP3_EOB_LEN) {
1521 /* We have a full match so the transfer is done, however we must transfer
1522 the CRLF at the start of the EOB as this is considered to be part of the
1523 message as per RFC-1939, sect. 3 */
1524 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1526 k->keepon &= ~KEEP_RECV;
1533 /* While EOB is matching nothing should be output */
1537 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1544 #endif /* CURL_DISABLE_POP3 */