1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC1734 POP3 Authentication
22 * RFC1939 POP3 protocol
23 * RFC2195 CRAM-MD5 authentication
24 * RFC2384 POP URL Scheme
25 * RFC2449 POP3 Extension Mechanism
26 * RFC2595 Using TLS with IMAP, POP3 and ACAP
27 * RFC2831 DIGEST-MD5 authentication
28 * RFC4422 Simple Authentication and Security Layer (SASL)
29 * RFC4616 PLAIN authentication
30 * RFC5034 POP3 SASL Authentication Mechanism
32 ***************************************************************************/
34 #include "curl_setup.h"
36 #ifndef CURL_DISABLE_POP3
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
41 #ifdef HAVE_ARPA_INET_H
42 #include <arpa/inet.h>
45 #include <sys/utsname.h>
55 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57 #define in_addr_t unsigned long
60 #include <curl/curl.h>
68 #include "http.h" /* for HTTP proxy tunnel stuff */
72 #include "strtoofft.h"
81 #include "curl_sasl.h"
85 #define _MPRINTF_REPLACE /* use our functions only */
86 #include <curl/mprintf.h>
88 #include "curl_memory.h"
89 /* The last #include file should be: */
92 /* Local API functions */
93 static CURLcode pop3_parse_url_path(struct connectdata *conn);
94 static CURLcode pop3_parse_custom_request(struct connectdata *conn);
95 static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
96 static CURLcode pop3_do(struct connectdata *conn, bool *done);
97 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
99 static CURLcode pop3_connect(struct connectdata *conn, bool *done);
100 static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
101 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
102 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
104 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
105 static CURLcode pop3_setup_connection(struct connectdata *conn);
108 * POP3 protocol handler.
111 const struct Curl_handler Curl_handler_pop3 = {
113 pop3_setup_connection, /* setup_connection */
115 pop3_done, /* done */
116 ZERO_NULL, /* do_more */
117 pop3_connect, /* connect_it */
118 pop3_multi_statemach, /* connecting */
119 pop3_doing, /* doing */
120 pop3_getsock, /* proto_getsock */
121 pop3_getsock, /* doing_getsock */
122 ZERO_NULL, /* domore_getsock */
123 ZERO_NULL, /* perform_getsock */
124 pop3_disconnect, /* disconnect */
125 ZERO_NULL, /* readwrite */
126 PORT_POP3, /* defport */
127 CURLPROTO_POP3, /* protocol */
128 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
133 * POP3S protocol handler.
136 const struct Curl_handler Curl_handler_pop3s = {
137 "POP3S", /* scheme */
138 pop3_setup_connection, /* setup_connection */
140 pop3_done, /* done */
141 ZERO_NULL, /* do_more */
142 pop3_connect, /* connect_it */
143 pop3_multi_statemach, /* connecting */
144 pop3_doing, /* doing */
145 pop3_getsock, /* proto_getsock */
146 pop3_getsock, /* doing_getsock */
147 ZERO_NULL, /* domore_getsock */
148 ZERO_NULL, /* perform_getsock */
149 pop3_disconnect, /* disconnect */
150 ZERO_NULL, /* readwrite */
151 PORT_POP3S, /* defport */
152 CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */
153 PROTOPT_CLOSEACTION | PROTOPT_SSL
154 | PROTOPT_NOURLQUERY /* flags */
158 #ifndef CURL_DISABLE_HTTP
160 * HTTP-proxyed POP3 protocol handler.
163 static const struct Curl_handler Curl_handler_pop3_proxy = {
165 ZERO_NULL, /* setup_connection */
166 Curl_http, /* do_it */
167 Curl_http_done, /* done */
168 ZERO_NULL, /* do_more */
169 ZERO_NULL, /* connect_it */
170 ZERO_NULL, /* connecting */
171 ZERO_NULL, /* doing */
172 ZERO_NULL, /* proto_getsock */
173 ZERO_NULL, /* doing_getsock */
174 ZERO_NULL, /* domore_getsock */
175 ZERO_NULL, /* perform_getsock */
176 ZERO_NULL, /* disconnect */
177 ZERO_NULL, /* readwrite */
178 PORT_POP3, /* defport */
179 CURLPROTO_HTTP, /* protocol */
180 PROTOPT_NONE /* flags */
185 * HTTP-proxyed POP3S protocol handler.
188 static const struct Curl_handler Curl_handler_pop3s_proxy = {
189 "POP3S", /* scheme */
190 ZERO_NULL, /* setup_connection */
191 Curl_http, /* do_it */
192 Curl_http_done, /* done */
193 ZERO_NULL, /* do_more */
194 ZERO_NULL, /* connect_it */
195 ZERO_NULL, /* connecting */
196 ZERO_NULL, /* doing */
197 ZERO_NULL, /* proto_getsock */
198 ZERO_NULL, /* doing_getsock */
199 ZERO_NULL, /* domore_getsock */
200 ZERO_NULL, /* perform_getsock */
201 ZERO_NULL, /* disconnect */
202 ZERO_NULL, /* readwrite */
203 PORT_POP3S, /* defport */
204 CURLPROTO_HTTP, /* protocol */
205 PROTOPT_NONE /* flags */
211 static void pop3_to_pop3s(struct connectdata *conn)
213 conn->handler = &Curl_handler_pop3s;
216 #define pop3_to_pop3s(x) Curl_nop_stmt
219 /* Function that checks for an ending POP3 status code at the start of the
220 given string, but also detects the APOP timestamp from the server greeting
221 and various capabilities from the CAPA response including the supported
222 authentication types and allowed SASL mechanisms. */
223 static bool pop3_endofresp(struct connectdata *conn, char *line, size_t len,
226 struct pop3_conn *pop3c = &conn->proto.pop3c;
230 /* Do we have an error response? */
231 if(len >= 4 && !memcmp("-ERR", line, 4)) {
237 /* Are we processing servergreet responses? */
238 if(pop3c->state == POP3_SERVERGREET) {
239 /* Look for the APOP timestamp */
240 if(len >= 3 && line[len - 3] == '>') {
241 for(i = 0; i < len - 3; ++i) {
243 /* Calculate the length of the timestamp */
244 size_t timestamplen = len - 2 - i;
246 /* Allocate some memory for the timestamp */
247 pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
249 if(!pop3c->apoptimestamp)
252 /* Copy the timestamp */
253 memcpy(pop3c->apoptimestamp, line + i, timestamplen);
254 pop3c->apoptimestamp[timestamplen] = '\0';
260 /* Are we processing CAPA command responses? */
261 else if(pop3c->state == POP3_CAPA) {
262 /* Do we have the terminating line? */
263 if(len >= 1 && !memcmp(line, ".", 1)) {
269 /* Does the server support the STLS capability? */
270 if(len >= 4 && !memcmp(line, "STLS", 4))
271 pop3c->tls_supported = TRUE;
273 /* Does the server support clear text authentication? */
274 else if(len >= 4 && !memcmp(line, "USER", 4))
275 pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
277 /* Does the server support APOP authentication? */
278 else if(len >= 4 && !memcmp(line, "APOP", 4))
279 pop3c->authtypes |= POP3_TYPE_APOP;
281 /* Does the server support SASL based authentication? */
282 else if(len >= 5 && !memcmp(line, "SASL ", 5)) {
283 pop3c->authtypes |= POP3_TYPE_SASL;
285 /* Advance past the SASL keyword */
289 /* Loop through the data line */
292 (*line == ' ' || *line == '\t' ||
293 *line == '\r' || *line == '\n')) {
302 /* Extract the word */
303 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
304 line[wordlen] != '\t' && line[wordlen] != '\r' &&
305 line[wordlen] != '\n';)
308 /* Test the word for a matching authentication mechanism */
309 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
310 pop3c->authmechs |= SASL_MECH_LOGIN;
311 else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
312 pop3c->authmechs |= SASL_MECH_PLAIN;
313 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
314 pop3c->authmechs |= SASL_MECH_CRAM_MD5;
315 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
316 pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
317 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
318 pop3c->authmechs |= SASL_MECH_GSSAPI;
319 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
320 pop3c->authmechs |= SASL_MECH_EXTERNAL;
321 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
322 pop3c->authmechs |= SASL_MECH_NTLM;
332 /* Do we have a command or continuation response? */
333 if((len >= 3 && !memcmp("+OK", line, 3)) ||
334 (len >= 1 && !memcmp("+", line, 1))) {
340 return FALSE; /* Nothing for us */
343 /* This is the ONLY way to change POP3 state! */
344 static void state(struct connectdata *conn, pop3state newstate)
346 struct pop3_conn *pop3c = &conn->proto.pop3c;
347 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
348 /* for debug purposes */
349 static const char * const names[] = {
360 "AUTH_DIGESTMD5_RESP",
362 "AUTH_NTLM_TYPE2MSG",
372 if(pop3c->state != newstate)
373 infof(conn->data, "POP3 %p state change from %s to %s\n",
374 pop3c, names[pop3c->state], names[newstate]);
377 pop3c->state = newstate;
380 static CURLcode pop3_state_capa(struct connectdata *conn)
382 CURLcode result = CURLE_OK;
383 struct pop3_conn *pop3c = &conn->proto.pop3c;
385 pop3c->authmechs = 0; /* No known authentication mechanisms yet */
386 pop3c->authused = 0; /* Clear the authentication mechanism used */
387 pop3c->tls_supported = FALSE; /* Clear the TLS capability */
389 /* Send the CAPA command */
390 result = Curl_pp_sendf(&pop3c->pp, "CAPA");
395 state(conn, POP3_CAPA);
400 static CURLcode pop3_state_starttls(struct connectdata *conn)
402 CURLcode result = CURLE_OK;
404 /* Send the STLS command */
405 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "STLS");
408 state(conn, POP3_STARTTLS);
413 static CURLcode pop3_state_upgrade_tls(struct connectdata *conn)
415 CURLcode result = CURLE_OK;
416 struct pop3_conn *pop3c = &conn->proto.pop3c;
418 /* Start the SSL connection */
419 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
422 if(pop3c->state != POP3_UPGRADETLS)
423 state(conn, POP3_UPGRADETLS);
427 result = pop3_state_capa(conn);
434 static CURLcode pop3_state_user(struct connectdata *conn)
436 CURLcode result = CURLE_OK;
437 struct FTP *pop3 = conn->data->state.proto.pop3;
439 /* Check we have a username and password to authenticate with and end the
440 connect phase if we don't */
441 if(!conn->bits.user_passwd) {
442 state(conn, POP3_STOP);
447 /* Send the USER command */
448 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
449 pop3->user ? pop3->user : "");
453 state(conn, POP3_USER);
458 #ifndef CURL_DISABLE_CRYPTO_AUTH
459 static CURLcode pop3_state_apop(struct connectdata *conn)
461 CURLcode result = CURLE_OK;
462 struct pop3_conn *pop3c = &conn->proto.pop3c;
465 unsigned char digest[MD5_DIGEST_LEN];
466 char secret[2 * MD5_DIGEST_LEN + 1];
468 /* Check we have a username and password to authenticate with and end the
469 connect phase if we don't */
470 if(!conn->bits.user_passwd) {
471 state(conn, POP3_STOP);
476 /* Create the digest */
477 ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
479 return CURLE_OUT_OF_MEMORY;
481 Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp,
482 curlx_uztoui(strlen(pop3c->apoptimestamp)));
484 Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd,
485 curlx_uztoui(strlen(conn->passwd)));
487 /* Finalise the digest */
488 Curl_MD5_final(ctxt, digest);
490 /* Convert the calculated 16 octet digest into a 32 byte hex string */
491 for(i = 0; i < MD5_DIGEST_LEN; i++)
492 snprintf(&secret[2 * i], 3, "%02x", digest[i]);
494 result = Curl_pp_sendf(&pop3c->pp, "APOP %s %s", conn->user, secret);
497 state(conn, POP3_APOP);
503 static CURLcode pop3_authenticate(struct connectdata *conn)
505 CURLcode result = CURLE_OK;
506 struct pop3_conn *pop3c = &conn->proto.pop3c;
507 const char *mech = NULL;
508 pop3state authstate = POP3_STOP;
510 /* Check we have a username and password to authenticate with and end the
511 connect phase if we don't */
512 if(!conn->bits.user_passwd) {
513 state(conn, POP3_STOP);
518 /* Calculate the supported authentication mechanism by decreasing order of
520 if(pop3c->authtypes & POP3_TYPE_SASL) {
521 #ifndef CURL_DISABLE_CRYPTO_AUTH
522 if(pop3c->authmechs & SASL_MECH_DIGEST_MD5) {
524 authstate = POP3_AUTH_DIGESTMD5;
525 pop3c->authused = SASL_MECH_DIGEST_MD5;
527 else if(pop3c->authmechs & SASL_MECH_CRAM_MD5) {
529 authstate = POP3_AUTH_CRAMMD5;
530 pop3c->authused = SASL_MECH_CRAM_MD5;
535 if(pop3c->authmechs & SASL_MECH_NTLM) {
537 authstate = POP3_AUTH_NTLM;
538 pop3c->authused = SASL_MECH_NTLM;
542 if(pop3c->authmechs & SASL_MECH_LOGIN) {
544 authstate = POP3_AUTH_LOGIN;
545 pop3c->authused = SASL_MECH_LOGIN;
547 else if(pop3c->authmechs & SASL_MECH_PLAIN) {
549 authstate = POP3_AUTH_PLAIN;
550 pop3c->authused = SASL_MECH_PLAIN;
555 /* Perform SASL based authentication */
556 result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);
559 state(conn, authstate);
561 #ifndef CURL_DISABLE_CRYPTO_AUTH
562 else if(pop3c->authtypes & POP3_TYPE_APOP)
563 /* Perform APOP authentication */
564 result = pop3_state_apop(conn);
566 else if(pop3c->authtypes & POP3_TYPE_CLEARTEXT)
567 /* Perform clear text authentication */
568 result = pop3_state_user(conn);
570 /* Other mechanisms not supported */
571 infof(conn->data, "No known authentication mechanisms supported!\n");
572 result = CURLE_LOGIN_DENIED;
578 /* For the initial server greeting */
579 static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
583 CURLcode result = CURLE_OK;
584 struct SessionHandle *data = conn->data;
586 (void)instate; /* no use for this yet */
588 if(pop3code != '+') {
589 failf(data, "Got unexpected pop3-server response");
590 return CURLE_FTP_WEIRD_SERVER_REPLY;
593 result = pop3_state_capa(conn);
598 /* For CAPA responses */
599 static CURLcode pop3_state_capa_resp(struct connectdata *conn, int pop3code,
602 CURLcode result = CURLE_OK;
603 struct SessionHandle *data = conn->data;
604 struct pop3_conn *pop3c = &conn->proto.pop3c;
606 (void)instate; /* no use for this yet */
609 result = pop3_state_user(conn);
610 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
611 /* We don't have a SSL/TLS connection yet, but SSL is requested */
612 if(pop3c->tls_supported)
613 /* Switch to TLS connection now */
614 result = pop3_state_starttls(conn);
615 else if(data->set.use_ssl == CURLUSESSL_TRY)
616 /* Fallback and carry on with authentication */
617 result = pop3_authenticate(conn);
619 failf(data, "STLS not supported.");
620 result = CURLE_USE_SSL_FAILED;
624 result = pop3_authenticate(conn);
629 /* For STARTTLS responses */
630 static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
634 CURLcode result = CURLE_OK;
635 struct SessionHandle *data = conn->data;
637 (void)instate; /* no use for this yet */
639 if(pop3code != '+') {
640 if(data->set.use_ssl != CURLUSESSL_TRY) {
641 failf(data, "STARTTLS denied. %c", pop3code);
642 result = CURLE_USE_SSL_FAILED;
645 result = pop3_authenticate(conn);
648 result = pop3_state_upgrade_tls(conn);
653 /* For AUTH PLAIN responses */
654 static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
658 CURLcode result = CURLE_OK;
659 struct SessionHandle *data = conn->data;
661 char *plainauth = NULL;
663 (void)instate; /* no use for this yet */
665 if(pop3code != '+') {
666 failf(data, "Access denied. %c", pop3code);
667 result = CURLE_LOGIN_DENIED;
670 /* Create the authorisation message */
671 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
674 /* Send the message */
677 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);
680 state(conn, POP3_AUTH);
683 Curl_safefree(plainauth);
690 /* For AUTH LOGIN responses */
691 static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
695 CURLcode result = CURLE_OK;
696 struct SessionHandle *data = conn->data;
698 char *authuser = NULL;
700 (void)instate; /* no use for this yet */
702 if(pop3code != '+') {
703 failf(data, "Access denied: %d", pop3code);
704 result = CURLE_LOGIN_DENIED;
707 /* Create the user message */
708 result = Curl_sasl_create_login_message(data, conn->user,
714 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);
717 state(conn, POP3_AUTH_LOGIN_PASSWD);
720 Curl_safefree(authuser);
727 /* For AUTH LOGIN user entry responses */
728 static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
732 CURLcode result = CURLE_OK;
733 struct SessionHandle *data = conn->data;
735 char *authpasswd = NULL;
737 (void)instate; /* no use for this yet */
739 if(pop3code != '+') {
740 failf(data, "Access denied: %d", pop3code);
741 result = CURLE_LOGIN_DENIED;
744 /* Create the password message */
745 result = Curl_sasl_create_login_message(data, conn->passwd,
748 /* Send the password */
751 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);
754 state(conn, POP3_AUTH);
757 Curl_safefree(authpasswd);
764 #ifndef CURL_DISABLE_CRYPTO_AUTH
765 /* For AUTH CRAM-MD5 responses */
766 static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
770 CURLcode result = CURLE_OK;
771 struct SessionHandle *data = conn->data;
772 char *chlg64 = data->state.buffer;
774 char *rplyb64 = NULL;
776 (void)instate; /* no use for this yet */
778 if(pop3code != '+') {
779 failf(data, "Access denied: %d", pop3code);
780 return CURLE_LOGIN_DENIED;
783 /* Get the challenge */
784 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
787 /* Terminate the challenge */
789 for(len = strlen(chlg64); len--;)
790 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
799 /* Create the response message */
800 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
801 conn->passwd, &rplyb64, &len);
803 /* Send the response */
806 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
809 state(conn, POP3_AUTH);
812 Curl_safefree(rplyb64);
818 /* For AUTH DIGEST-MD5 challenge responses */
819 static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
823 CURLcode result = CURLE_OK;
824 struct SessionHandle *data = conn->data;
825 char *chlg64 = data->state.buffer;
827 char *rplyb64 = NULL;
829 (void)instate; /* no use for this yet */
831 if(pop3code != '+') {
832 failf(data, "Access denied: %d", pop3code);
833 return CURLE_LOGIN_DENIED;
836 /* Get the challenge */
837 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
840 /* Create the response message */
841 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
845 /* Send the response */
848 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);
851 state(conn, POP3_AUTH_DIGESTMD5_RESP);
854 Curl_safefree(rplyb64);
860 /* For AUTH DIGEST-MD5 challenge-response responses */
861 static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
865 CURLcode result = CURLE_OK;
866 struct SessionHandle *data = conn->data;
868 (void)instate; /* no use for this yet */
870 if(pop3code != '+') {
871 failf(data, "Authentication failed: %d", pop3code);
872 result = CURLE_LOGIN_DENIED;
875 /* Send an empty response */
876 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "");
879 state(conn, POP3_AUTH);
887 /* For AUTH NTLM responses */
888 static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
892 CURLcode result = CURLE_OK;
893 struct SessionHandle *data = conn->data;
895 char *type1msg = NULL;
897 (void)instate; /* no use for this yet */
899 if(pop3code != '+') {
900 failf(data, "Access denied: %d", pop3code);
901 result = CURLE_LOGIN_DENIED;
904 /* Create the type-1 message */
905 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
909 /* Send the message */
912 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);
915 state(conn, POP3_AUTH_NTLM_TYPE2MSG);
918 Curl_safefree(type1msg);
925 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
926 static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
930 CURLcode result = CURLE_OK;
931 struct SessionHandle *data = conn->data;
933 char *type3msg = NULL;
935 (void)instate; /* no use for this yet */
937 if(pop3code != '+') {
938 failf(data, "Access denied: %d", pop3code);
939 result = CURLE_LOGIN_DENIED;
942 /* Create the type-3 message */
943 result = Curl_sasl_create_ntlm_type3_message(data,
944 data->state.buffer + 2,
945 conn->user, conn->passwd,
949 /* Send the message */
952 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);
955 state(conn, POP3_AUTH);
958 Curl_safefree(type3msg);
966 /* For final responses to the AUTH sequence */
967 static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
971 CURLcode result = CURLE_OK;
972 struct SessionHandle *data = conn->data;
974 (void)instate; /* no use for this yet */
976 if(pop3code != '+') {
977 failf(data, "Authentication failed: %d", pop3code);
978 result = CURLE_LOGIN_DENIED;
981 /* End of connect phase */
982 state(conn, POP3_STOP);
987 #ifndef CURL_DISABLE_CRYPTO_AUTH
988 static CURLcode pop3_state_apop_resp(struct connectdata *conn, int pop3code,
991 CURLcode result = CURLE_OK;
992 struct SessionHandle *data = conn->data;
994 (void)instate; /* no use for this yet */
996 if(pop3code != '+') {
997 failf(data, "Authentication failed: %d", pop3code);
998 result = CURLE_LOGIN_DENIED;
1001 /* End of connect phase */
1002 state(conn, POP3_STOP);
1008 /* For USER responses */
1009 static CURLcode pop3_state_user_resp(struct connectdata *conn, int pop3code,
1012 CURLcode result = CURLE_OK;
1013 struct SessionHandle *data = conn->data;
1014 struct FTP *pop3 = data->state.proto.pop3;
1016 (void)instate; /* no use for this yet */
1018 if(pop3code != '+') {
1019 failf(data, "Access denied. %c", pop3code);
1020 result = CURLE_LOGIN_DENIED;
1023 /* Send the PASS command */
1024 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
1025 pop3->passwd ? pop3->passwd : "");
1029 state(conn, POP3_PASS);
1034 /* For PASS responses */
1035 static CURLcode pop3_state_pass_resp(struct connectdata *conn, int pop3code,
1038 CURLcode result = CURLE_OK;
1039 struct SessionHandle *data = conn->data;
1041 (void)instate; /* no use for this yet */
1043 if(pop3code != '+') {
1044 failf(data, "Access denied. %c", pop3code);
1045 result = CURLE_LOGIN_DENIED;
1048 /* End of connect phase */
1049 state(conn, POP3_STOP);
1054 /* Start the DO phase for the command */
1055 static CURLcode pop3_command(struct connectdata *conn)
1057 CURLcode result = CURLE_OK;
1058 struct pop3_conn *pop3c = &conn->proto.pop3c;
1059 const char *command = NULL;
1061 /* Calculate the default command */
1062 if(pop3c->mailbox[0] == '\0' || conn->data->set.ftp_list_only) {
1065 if(pop3c->mailbox[0] != '\0') {
1066 /* Message specific LIST so skip the BODY transfer */
1067 struct FTP *pop3 = conn->data->state.proto.pop3;
1068 pop3->transfer = FTPTRANSFER_INFO;
1074 /* Send the command */
1075 if(pop3c->mailbox[0] != '\0')
1076 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s %s",
1077 (pop3c->custom && pop3c->custom[0] != '\0' ?
1078 pop3c->custom : command), pop3c->mailbox);
1080 result = Curl_pp_sendf(&conn->proto.pop3c.pp,
1081 (pop3c->custom && pop3c->custom[0] != '\0' ?
1082 pop3c->custom : command));
1087 state(conn, POP3_COMMAND);
1092 /* For command responses */
1093 static CURLcode pop3_state_command_resp(struct connectdata *conn,
1097 CURLcode result = CURLE_OK;
1098 struct SessionHandle *data = conn->data;
1099 struct FTP *pop3 = data->state.proto.pop3;
1100 struct pop3_conn *pop3c = &conn->proto.pop3c;
1101 struct pingpong *pp = &pop3c->pp;
1103 (void)instate; /* no use for this yet */
1105 if(pop3code != '+') {
1106 state(conn, POP3_STOP);
1107 return CURLE_RECV_ERROR;
1110 /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
1111 EOB string so count this is two matching bytes. This is necessary to make
1112 the code detect the EOB if the only data than comes now is %2e CR LF like
1113 when there is no body to return. */
1116 /* But since this initial CR LF pair is not part of the actual body, we set
1117 the strip counter here so that these bytes won't be delivered. */
1121 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp,
1122 -1, NULL); /* no upload here */
1125 /* The header "cache" contains a bunch of data that is actually body
1126 content so send it as such. Note that there may even be additional
1127 "headers" after the body */
1129 if(!data->set.opt_no_body) {
1130 result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
1135 /* Free the cache */
1136 Curl_safefree(pp->cache);
1138 /* Reset the cache size */
1142 /* End of do phase */
1143 state(conn, POP3_STOP);
1148 static CURLcode pop3_statemach_act(struct connectdata *conn)
1150 CURLcode result = CURLE_OK;
1151 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1153 struct pop3_conn *pop3c = &conn->proto.pop3c;
1154 struct pingpong *pp = &pop3c->pp;
1157 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
1158 if(pop3c->state == POP3_UPGRADETLS)
1159 return pop3_state_upgrade_tls(conn);
1161 /* Flush any data that needs to be sent */
1163 return Curl_pp_flushsend(pp);
1165 /* Read the response from the server */
1166 result = Curl_pp_readresp(sock, pp, &pop3code, &nread);
1171 /* We have now received a full POP3 server response */
1172 switch(pop3c->state) {
1173 case POP3_SERVERGREET:
1174 result = pop3_state_servergreet_resp(conn, pop3code, pop3c->state);
1178 result = pop3_state_capa_resp(conn, pop3code, pop3c->state);
1182 result = pop3_state_starttls_resp(conn, pop3code, pop3c->state);
1185 case POP3_AUTH_PLAIN:
1186 result = pop3_state_auth_plain_resp(conn, pop3code, pop3c->state);
1189 case POP3_AUTH_LOGIN:
1190 result = pop3_state_auth_login_resp(conn, pop3code, pop3c->state);
1193 case POP3_AUTH_LOGIN_PASSWD:
1194 result = pop3_state_auth_login_password_resp(conn, pop3code,
1198 #ifndef CURL_DISABLE_CRYPTO_AUTH
1199 case POP3_AUTH_CRAMMD5:
1200 result = pop3_state_auth_cram_resp(conn, pop3code, pop3c->state);
1203 case POP3_AUTH_DIGESTMD5:
1204 result = pop3_state_auth_digest_resp(conn, pop3code, pop3c->state);
1207 case POP3_AUTH_DIGESTMD5_RESP:
1208 result = pop3_state_auth_digest_resp_resp(conn, pop3code, pop3c->state);
1213 case POP3_AUTH_NTLM:
1214 result = pop3_state_auth_ntlm_resp(conn, pop3code, pop3c->state);
1217 case POP3_AUTH_NTLM_TYPE2MSG:
1218 result = pop3_state_auth_ntlm_type2msg_resp(conn, pop3code,
1224 result = pop3_state_auth_final_resp(conn, pop3code, pop3c->state);
1227 #ifndef CURL_DISABLE_CRYPTO_AUTH
1229 result = pop3_state_apop_resp(conn, pop3code, pop3c->state);
1234 result = pop3_state_user_resp(conn, pop3code, pop3c->state);
1238 result = pop3_state_pass_resp(conn, pop3code, pop3c->state);
1242 result = pop3_state_command_resp(conn, pop3code, pop3c->state);
1246 /* fallthrough, just stop! */
1248 /* internal error */
1249 state(conn, POP3_STOP);
1257 /* Called repeatedly until done from multi.c */
1258 static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done)
1260 CURLcode result = CURLE_OK;
1261 struct pop3_conn *pop3c = &conn->proto.pop3c;
1263 if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone)
1264 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &pop3c->ssldone);
1266 result = Curl_pp_statemach(&pop3c->pp, FALSE);
1268 *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE;
1273 static CURLcode pop3_block_statemach(struct connectdata *conn)
1275 CURLcode result = CURLE_OK;
1276 struct pop3_conn *pop3c = &conn->proto.pop3c;
1278 while(pop3c->state != POP3_STOP) {
1279 result = Curl_pp_statemach(&pop3c->pp, TRUE);
1287 /* Allocate and initialize the POP3 struct for the current SessionHandle if
1289 static CURLcode pop3_init(struct connectdata *conn)
1291 struct SessionHandle *data = conn->data;
1292 struct FTP *pop3 = data->state.proto.pop3;
1295 pop3 = data->state.proto.pop3 = calloc(sizeof(struct FTP), 1);
1297 return CURLE_OUT_OF_MEMORY;
1300 /* Get some initial data into the pop3 struct */
1301 pop3->bytecountp = &data->req.bytecount;
1303 /* No need to duplicate user+password, the connectdata struct won't change
1304 during a session, but we re-init them here since on subsequent inits
1305 since the conn struct may have changed or been replaced.
1307 pop3->user = conn->user;
1308 pop3->passwd = conn->passwd;
1313 /* For the POP3 "protocol connect" and "doing" phases only */
1314 static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
1317 return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
1320 /***********************************************************************
1324 * This function should do everything that is to be considered a part of the
1327 * The variable 'done' points to will be TRUE if the protocol-layer connect
1328 * phase is done when this function returns, or FALSE is not. When called as
1329 * a part of the easy interface, it will always be TRUE.
1331 static CURLcode pop3_connect(struct connectdata *conn, bool *done)
1333 CURLcode result = CURLE_OK;
1334 struct pop3_conn *pop3c = &conn->proto.pop3c;
1335 struct pingpong *pp = &pop3c->pp;
1337 *done = FALSE; /* default to not done yet */
1339 /* If there already is a protocol-specific struct allocated for this
1340 sessionhandle, deal with it */
1341 Curl_reset_reqproto(conn);
1343 /* Initialise the POP3 layer */
1344 result = pop3_init(conn);
1348 /* We always support persistent connections in POP3 */
1349 conn->bits.close = FALSE;
1351 /* Set the default response time-out */
1352 pp->response_time = RESP_TIMEOUT;
1353 pp->statemach_act = pop3_statemach_act;
1354 pp->endofresp = pop3_endofresp;
1357 /* Initialise the pingpong layer */
1360 /* Start off waiting for the server greeting response */
1361 state(conn, POP3_SERVERGREET);
1363 result = pop3_multi_statemach(conn, done);
1368 /***********************************************************************
1372 * The DONE function. This does what needs to be done after a single DO has
1375 * Input argument is already checked for validity.
1377 static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
1380 CURLcode result = CURLE_OK;
1381 struct SessionHandle *data = conn->data;
1382 struct FTP *pop3 = data->state.proto.pop3;
1383 struct pop3_conn *pop3c = &conn->proto.pop3c;
1388 /* When the easy handle is removed from the multi while libcurl is still
1389 * trying to resolve the host name, it seems that the POP3 struct is not
1390 * yet initialized, but the removal action calls Curl_done() which calls
1391 * this function. So we simply return success if no POP3 pointer is set.
1396 conn->bits.close = TRUE; /* marked for closure */
1397 result = status; /* use the already set error code */
1400 /* Cleanup our do based variables */
1401 Curl_safefree(pop3c->mailbox);
1402 Curl_safefree(pop3c->custom);
1404 /* Clear the transfer mode for the next connection */
1405 pop3->transfer = FTPTRANSFER_BODY;
1410 /***********************************************************************
1414 * This is the actual DO function for POP3. Get a file/directory according to
1415 * the options previously setup.
1417 static CURLcode pop3_perform(struct connectdata *conn, bool *connected,
1420 /* This is POP3 and no proxy */
1421 CURLcode result = CURLE_OK;
1423 DEBUGF(infof(conn->data, "DO phase starts\n"));
1425 if(conn->data->set.opt_no_body) {
1426 /* Requested no body means no transfer */
1427 struct FTP *pop3 = conn->data->state.proto.pop3;
1428 pop3->transfer = FTPTRANSFER_INFO;
1431 *dophase_done = FALSE; /* not done yet */
1433 /* Start the first command in the DO phase */
1434 result = pop3_command(conn);
1438 /* Run the state-machine */
1439 result = pop3_multi_statemach(conn, dophase_done);
1441 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1444 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1449 /***********************************************************************
1453 * This function is registered as 'curl_do' function. It decodes the path
1454 * parts etc as a wrapper to the actual DO function (pop3_perform).
1456 * The input argument is already checked for validity.
1458 static CURLcode pop3_do(struct connectdata *conn, bool *done)
1460 CURLcode result = CURLE_OK;
1462 *done = FALSE; /* default to false */
1465 Since connections can be re-used between SessionHandles, this might be a
1466 connection already existing but on a fresh SessionHandle struct so we must
1467 make sure we have a good 'struct POP3' to play with. For new connections,
1468 the struct POP3 is allocated and setup in the pop3_connect() function.
1470 Curl_reset_reqproto(conn);
1471 result = pop3_init(conn);
1475 /* Parse the URL path */
1476 result = pop3_parse_url_path(conn);
1480 /* Parse the custom request */
1481 result = pop3_parse_custom_request(conn);
1485 result = pop3_regular_transfer(conn, done);
1490 /***********************************************************************
1494 * This should be called before calling sclose(). We should then wait for the
1495 * response from the server before returning. The calling code should then try
1496 * to close the connection.
1498 static CURLcode pop3_quit(struct connectdata *conn)
1500 CURLcode result = CURLE_OK;
1502 result = Curl_pp_sendf(&conn->proto.pop3c.pp, "QUIT", NULL);
1506 state(conn, POP3_QUIT);
1508 result = pop3_block_statemach(conn);
1513 /***********************************************************************
1517 * Disconnect from an POP3 server. Cleanup protocol-specific per-connection
1518 * resources. BLOCKING.
1520 static CURLcode pop3_disconnect(struct connectdata *conn,
1521 bool dead_connection)
1523 struct pop3_conn *pop3c = &conn->proto.pop3c;
1525 /* We cannot send quit unconditionally. If this connection is stale or
1526 bad in any way, sending quit and waiting around here will make the
1527 disconnect wait in vain and cause more problems than we need to */
1529 /* The POP3 session may or may not have been allocated/setup at this
1531 if(!dead_connection && pop3c->pp.conn)
1532 (void)pop3_quit(conn); /* ignore errors on the LOGOUT */
1534 /* Disconnect from the server */
1535 Curl_pp_disconnect(&pop3c->pp);
1537 /* Cleanup the SASL module */
1538 Curl_sasl_cleanup(conn, pop3c->authused);
1540 /* Cleanup our connection based variables */
1541 Curl_safefree(pop3c->apoptimestamp);
1546 /***********************************************************************
1548 * pop3_parse_url_path()
1550 * Parse the URL path into separate path components.
1552 static CURLcode pop3_parse_url_path(struct connectdata *conn)
1554 /* The POP3 struct is already initialised in pop3_connect() */
1555 struct pop3_conn *pop3c = &conn->proto.pop3c;
1556 struct SessionHandle *data = conn->data;
1557 const char *path = data->state.path;
1559 /* URL decode the path and use this mailbox */
1560 return Curl_urldecode(data, path, 0, &pop3c->mailbox, NULL, TRUE);
1563 static CURLcode pop3_parse_custom_request(struct connectdata *conn)
1565 CURLcode result = CURLE_OK;
1566 struct pop3_conn *pop3c = &conn->proto.pop3c;
1567 struct SessionHandle *data = conn->data;
1568 const char *custom = conn->data->set.str[STRING_CUSTOMREQUEST];
1570 /* URL decode the custom request */
1572 result = Curl_urldecode(data, custom, 0, &pop3c->custom, NULL, TRUE);
1577 /* Call this when the DO phase has completed */
1578 static CURLcode pop3_dophase_done(struct connectdata *conn, bool connected)
1580 struct FTP *pop3 = conn->data->state.proto.pop3;
1584 if(pop3->transfer != FTPTRANSFER_BODY)
1585 /* no data to transfer */
1586 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1591 /* Called from multi.c while DOing */
1592 static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done)
1594 CURLcode result = pop3_multi_statemach(conn, dophase_done);
1597 DEBUGF(infof(conn->data, "DO phase failed\n"));
1600 result = pop3_dophase_done(conn, FALSE /* not connected */);
1602 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1609 /***********************************************************************
1611 * pop3_regular_transfer()
1613 * The input argument is already checked for validity.
1615 * Performs all commands done before a regular transfer between a local and a
1618 static CURLcode pop3_regular_transfer(struct connectdata *conn,
1621 CURLcode result = CURLE_OK;
1622 bool connected = FALSE;
1623 struct SessionHandle *data = conn->data;
1625 /* Make sure size is unknown at this point */
1626 data->req.size = -1;
1628 Curl_pgrsSetUploadCounter(data, 0);
1629 Curl_pgrsSetDownloadCounter(data, 0);
1630 Curl_pgrsSetUploadSize(data, 0);
1631 Curl_pgrsSetDownloadSize(data, 0);
1633 result = pop3_perform(conn, &connected, dophase_done);
1637 /* The DO phase has not completed yet */
1640 result = pop3_dophase_done(conn, connected);
1646 static CURLcode pop3_setup_connection(struct connectdata * conn)
1648 struct SessionHandle *data = conn->data;
1650 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1651 /* Unless we have asked to tunnel POP3 operations through the proxy, we
1652 switch and use HTTP operations only */
1653 #ifndef CURL_DISABLE_HTTP
1654 if(conn->handler == &Curl_handler_pop3)
1655 conn->handler = &Curl_handler_pop3_proxy;
1658 conn->handler = &Curl_handler_pop3s_proxy;
1660 failf(data, "POP3S not supported!");
1661 return CURLE_UNSUPPORTED_PROTOCOL;
1665 /* We explicitly mark this connection as persistent here as we're doing
1666 POP3 over HTTP and thus we accidentally avoid setting this value
1668 conn->bits.close = FALSE;
1670 failf(data, "POP3 over http proxy requires HTTP support built-in!");
1671 return CURLE_UNSUPPORTED_PROTOCOL;
1675 data->state.path++; /* don't include the initial slash */
1680 /* This function scans the body after the end-of-body and writes everything
1681 until the end is found */
1682 CURLcode Curl_pop3_write(struct connectdata *conn, char *str, size_t nread)
1684 /* This code could be made into a special function in the handler struct */
1685 CURLcode result = CURLE_OK;
1686 struct SessionHandle *data = conn->data;
1687 struct SingleRequest *k = &data->req;
1689 struct pop3_conn *pop3c = &conn->proto.pop3c;
1690 bool strip_dot = FALSE;
1694 /* Search through the buffer looking for the end-of-body marker which is
1695 5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
1696 the eob so the server will have prefixed it with an extra dot which we
1697 need to strip out. Additionally the marker could of course be spread out
1698 over 5 different data chunks */
1699 for(i = 0; i < nread; i++) {
1700 size_t prev = pop3c->eob;
1704 if(pop3c->eob == 0) {
1708 /* Write out the body part that didn't match */
1709 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1718 else if(pop3c->eob == 3)
1721 /* If the character match wasn't at position 0 or 3 then restart the
1727 if(pop3c->eob == 1 || pop3c->eob == 4)
1730 /* If the character match wasn't at position 1 or 4 then start the
1738 else if(pop3c->eob == 3) {
1739 /* We have an extra dot after the CRLF which we need to strip off */
1744 /* If the character match wasn't at position 2 then start the search
1754 /* Did we have a partial match which has subsequently failed? */
1755 if(prev && prev >= pop3c->eob) {
1756 /* Strip can only be non-zero for the very first mismatch after CRLF
1757 and then both prev and strip are equal and nothing will be output
1759 while(prev && pop3c->strip) {
1765 /* If the partial match was the CRLF and dot then only write the CRLF
1766 as the server would have inserted the dot */
1767 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char*)POP3_EOB,
1768 strip_dot ? prev - 1 : prev);
1779 if(pop3c->eob == POP3_EOB_LEN) {
1780 /* We have a full match so the transfer is done, however we must transfer
1781 the CRLF at the start of the EOB as this is considered to be part of the
1782 message as per RFC-1939, sect. 3 */
1783 result = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
1785 k->keepon &= ~KEEP_RECV;
1792 /* While EOB is matching nothing should be output */
1796 result = Curl_client_write(conn, CLIENTWRITE_BODY, &str[last],
1803 #endif /* CURL_DISABLE_POP3 */