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 * RFC2195 CRAM-MD5 authentication
22 * RFC2222 Simple Authentication and Security Layer (SASL)
23 * RFC2595 Using TLS with IMAP, POP3 and ACAP
24 * RFC2831 DIGEST-MD5 authentication
25 * RFC3501 IMAPv4 protocol
26 * RFC4616 PLAIN authentication
27 * RFC5092 IMAP URL Scheme
29 ***************************************************************************/
31 #include "curl_setup.h"
33 #ifndef CURL_DISABLE_IMAP
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
38 #ifdef HAVE_ARPA_INET_H
39 #include <arpa/inet.h>
42 #include <sys/utsname.h>
52 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
54 #define in_addr_t unsigned long
57 #include <curl/curl.h>
65 #include "http.h" /* for HTTP proxy tunnel stuff */
69 #include "strtoofft.h"
78 #include "curl_sasl.h"
80 #define _MPRINTF_REPLACE /* use our functions only */
81 #include <curl/mprintf.h>
83 #include "curl_memory.h"
84 /* The last #include file should be: */
87 /* Local API functions */
88 static CURLcode imap_parse_url_path(struct connectdata *conn);
89 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
90 static CURLcode imap_do(struct connectdata *conn, bool *done);
91 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
93 static CURLcode imap_connect(struct connectdata *conn, bool *done);
94 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
95 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
96 static int imap_getsock(struct connectdata *conn,
99 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode imap_setup_connection(struct connectdata *conn);
101 static CURLcode imap_state_upgrade_tls(struct connectdata *conn);
104 * IMAP protocol handler.
107 const struct Curl_handler Curl_handler_imap = {
109 imap_setup_connection, /* setup_connection */
111 imap_done, /* done */
112 ZERO_NULL, /* do_more */
113 imap_connect, /* connect_it */
114 imap_multi_statemach, /* connecting */
115 imap_doing, /* doing */
116 imap_getsock, /* proto_getsock */
117 imap_getsock, /* doing_getsock */
118 ZERO_NULL, /* domore_getsock */
119 ZERO_NULL, /* perform_getsock */
120 imap_disconnect, /* disconnect */
121 ZERO_NULL, /* readwrite */
122 PORT_IMAP, /* defport */
123 CURLPROTO_IMAP, /* protocol */
124 PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
125 | PROTOPT_NOURLQUERY /* flags */
131 * IMAPS protocol handler.
134 const struct Curl_handler Curl_handler_imaps = {
135 "IMAPS", /* scheme */
136 imap_setup_connection, /* setup_connection */
138 imap_done, /* done */
139 ZERO_NULL, /* do_more */
140 imap_connect, /* connect_it */
141 imap_multi_statemach, /* connecting */
142 imap_doing, /* doing */
143 imap_getsock, /* proto_getsock */
144 imap_getsock, /* doing_getsock */
145 ZERO_NULL, /* domore_getsock */
146 ZERO_NULL, /* perform_getsock */
147 imap_disconnect, /* disconnect */
148 ZERO_NULL, /* readwrite */
149 PORT_IMAPS, /* defport */
150 CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
151 PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
152 | PROTOPT_NOURLQUERY /* flags */
156 #ifndef CURL_DISABLE_HTTP
158 * HTTP-proxyed IMAP protocol handler.
161 static const struct Curl_handler Curl_handler_imap_proxy = {
163 ZERO_NULL, /* setup_connection */
164 Curl_http, /* do_it */
165 Curl_http_done, /* done */
166 ZERO_NULL, /* do_more */
167 ZERO_NULL, /* connect_it */
168 ZERO_NULL, /* connecting */
169 ZERO_NULL, /* doing */
170 ZERO_NULL, /* proto_getsock */
171 ZERO_NULL, /* doing_getsock */
172 ZERO_NULL, /* domore_getsock */
173 ZERO_NULL, /* perform_getsock */
174 ZERO_NULL, /* disconnect */
175 ZERO_NULL, /* readwrite */
176 PORT_IMAP, /* defport */
177 CURLPROTO_HTTP, /* protocol */
178 PROTOPT_NONE /* flags */
184 * HTTP-proxyed IMAPS protocol handler.
187 static const struct Curl_handler Curl_handler_imaps_proxy = {
188 "IMAPS", /* scheme */
189 ZERO_NULL, /* setup_connection */
190 Curl_http, /* do_it */
191 Curl_http_done, /* done */
192 ZERO_NULL, /* do_more */
193 ZERO_NULL, /* connect_it */
194 ZERO_NULL, /* connecting */
195 ZERO_NULL, /* doing */
196 ZERO_NULL, /* proto_getsock */
197 ZERO_NULL, /* doing_getsock */
198 ZERO_NULL, /* domore_getsock */
199 ZERO_NULL, /* perform_getsock */
200 ZERO_NULL, /* disconnect */
201 ZERO_NULL, /* readwrite */
202 PORT_IMAPS, /* defport */
203 CURLPROTO_HTTP, /* protocol */
204 PROTOPT_NONE /* flags */
209 /***********************************************************************
213 * Sends the formated string as an IMAP command to the server.
215 * Designed to never block.
217 static CURLcode imap_sendf(struct connectdata *conn,
218 const char *idstr, /* command id to wait for */
219 const char *fmt, ...)
222 struct imap_conn *imapc = &conn->proto.imapc;
226 imapc->idstr = idstr;
228 res = Curl_pp_vsendf(&imapc->pp, fmt, ap);
235 static const char *getcmdid(struct connectdata *conn)
237 static const char * const ids[]= {
244 struct imap_conn *imapc = &conn->proto.imapc;
246 /* Get the next id, but wrap at end of table */
247 imapc->cmdid = (int)((imapc->cmdid + 1) % (sizeof(ids) / sizeof(ids[0])));
249 return ids[imapc->cmdid];
252 /***********************************************************************
256 * Checks the input string for characters that need escaping and returns an
257 * atom ready for sending to the server.
259 * The returned string needs to be freed.
262 static char* imap_atom(const char* str)
266 size_t backsp_count = 0;
267 size_t quote_count = 0;
268 bool space_exists = FALSE;
275 /* Count any unescapped characters */
288 /* Does the input contain any unescapped characters? */
289 if(!backsp_count && !quote_count && !space_exists)
292 /* Calculate the new string length */
293 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
295 /* Allocate the new string */
296 newstr = (char *) malloc((newlen + 1) * sizeof(char));
300 /* Surround the string in quotes if necessary */
304 newstr[newlen - 1] = '"';
308 /* Copy the string, escaping backslash and quote characters along the way */
311 if(*p1 == '\\' || *p1 == '"') {
322 /* Terminate the string */
323 newstr[newlen] = '\0';
328 /* Function that checks for an ending imap status code at the start of the
329 given string but also detects the supported mechanisms from the CAPABILITY
331 static int imap_endofresp(struct pingpong *pp, int *resp)
333 char *line = pp->linestart_resp;
334 size_t len = pp->nread_resp;
335 struct imap_conn *imapc = &pp->conn->proto.imapc;
336 const char *id = imapc->idstr;
337 size_t id_len = strlen(id);
340 /* Do we have a generic command response? */
341 if(len >= id_len + 3) {
342 if(!memcmp(id, line, id_len) && line[id_len] == ' ') {
343 *resp = line[id_len + 1]; /* O, N or B */
348 /* Do we have a generic continuation response? */
349 if((len == 3 && !memcmp("+", line, 1)) ||
350 (len >= 2 && !memcmp("+ ", line, 2))) {
355 /* Are we processing CAPABILITY command responses? */
356 if(imapc->state == IMAP_CAPABILITY) {
357 /* Do we have a valid response? */
358 if(len >= 2 && !memcmp("* ", line, 2)) {
362 /* Loop through the data line */
365 (*line == ' ' || *line == '\t' ||
366 *line == '\r' || *line == '\n')) {
378 /* Extract the word */
379 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
380 line[wordlen] != '\t' && line[wordlen] != '\r' &&
381 line[wordlen] != '\n';)
384 /* Do we have an AUTH capability? */
385 if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
390 /* Test the word for a matching authentication mechanism */
391 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
392 imapc->authmechs |= SASL_MECH_LOGIN;
393 if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
394 imapc->authmechs |= SASL_MECH_PLAIN;
395 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
396 imapc->authmechs |= SASL_MECH_CRAM_MD5;
397 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
398 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
399 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
400 imapc->authmechs |= SASL_MECH_GSSAPI;
401 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
402 imapc->authmechs |= SASL_MECH_EXTERNAL;
403 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
404 imapc->authmechs |= SASL_MECH_NTLM;
413 /* Are we processing FETCH command responses? */
414 if(imapc->state == IMAP_FETCH) {
415 /* Do we have a valid response? */
416 if(len >= 2 && !memcmp("* ", line, 2)) {
422 return FALSE; /* Nothing for us */
425 /* This is the ONLY way to change IMAP state! */
426 static void state(struct connectdata *conn,
429 struct imap_conn *imapc = &conn->proto.imapc;
430 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
431 /* for debug purposes */
432 static const char * const names[]={
438 "AUTHENTICATE_PLAIN",
439 "AUTHENTICATE_LOGIN",
440 "AUTHENTICATE_LOGIN_PASSWD",
441 "AUTHENTICATE_CRAMMD5",
442 "AUTHENTICATE_DIGESTMD5",
443 "AUTHENTICATE_DIGESTMD5_RESP",
445 "AUTHENTICATE_NTLM_TYPE2MSG",
454 if(imapc->state != newstate)
455 infof(conn->data, "IMAP %p state change from %s to %s\n",
456 imapc, names[imapc->state], names[newstate]);
459 imapc->state = newstate;
462 static CURLcode imap_state_capability(struct connectdata *conn)
464 CURLcode result = CURLE_OK;
465 struct imap_conn *imapc = &conn->proto.imapc;
468 imapc->authmechs = 0; /* No known authentication mechanisms yet */
469 imapc->authused = 0; /* Clear the authentication mechanism used */
471 /* Check we have a username and password to authenticate with and end the
472 connect phase if we don't */
473 if(!conn->bits.user_passwd) {
474 state(conn, IMAP_STOP);
479 str = getcmdid(conn);
481 /* Send the CAPABILITY command */
482 result = imap_sendf(conn, str, "%s CAPABILITY", str);
487 state(conn, IMAP_CAPABILITY);
492 static CURLcode imap_state_login(struct connectdata *conn)
495 struct FTP *imap = conn->data->state.proto.imap;
496 const char *str = getcmdid(conn);
497 char *user = imap_atom(imap->user);
498 char *passwd = imap_atom(imap->passwd);
500 /* send USER and password */
501 result = imap_sendf(conn, str, "%s LOGIN %s %s", str,
502 user ? user : "", passwd ? passwd : "");
505 Curl_safefree(passwd);
510 state(conn, IMAP_LOGIN);
515 static CURLcode imap_authenticate(struct connectdata *conn)
517 CURLcode result = CURLE_OK;
518 struct imap_conn *imapc = &conn->proto.imapc;
519 const char *mech = NULL;
520 imapstate authstate = IMAP_STOP;
522 /* Check supported authentication mechanisms by decreasing order of
524 #ifndef CURL_DISABLE_CRYPTO_AUTH
525 if(imapc->authmechs & SASL_MECH_DIGEST_MD5) {
527 authstate = IMAP_AUTHENTICATE_DIGESTMD5;
528 imapc->authused = SASL_MECH_DIGEST_MD5;
530 else if(imapc->authmechs & SASL_MECH_CRAM_MD5) {
532 authstate = IMAP_AUTHENTICATE_CRAMMD5;
533 imapc->authused = SASL_MECH_CRAM_MD5;
538 if(imapc->authmechs & SASL_MECH_NTLM) {
540 authstate = IMAP_AUTHENTICATE_NTLM;
541 imapc->authused = SASL_MECH_NTLM;
545 if(imapc->authmechs & SASL_MECH_LOGIN) {
547 authstate = IMAP_AUTHENTICATE_LOGIN;
548 imapc->authused = SASL_MECH_LOGIN;
550 else if(imapc->authmechs & SASL_MECH_PLAIN) {
552 authstate = IMAP_AUTHENTICATE_PLAIN;
553 imapc->authused = SASL_MECH_PLAIN;
556 infof(conn->data, "No known authentication mechanisms supported!\n");
557 result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
561 const char *str = getcmdid(conn);
563 result = imap_sendf(conn, str, "%s AUTHENTICATE %s", str, mech);
566 state(conn, authstate);
572 /* For the IMAP "protocol connect" and "doing" phases only */
573 static int imap_getsock(struct connectdata *conn,
574 curl_socket_t *socks,
577 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
581 static void imap_to_imaps(struct connectdata *conn)
583 conn->handler = &Curl_handler_imaps;
586 #define imap_to_imaps(x) Curl_nop_stmt
589 /* For the initial server greeting */
590 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
594 CURLcode result = CURLE_OK;
595 struct SessionHandle *data = conn->data;
597 (void)instate; /* no use for this yet */
599 if(imapcode != 'O') {
600 failf(data, "Got unexpected imap-server response");
601 return CURLE_FTP_WEIRD_SERVER_REPLY;
604 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
605 /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
606 to TLS connection now */
607 const char *str = getcmdid(conn);
608 result = imap_sendf(conn, str, "%s STARTTLS", str);
609 state(conn, IMAP_STARTTLS);
612 result = imap_state_capability(conn);
617 /* For STARTTLS responses */
618 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
622 CURLcode result = CURLE_OK;
623 struct SessionHandle *data = conn->data;
625 (void)instate; /* no use for this yet */
627 if(imapcode != 'O') {
628 if(data->set.use_ssl != CURLUSESSL_TRY) {
629 failf(data, "STARTTLS denied. %c", imapcode);
630 result = CURLE_USE_SSL_FAILED;
633 result = imap_state_capability(conn);
636 if(data->state.used_interface == Curl_if_multi) {
637 state(conn, IMAP_UPGRADETLS);
638 result = imap_state_upgrade_tls(conn);
641 result = Curl_ssl_connect(conn, FIRSTSOCKET);
642 if(CURLE_OK == result) {
644 result = imap_state_capability(conn);
652 static CURLcode imap_state_upgrade_tls(struct connectdata *conn)
654 struct imap_conn *imapc = &conn->proto.imapc;
657 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
661 result = imap_state_capability(conn);
667 /* For CAPABILITY responses */
668 static CURLcode imap_state_capability_resp(struct connectdata *conn,
672 CURLcode result = CURLE_OK;
673 struct imap_conn *imapc = &conn->proto.imapc;
675 (void)instate; /* no use for this yet */
677 if(imapcode == 'O' && imapc->authmechs)
678 result = imap_authenticate(conn);
680 result = imap_state_login(conn);
685 /* For AUTHENTICATE PLAIN responses */
686 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
690 CURLcode result = CURLE_OK;
691 struct SessionHandle *data = conn->data;
693 char *plainauth = NULL;
695 (void)instate; /* no use for this yet */
697 if(imapcode != '+') {
698 failf(data, "Access denied. %c", imapcode);
699 result = CURLE_LOGIN_DENIED;
702 /* Create the authorisation message */
703 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
706 /* Send the message */
709 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
712 state(conn, IMAP_AUTHENTICATE);
715 Curl_safefree(plainauth);
722 /* For AUTHENTICATE LOGIN responses */
723 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
727 CURLcode result = CURLE_OK;
728 struct SessionHandle *data = conn->data;
730 char *authuser = NULL;
732 (void)instate; /* no use for this yet */
734 if(imapcode != '+') {
735 failf(data, "Access denied: %d", imapcode);
736 result = CURLE_LOGIN_DENIED;
739 /* Create the user message */
740 result = Curl_sasl_create_login_message(data, conn->user,
746 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
749 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
752 Curl_safefree(authuser);
759 /* For AUTHENTICATE LOGIN user entry responses */
760 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
764 CURLcode result = CURLE_OK;
765 struct SessionHandle *data = conn->data;
767 char *authpasswd = NULL;
769 (void)instate; /* no use for this yet */
771 if(imapcode != '+') {
772 failf(data, "Access denied: %d", imapcode);
773 result = CURLE_LOGIN_DENIED;
776 /* Create the password message */
777 result = Curl_sasl_create_login_message(data, conn->passwd,
780 /* Send the password */
783 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
786 state(conn, IMAP_AUTHENTICATE);
789 Curl_safefree(authpasswd);
796 #ifndef CURL_DISABLE_CRYPTO_AUTH
797 /* For AUTHENTICATE CRAM-MD5 responses */
798 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
802 CURLcode result = CURLE_OK;
803 struct SessionHandle *data = conn->data;
804 char *chlg64 = data->state.buffer;
806 char *rplyb64 = NULL;
808 (void)instate; /* no use for this yet */
810 if(imapcode != '+') {
811 failf(data, "Access denied: %d", imapcode);
812 return CURLE_LOGIN_DENIED;
815 /* Get the challenge */
816 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
819 /* Terminate the challenge */
821 for(len = strlen(chlg64); len--;)
822 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
831 /* Create the response message */
832 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
833 conn->passwd, &rplyb64, &len);
835 /* Send the response */
838 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
841 state(conn, IMAP_AUTHENTICATE);
844 Curl_safefree(rplyb64);
850 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
851 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
855 CURLcode result = CURLE_OK;
856 struct SessionHandle *data = conn->data;
857 char *chlg64 = data->state.buffer;
859 char *rplyb64 = NULL;
861 (void)instate; /* no use for this yet */
863 if(imapcode != '+') {
864 failf(data, "Access denied: %d", imapcode);
865 return CURLE_LOGIN_DENIED;
868 /* Get the challenge */
869 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
872 /* Create the response message */
873 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
874 conn->passwd, "imap",
877 /* Send the response */
880 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
883 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
886 Curl_safefree(rplyb64);
892 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
893 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
897 CURLcode result = CURLE_OK;
898 struct SessionHandle *data = conn->data;
900 (void)instate; /* no use for this yet */
902 if(imapcode != '+') {
903 failf(data, "Authentication failed: %d", imapcode);
904 result = CURLE_LOGIN_DENIED;
907 /* Send an empty response */
908 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
911 state(conn, IMAP_AUTHENTICATE);
919 /* For AUTHENTICATE NTLM responses */
920 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
924 CURLcode result = CURLE_OK;
925 struct SessionHandle *data = conn->data;
927 char *type1msg = NULL;
929 (void)instate; /* no use for this yet */
931 if(imapcode != '+') {
932 failf(data, "Access denied: %d", imapcode);
933 result = CURLE_LOGIN_DENIED;
936 /* Create the type-1 message */
937 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
941 /* Send the message */
944 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
947 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
950 Curl_safefree(type1msg);
957 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
958 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
962 CURLcode result = CURLE_OK;
963 struct SessionHandle *data = conn->data;
965 char *type3msg = NULL;
967 (void)instate; /* no use for this yet */
969 if(imapcode != '+') {
970 failf(data, "Access denied: %d", imapcode);
971 result = CURLE_LOGIN_DENIED;
974 /* Create the type-3 message */
975 result = Curl_sasl_create_ntlm_type3_message(data,
976 data->state.buffer + 2,
977 conn->user, conn->passwd,
981 /* Send the message */
984 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
987 state(conn, IMAP_AUTHENTICATE);
990 Curl_safefree(type3msg);
998 /* For final responses to the AUTHENTICATE sequence */
999 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1003 CURLcode result = CURLE_OK;
1004 struct SessionHandle *data = conn->data;
1006 (void)instate; /* no use for this yet */
1008 if(imapcode != 'O') {
1009 failf(data, "Authentication failed: %d", imapcode);
1010 result = CURLE_LOGIN_DENIED;
1013 /* End of connect phase */
1014 state(conn, IMAP_STOP);
1019 /* For LOGIN responses */
1020 static CURLcode imap_state_login_resp(struct connectdata *conn,
1024 CURLcode result = CURLE_OK;
1025 struct SessionHandle *data = conn->data;
1027 (void)instate; /* no use for this yet */
1029 if(imapcode != 'O') {
1030 failf(data, "Access denied. %c", imapcode);
1031 result = CURLE_LOGIN_DENIED;
1034 /* End of connect phase */
1035 state(conn, IMAP_STOP);
1040 /* Start the DO phase */
1041 static CURLcode imap_select(struct connectdata *conn)
1043 CURLcode result = CURLE_OK;
1044 struct imap_conn *imapc = &conn->proto.imapc;
1045 const char *str = getcmdid(conn);
1047 result = imap_sendf(conn, str, "%s SELECT %s", str,
1048 imapc->mailbox?imapc->mailbox:"");
1052 state(conn, IMAP_SELECT);
1057 static CURLcode imap_fetch(struct connectdata *conn)
1059 CURLcode result = CURLE_OK;
1060 const char *str = getcmdid(conn);
1062 /* TODO: make this select the correct mail
1063 * Use "1 body[text]" to get the full mail body of mail 1
1065 result = imap_sendf(conn, str, "%s FETCH 1 BODY[TEXT]", str);
1070 * When issued, the server will respond with a single line similar to
1071 * '* 1 FETCH (BODY[TEXT] {2021}'
1073 * Identifying the fetch and how many bytes of contents we can expect. We
1074 * must extract that number before continuing to "download as usual".
1077 state(conn, IMAP_FETCH);
1082 /* For SELECT responses */
1083 static CURLcode imap_state_select_resp(struct connectdata *conn,
1087 CURLcode result = CURLE_OK;
1088 struct SessionHandle *data = conn->data;
1090 (void)instate; /* no use for this yet */
1092 if(imapcode != 'O') {
1093 failf(data, "Select failed");
1094 result = CURLE_LOGIN_DENIED;
1097 result = imap_fetch(conn);
1102 /* For the (first line of) FETCH BODY[TEXT] response */
1103 static CURLcode imap_state_fetch_resp(struct connectdata *conn,
1107 CURLcode result = CURLE_OK;
1108 struct SessionHandle *data = conn->data;
1109 struct imap_conn *imapc = &conn->proto.imapc;
1110 struct FTP *imap = data->state.proto.imap;
1111 struct pingpong *pp = &imapc->pp;
1112 const char *ptr = data->state.buffer;
1114 (void)instate; /* no use for this yet */
1116 if('*' != imapcode) {
1117 Curl_pgrsSetDownloadSize(data, 0);
1118 state(conn, IMAP_STOP);
1122 /* Something like this comes "* 1 FETCH (BODY[TEXT] {2021}\r" */
1123 while(*ptr && (*ptr != '{'))
1127 curl_off_t filesize = curlx_strtoofft(ptr + 1, NULL, 10);
1129 Curl_pgrsSetDownloadSize(data, filesize);
1131 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", filesize);
1134 /* At this point there is a bunch of data in the header "cache" that is
1135 actually body content, send it as body and then skip it. Do note
1136 that there may even be additional "headers" after the body. */
1137 size_t chunk = pp->cache_size;
1139 if(chunk > (size_t)filesize)
1140 /* the conversion from curl_off_t to size_t is always fine here */
1141 chunk = (size_t)filesize;
1143 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1149 /* we've now used parts of or the entire cache */
1150 if(pp->cache_size > chunk) {
1151 /* part of, move the trailing data to the start and reduce the size */
1152 memmove(pp->cache, pp->cache+chunk,
1153 pp->cache_size - chunk);
1154 pp->cache_size -= chunk;
1157 /* cache is drained */
1158 Curl_safefree(pp->cache);
1164 infof(data, "Filesize left: %" FORMAT_OFF_T "\n", filesize);
1167 /* the entire data is already transferred! */
1168 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1171 Curl_setup_transfer(conn, FIRSTSOCKET, filesize, FALSE,
1172 imap->bytecountp, -1, NULL); /* no upload here */
1174 data->req.maxdownload = filesize;
1177 /* We don't know how to parse this line */
1178 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1180 /* End of do phase */
1181 state(conn, IMAP_STOP);
1186 static CURLcode imap_statemach_act(struct connectdata *conn)
1189 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1191 struct imap_conn *imapc = &conn->proto.imapc;
1192 struct pingpong *pp = &imapc->pp;
1195 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1196 if(imapc->state == IMAP_UPGRADETLS)
1197 return imap_state_upgrade_tls(conn);
1199 /* Flush any data that needs to be sent */
1201 return Curl_pp_flushsend(pp);
1203 /* Read the response from the server */
1204 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1209 /* We have now received a full IMAP server response */
1210 switch(imapc->state) {
1211 case IMAP_SERVERGREET:
1212 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1216 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1219 case IMAP_CAPABILITY:
1220 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1223 case IMAP_AUTHENTICATE_PLAIN:
1224 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1227 case IMAP_AUTHENTICATE_LOGIN:
1228 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1231 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1232 result = imap_state_auth_login_password_resp(conn, imapcode,
1236 #ifndef CURL_DISABLE_CRYPTO_AUTH
1237 case IMAP_AUTHENTICATE_CRAMMD5:
1238 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1241 case IMAP_AUTHENTICATE_DIGESTMD5:
1242 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1245 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1246 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1251 case IMAP_AUTHENTICATE_NTLM:
1252 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1255 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1256 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1261 case IMAP_AUTHENTICATE:
1262 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1266 result = imap_state_login_resp(conn, imapcode, imapc->state);
1270 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1274 result = imap_state_select_resp(conn, imapcode, imapc->state);
1278 /* fallthrough, just stop! */
1280 /* internal error */
1281 state(conn, IMAP_STOP);
1289 /* Called repeatedly until done from multi.c */
1290 static CURLcode imap_multi_statemach(struct connectdata *conn,
1293 struct imap_conn *imapc = &conn->proto.imapc;
1296 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1297 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1299 result = Curl_pp_multi_statemach(&imapc->pp);
1301 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1306 static CURLcode imap_easy_statemach(struct connectdata *conn)
1308 struct imap_conn *imapc = &conn->proto.imapc;
1309 struct pingpong *pp = &imapc->pp;
1310 CURLcode result = CURLE_OK;
1312 while(imapc->state != IMAP_STOP) {
1313 result = Curl_pp_easy_statemach(pp);
1321 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1323 static CURLcode imap_init(struct connectdata *conn)
1325 struct SessionHandle *data = conn->data;
1326 struct FTP *imap = data->state.proto.imap;
1329 imap = data->state.proto.imap = calloc(sizeof(struct FTP), 1);
1331 return CURLE_OUT_OF_MEMORY;
1334 /* Get some initial data into the imap struct */
1335 imap->bytecountp = &data->req.bytecount;
1337 /* No need to duplicate user+password, the connectdata struct won't change
1338 during a session, but we re-init them here since on subsequent inits
1339 since the conn struct may have changed or been replaced.
1341 imap->user = conn->user;
1342 imap->passwd = conn->passwd;
1347 /***********************************************************************
1349 * imap_connect() should do everything that is to be considered a part of
1350 * the connection phase.
1352 * The variable 'done' points to will be TRUE if the protocol-layer connect
1353 * phase is done when this function returns, or FALSE is not. When called as
1354 * a part of the easy interface, it will always be TRUE.
1356 static CURLcode imap_connect(struct connectdata *conn,
1357 bool *done) /* see description above */
1360 struct imap_conn *imapc = &conn->proto.imapc;
1361 struct SessionHandle *data=conn->data;
1362 struct pingpong *pp = &imapc->pp;
1364 *done = FALSE; /* default to not done yet */
1366 /* If there already is a protocol-specific struct allocated for this
1367 sessionhandle, deal with it */
1368 Curl_reset_reqproto(conn);
1370 result = imap_init(conn);
1371 if(CURLE_OK != result)
1374 /* We always support persistent connections on imap */
1375 conn->bits.close = FALSE;
1377 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1378 pp->statemach_act = imap_statemach_act;
1379 pp->endofresp = imap_endofresp;
1382 if((conn->handler->flags & PROTOPT_SSL) &&
1383 data->state.used_interface != Curl_if_multi) {
1384 /* IMAPS is simply imap with SSL for the control channel */
1385 /* so perform the SSL initialization for this socket */
1386 result = Curl_ssl_connect(conn, FIRSTSOCKET);
1391 /* Initialise the response reader stuff */
1394 /* Start off waiting for the server greeting response */
1395 state(conn, IMAP_SERVERGREET);
1397 /* Start off with an id of '*' */
1400 if(data->state.used_interface == Curl_if_multi)
1401 result = imap_multi_statemach(conn, done);
1403 result = imap_easy_statemach(conn);
1411 /***********************************************************************
1415 * The DONE function. This does what needs to be done after a single DO has
1418 * Input argument is already checked for validity.
1420 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1423 struct SessionHandle *data = conn->data;
1424 struct FTP *imap = data->state.proto.imap;
1425 CURLcode result=CURLE_OK;
1430 /* When the easy handle is removed from the multi while libcurl is still
1431 * trying to resolve the host name, it seems that the imap struct is not
1432 * yet initialized, but the removal action calls Curl_done() which calls
1433 * this function. So we simply return success if no imap pointer is set.
1438 conn->bits.close = TRUE; /* marked for closure */
1439 result = status; /* use the already set error code */
1442 /* Clear the transfer mode for the next connection */
1443 imap->transfer = FTPTRANSFER_BODY;
1448 /***********************************************************************
1452 * This is the actual DO function for IMAP. Get a file/directory according to
1453 * the options previously setup.
1455 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1458 /* This is IMAP and no proxy */
1459 CURLcode result = CURLE_OK;
1461 DEBUGF(infof(conn->data, "DO phase starts\n"));
1463 if(conn->data->set.opt_no_body) {
1464 /* Requested no body means no transfer */
1465 struct FTP *imap = conn->data->state.proto.imap;
1466 imap->transfer = FTPTRANSFER_INFO;
1469 *dophase_done = FALSE; /* not done yet */
1471 /* Start the first command in the DO phase */
1472 result = imap_select(conn);
1476 /* Run the state-machine */
1477 if(conn->data->state.used_interface == Curl_if_multi)
1478 result = imap_multi_statemach(conn, dophase_done);
1480 result = imap_easy_statemach(conn);
1481 *dophase_done = TRUE; /* with the easy interface we are done here */
1483 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1486 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1491 /***********************************************************************
1495 * This function is registered as 'curl_do' function. It decodes the path
1496 * parts etc as a wrapper to the actual DO function (imap_perform).
1498 * The input argument is already checked for validity.
1500 static CURLcode imap_do(struct connectdata *conn, bool *done)
1502 CURLcode retcode = CURLE_OK;
1504 *done = FALSE; /* default to false */
1507 Since connections can be re-used between SessionHandles, this might be a
1508 connection already existing but on a fresh SessionHandle struct so we must
1509 make sure we have a good 'struct IMAP' to play with. For new connections,
1510 the struct IMAP is allocated and setup in the imap_connect() function.
1512 Curl_reset_reqproto(conn);
1513 retcode = imap_init(conn);
1517 /* Parse the URL path */
1518 retcode = imap_parse_url_path(conn);
1522 retcode = imap_regular_transfer(conn, done);
1527 /***********************************************************************
1531 * This should be called before calling sclose(). We should then wait for the
1532 * response from the server before returning. The calling code should then try
1533 * to close the connection.
1536 static CURLcode imap_logout(struct connectdata *conn)
1538 CURLcode result = CURLE_OK;
1539 const char *str = getcmdid(conn);
1541 result = imap_sendf(conn, str, "%s LOGOUT", str, NULL);
1545 state(conn, IMAP_LOGOUT);
1547 result = imap_easy_statemach(conn);
1552 /***********************************************************************
1556 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1557 * resources. BLOCKING.
1559 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1561 struct imap_conn *imapc= &conn->proto.imapc;
1563 /* We cannot send quit unconditionally. If this connection is stale or
1564 bad in any way, sending quit and waiting around here will make the
1565 disconnect wait in vain and cause more problems than we need to */
1567 /* The IMAP session may or may not have been allocated/setup at this
1569 if(!dead_connection && imapc->pp.conn)
1570 (void)imap_logout(conn); /* ignore errors on the LOGOUT */
1572 /* Disconnect from the server */
1573 Curl_pp_disconnect(&imapc->pp);
1575 /* Cleanup the SASL module */
1576 Curl_sasl_cleanup(conn, imapc->authused);
1578 /* Cleanup our connection based variables */
1579 Curl_safefree(imapc->mailbox);
1584 /***********************************************************************
1586 * imap_parse_url_path()
1588 * Parse the URL path into separate path components.
1591 static CURLcode imap_parse_url_path(struct connectdata *conn)
1593 /* The imap struct is already inited in imap_connect() */
1594 struct imap_conn *imapc = &conn->proto.imapc;
1595 struct SessionHandle *data = conn->data;
1596 const char *path = data->state.path;
1601 /* URL decode the path and use this mailbox */
1602 return Curl_urldecode(data, path, 0, &imapc->mailbox, NULL, TRUE);
1605 /* Call this when the DO phase has completed */
1606 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1608 struct FTP *imap = conn->data->state.proto.imap;
1612 if(imap->transfer != FTPTRANSFER_BODY)
1613 /* no data to transfer */
1614 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1619 /* Called from multi.c while DOing */
1620 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1622 CURLcode result = imap_multi_statemach(conn, dophase_done);
1625 DEBUGF(infof(conn->data, "DO phase failed\n"));
1628 result = imap_dophase_done(conn, FALSE /* not connected */);
1630 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1637 /***********************************************************************
1639 * imap_regular_transfer()
1641 * The input argument is already checked for validity.
1643 * Performs all commands done before a regular transfer between a local and a
1646 static CURLcode imap_regular_transfer(struct connectdata *conn,
1649 CURLcode result = CURLE_OK;
1650 bool connected = FALSE;
1651 struct SessionHandle *data = conn->data;
1653 /* Make sure size is unknown at this point */
1654 data->req.size = -1;
1656 Curl_pgrsSetUploadCounter(data, 0);
1657 Curl_pgrsSetDownloadCounter(data, 0);
1658 Curl_pgrsSetUploadSize(data, 0);
1659 Curl_pgrsSetDownloadSize(data, 0);
1661 result = imap_perform(conn, &connected, dophase_done);
1663 if(CURLE_OK == result) {
1665 /* The DO phase has not completed yet */
1668 result = imap_dophase_done(conn, connected);
1676 static CURLcode imap_setup_connection(struct connectdata * conn)
1678 struct SessionHandle *data = conn->data;
1680 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1681 /* Unless we have asked to tunnel imap operations through the proxy, we
1682 switch and use HTTP operations only */
1683 #ifndef CURL_DISABLE_HTTP
1684 if(conn->handler == &Curl_handler_imap)
1685 conn->handler = &Curl_handler_imap_proxy;
1688 conn->handler = &Curl_handler_imaps_proxy;
1690 failf(data, "IMAPS not supported!");
1691 return CURLE_UNSUPPORTED_PROTOCOL;
1695 /* We explicitly mark this connection as persistent here as we're doing
1696 IMAP over HTTP and thus we accidentally avoid setting this value
1698 conn->bits.close = FALSE;
1700 failf(data, "IMAP over http proxy requires HTTP support built-in!");
1701 return CURLE_UNSUPPORTED_PROTOCOL;
1705 data->state.path++; /* don't include the initial slash */
1710 #endif /* CURL_DISABLE_IMAP */