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 * RFC2595 Using TLS with IMAP, POP3 and ACAP
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3501 IMAPv4 protocol
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4959 IMAP Extension for SASL Initial Client Response
28 * RFC5092 IMAP URL Scheme
29 * RFC6749 OAuth 2.0 Authorization Framework
30 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
32 ***************************************************************************/
34 #include "curl_setup.h"
36 #ifndef CURL_DISABLE_IMAP
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"
84 #define _MPRINTF_REPLACE /* use our functions only */
85 #include <curl/mprintf.h>
87 #include "curl_memory.h"
88 /* The last #include file should be: */
91 /* Local API functions */
92 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
93 static CURLcode imap_do(struct connectdata *conn, bool *done);
94 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
96 static CURLcode imap_connect(struct connectdata *conn, bool *done);
97 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
98 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
99 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
101 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
102 static CURLcode imap_setup_connection(struct connectdata *conn);
103 static char *imap_atom(const char *str);
104 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
105 static CURLcode imap_parse_url_options(struct connectdata *conn);
106 static CURLcode imap_parse_url_path(struct connectdata *conn);
107 static CURLcode imap_parse_custom_request(struct connectdata *conn);
110 * IMAP protocol handler.
113 const struct Curl_handler Curl_handler_imap = {
115 imap_setup_connection, /* setup_connection */
117 imap_done, /* done */
118 ZERO_NULL, /* do_more */
119 imap_connect, /* connect_it */
120 imap_multi_statemach, /* connecting */
121 imap_doing, /* doing */
122 imap_getsock, /* proto_getsock */
123 imap_getsock, /* doing_getsock */
124 ZERO_NULL, /* domore_getsock */
125 ZERO_NULL, /* perform_getsock */
126 imap_disconnect, /* disconnect */
127 ZERO_NULL, /* readwrite */
128 PORT_IMAP, /* defport */
129 CURLPROTO_IMAP, /* protocol */
130 PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
131 | PROTOPT_NOURLQUERY /* flags */
136 * IMAPS protocol handler.
139 const struct Curl_handler Curl_handler_imaps = {
140 "IMAPS", /* scheme */
141 imap_setup_connection, /* setup_connection */
143 imap_done, /* done */
144 ZERO_NULL, /* do_more */
145 imap_connect, /* connect_it */
146 imap_multi_statemach, /* connecting */
147 imap_doing, /* doing */
148 imap_getsock, /* proto_getsock */
149 imap_getsock, /* doing_getsock */
150 ZERO_NULL, /* domore_getsock */
151 ZERO_NULL, /* perform_getsock */
152 imap_disconnect, /* disconnect */
153 ZERO_NULL, /* readwrite */
154 PORT_IMAPS, /* defport */
155 CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
156 PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
157 | PROTOPT_NOURLQUERY /* flags */
161 #ifndef CURL_DISABLE_HTTP
163 * HTTP-proxyed IMAP protocol handler.
166 static const struct Curl_handler Curl_handler_imap_proxy = {
168 Curl_http_setup_conn, /* setup_connection */
169 Curl_http, /* do_it */
170 Curl_http_done, /* done */
171 ZERO_NULL, /* do_more */
172 ZERO_NULL, /* connect_it */
173 ZERO_NULL, /* connecting */
174 ZERO_NULL, /* doing */
175 ZERO_NULL, /* proto_getsock */
176 ZERO_NULL, /* doing_getsock */
177 ZERO_NULL, /* domore_getsock */
178 ZERO_NULL, /* perform_getsock */
179 ZERO_NULL, /* disconnect */
180 ZERO_NULL, /* readwrite */
181 PORT_IMAP, /* defport */
182 CURLPROTO_HTTP, /* protocol */
183 PROTOPT_NONE /* flags */
188 * HTTP-proxyed IMAPS protocol handler.
191 static const struct Curl_handler Curl_handler_imaps_proxy = {
192 "IMAPS", /* scheme */
193 Curl_http_setup_conn, /* setup_connection */
194 Curl_http, /* do_it */
195 Curl_http_done, /* done */
196 ZERO_NULL, /* do_more */
197 ZERO_NULL, /* connect_it */
198 ZERO_NULL, /* connecting */
199 ZERO_NULL, /* doing */
200 ZERO_NULL, /* proto_getsock */
201 ZERO_NULL, /* doing_getsock */
202 ZERO_NULL, /* domore_getsock */
203 ZERO_NULL, /* perform_getsock */
204 ZERO_NULL, /* disconnect */
205 ZERO_NULL, /* readwrite */
206 PORT_IMAPS, /* defport */
207 CURLPROTO_HTTP, /* protocol */
208 PROTOPT_NONE /* flags */
214 static void imap_to_imaps(struct connectdata *conn)
216 conn->handler = &Curl_handler_imaps;
219 #define imap_to_imaps(x) Curl_nop_stmt
222 /***********************************************************************
226 * Determines whether the untagged response is related to the specified
227 * command by checking if it is in format "* <command-name> ..." or
228 * "* <number> <command-name> ...".
230 * The "* " marker is assumed to have already been checked by the caller.
232 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
234 const char *end = line + len;
235 size_t cmd_len = strlen(cmd);
237 /* Skip the untagged response marker */
240 /* Do we have a number after the marker? */
241 if(line < end && ISDIGIT(*line)) {
242 /* Skip the number */
245 while(line < end && ISDIGIT(*line));
247 /* Do we have the space character? */
248 if(line == end || *line != ' ')
254 /* Does the command name match and is it followed by a space character or at
256 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
257 (line[cmd_len] == ' ' || line + cmd_len == end))
263 /***********************************************************************
267 * Checks whether the given string is a valid tagged, untagged or continuation
268 * response which can be processed by the response handler.
270 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
273 struct IMAP *imap = conn->data->req.protop;
274 struct imap_conn *imapc = &conn->proto.imapc;
275 const char *id = imapc->resptag;
276 size_t id_len = strlen(id);
278 /* Do we have a tagged command response? */
279 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
283 if(len >= 2 && !memcmp(line, "OK", 2))
285 else if(len >= 2 && !memcmp(line, "NO", 2))
287 else if(len >= 3 && !memcmp(line, "BAD", 3))
290 failf(conn->data, "Bad tagged response");
297 /* Do we have an untagged command response? */
298 if(len >= 2 && !memcmp("* ", line, 2)) {
299 switch(imapc->state) {
300 /* States which are interested in untagged responses */
301 case IMAP_CAPABILITY:
302 if(!imap_matchresp(line, len, "CAPABILITY"))
307 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
308 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
309 (strcmp(imap->custom, "STORE") ||
310 !imap_matchresp(line, len, "FETCH")) &&
311 strcmp(imap->custom, "SELECT") &&
312 strcmp(imap->custom, "EXAMINE") &&
313 strcmp(imap->custom, "SEARCH") &&
314 strcmp(imap->custom, "EXPUNGE") &&
315 strcmp(imap->custom, "LSUB") &&
316 strcmp(imap->custom, "UID") &&
317 strcmp(imap->custom, "NOOP")))
322 /* SELECT is special in that its untagged responses do not have a
323 common prefix so accept anything! */
327 if(!imap_matchresp(line, len, "FETCH"))
331 /* Ignore other untagged responses */
340 /* Do we have a continuation response? This should be a + symbol followed by
341 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
342 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
343 some e-mail servers ignore this and only send a single + instead. */
344 if((len == 3 && !memcmp("+", line, 1)) ||
345 (len >= 2 && !memcmp("+ ", line, 2))) {
346 switch(imapc->state) {
347 /* States which are interested in continuation responses */
348 case IMAP_AUTHENTICATE_PLAIN:
349 case IMAP_AUTHENTICATE_LOGIN:
350 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
351 case IMAP_AUTHENTICATE_CRAMMD5:
352 case IMAP_AUTHENTICATE_DIGESTMD5:
353 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
354 case IMAP_AUTHENTICATE_NTLM:
355 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
356 case IMAP_AUTHENTICATE_XOAUTH2:
357 case IMAP_AUTHENTICATE_FINAL:
363 failf(conn->data, "Unexpected continuation response");
371 return FALSE; /* Nothing for us */
374 /***********************************************************************
378 * Gets the authentication message from the response buffer.
380 static void imap_get_message(char *buffer, char** outptr)
383 char* message = NULL;
385 /* Find the start of the message */
386 for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
389 /* Find the end of the message */
390 for(len = strlen(message); len--;)
391 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
392 message[len] != '\t')
395 /* Terminate the message */
403 /***********************************************************************
407 * This is the ONLY way to change IMAP state!
409 static void state(struct connectdata *conn, imapstate newstate)
411 struct imap_conn *imapc = &conn->proto.imapc;
412 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
413 /* for debug purposes */
414 static const char * const names[]={
420 "AUTHENTICATE_PLAIN",
421 "AUTHENTICATE_LOGIN",
422 "AUTHENTICATE_LOGIN_PASSWD",
423 "AUTHENTICATE_CRAMMD5",
424 "AUTHENTICATE_DIGESTMD5",
425 "AUTHENTICATE_DIGESTMD5_RESP",
427 "AUTHENTICATE_NTLM_TYPE2MSG",
428 "AUTHENTICATE_XOAUTH2",
429 "AUTHENTICATE_CANCEL",
430 "AUTHENTICATE_FINAL",
442 if(imapc->state != newstate)
443 infof(conn->data, "IMAP %p state change from %s to %s\n",
444 (void *)imapc, names[imapc->state], names[newstate]);
447 imapc->state = newstate;
450 /***********************************************************************
452 * imap_perform_capability()
454 * Sends the CAPABILITY command in order to obtain a list of server side
455 * supported capabilities.
457 static CURLcode imap_perform_capability(struct connectdata *conn)
459 CURLcode result = CURLE_OK;
460 struct imap_conn *imapc = &conn->proto.imapc;
462 imapc->authmechs = 0; /* No known authentication mechanisms yet */
463 imapc->authused = 0; /* Clear the authentication mechanism used */
464 imapc->tls_supported = FALSE; /* Clear the TLS capability */
466 /* Send the CAPABILITY command */
467 result = imap_sendf(conn, "CAPABILITY");
470 state(conn, IMAP_CAPABILITY);
475 /***********************************************************************
477 * imap_perform_starttls()
479 * Sends the STARTTLS command to start the upgrade to TLS.
481 static CURLcode imap_perform_starttls(struct connectdata *conn)
483 CURLcode result = CURLE_OK;
485 /* Send the STARTTLS command */
486 result = imap_sendf(conn, "STARTTLS");
489 state(conn, IMAP_STARTTLS);
494 /***********************************************************************
496 * imap_perform_upgrade_tls()
498 * Performs the upgrade to TLS.
500 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
502 CURLcode result = CURLE_OK;
503 struct imap_conn *imapc = &conn->proto.imapc;
505 /* Start the SSL connection */
506 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
509 if(imapc->state != IMAP_UPGRADETLS)
510 state(conn, IMAP_UPGRADETLS);
514 result = imap_perform_capability(conn);
521 /***********************************************************************
523 * imap_perform_login()
525 * Sends a clear text LOGIN command to authenticate with.
527 static CURLcode imap_perform_login(struct connectdata *conn)
529 CURLcode result = CURLE_OK;
533 /* Check we have a username and password to authenticate with and end the
534 connect phase if we don't */
535 if(!conn->bits.user_passwd) {
536 state(conn, IMAP_STOP);
541 /* Make sure the username and password are in the correct atom format */
542 user = imap_atom(conn->user);
543 passwd = imap_atom(conn->passwd);
545 /* Send the LOGIN command */
546 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
547 passwd ? passwd : "");
550 Curl_safefree(passwd);
553 state(conn, IMAP_LOGIN);
558 /***********************************************************************
560 * imap_perform_authenticate()
562 * Sends an AUTHENTICATE command allowing the client to login with the
563 * appropriate SASL authentication mechanism.
565 * Additionally, the function will perform fallback to the LOGIN command
566 * should a common mechanism not be available between the client and server.
568 static CURLcode imap_perform_authenticate(struct connectdata *conn)
570 CURLcode result = CURLE_OK;
571 struct SessionHandle *data = conn->data;
572 struct imap_conn *imapc = &conn->proto.imapc;
573 const char *mech = NULL;
574 char *initresp = NULL;
576 imapstate state1 = IMAP_STOP;
577 imapstate state2 = IMAP_STOP;
579 /* Check we have a username and password to authenticate with and end the
580 connect phase if we don't */
581 if(!conn->bits.user_passwd) {
582 state(conn, IMAP_STOP);
587 /* Calculate the supported authentication mechanism by decreasing order of
589 #ifndef CURL_DISABLE_CRYPTO_AUTH
590 if((imapc->authmechs & SASL_MECH_DIGEST_MD5) &&
591 (imapc->prefmech & SASL_MECH_DIGEST_MD5)) {
592 mech = SASL_MECH_STRING_DIGEST_MD5;
593 state1 = IMAP_AUTHENTICATE_DIGESTMD5;
594 imapc->authused = SASL_MECH_DIGEST_MD5;
596 else if((imapc->authmechs & SASL_MECH_CRAM_MD5) &&
597 (imapc->prefmech & SASL_MECH_CRAM_MD5)) {
598 mech = SASL_MECH_STRING_CRAM_MD5;
599 state1 = IMAP_AUTHENTICATE_CRAMMD5;
600 imapc->authused = SASL_MECH_CRAM_MD5;
605 if((imapc->authmechs & SASL_MECH_NTLM) &&
606 (imapc->prefmech & SASL_MECH_NTLM)) {
607 mech = SASL_MECH_STRING_NTLM;
608 state1 = IMAP_AUTHENTICATE_NTLM;
609 state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
610 imapc->authused = SASL_MECH_NTLM;
612 if(imapc->ir_supported || data->set.sasl_ir)
613 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
619 if(((imapc->authmechs & SASL_MECH_XOAUTH2) &&
620 (imapc->prefmech & SASL_MECH_XOAUTH2) &&
621 (imapc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
622 mech = SASL_MECH_STRING_XOAUTH2;
623 state1 = IMAP_AUTHENTICATE_XOAUTH2;
624 state2 = IMAP_AUTHENTICATE_FINAL;
625 imapc->authused = SASL_MECH_XOAUTH2;
627 if(imapc->ir_supported || data->set.sasl_ir)
628 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
629 conn->xoauth2_bearer,
632 else if((imapc->authmechs & SASL_MECH_LOGIN) &&
633 (imapc->prefmech & SASL_MECH_LOGIN)) {
634 mech = SASL_MECH_STRING_LOGIN;
635 state1 = IMAP_AUTHENTICATE_LOGIN;
636 state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
637 imapc->authused = SASL_MECH_LOGIN;
639 if(imapc->ir_supported || data->set.sasl_ir)
640 result = Curl_sasl_create_login_message(conn->data, conn->user,
643 else if((imapc->authmechs & SASL_MECH_PLAIN) &&
644 (imapc->prefmech & SASL_MECH_PLAIN)) {
645 mech = SASL_MECH_STRING_PLAIN;
646 state1 = IMAP_AUTHENTICATE_PLAIN;
647 state2 = IMAP_AUTHENTICATE_FINAL;
648 imapc->authused = SASL_MECH_PLAIN;
650 if(imapc->ir_supported || data->set.sasl_ir)
651 result = Curl_sasl_create_plain_message(conn->data, conn->user,
652 conn->passwd, &initresp, &len);
657 /* Perform SASL based authentication */
659 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
665 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
671 Curl_safefree(initresp);
673 else if(!imapc->login_disabled)
674 /* Perform clear text authentication */
675 result = imap_perform_login(conn);
677 /* Other mechanisms not supported */
678 infof(conn->data, "No known authentication mechanisms supported!\n");
679 result = CURLE_LOGIN_DENIED;
686 /***********************************************************************
688 * imap_perform_list()
690 * Sends a LIST command or an alternative custom request.
692 static CURLcode imap_perform_list(struct connectdata *conn)
694 CURLcode result = CURLE_OK;
695 struct SessionHandle *data = conn->data;
696 struct IMAP *imap = data->req.protop;
700 /* Send the custom request */
701 result = imap_sendf(conn, "%s%s", imap->custom,
702 imap->custom_params ? imap->custom_params : "");
704 /* Make sure the mailbox is in the correct atom format */
705 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
707 return CURLE_OUT_OF_MEMORY;
709 /* Send the LIST command */
710 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
712 Curl_safefree(mailbox);
716 state(conn, IMAP_LIST);
721 /***********************************************************************
723 * imap_perform_select()
725 * Sends a SELECT command to ask the server to change the selected mailbox.
727 static CURLcode imap_perform_select(struct connectdata *conn)
729 CURLcode result = CURLE_OK;
730 struct SessionHandle *data = conn->data;
731 struct IMAP *imap = data->req.protop;
732 struct imap_conn *imapc = &conn->proto.imapc;
735 /* Invalidate old information as we are switching mailboxes */
736 Curl_safefree(imapc->mailbox);
737 Curl_safefree(imapc->mailbox_uidvalidity);
739 /* Check we have a mailbox */
741 failf(conn->data, "Cannot SELECT without a mailbox.");
742 return CURLE_URL_MALFORMAT;
745 /* Make sure the mailbox is in the correct atom format */
746 mailbox = imap_atom(imap->mailbox);
748 return CURLE_OUT_OF_MEMORY;
750 /* Send the SELECT command */
751 result = imap_sendf(conn, "SELECT %s", mailbox);
753 Curl_safefree(mailbox);
756 state(conn, IMAP_SELECT);
761 /***********************************************************************
763 * imap_perform_fetch()
765 * Sends a FETCH command to initiate the download of a message.
767 static CURLcode imap_perform_fetch(struct connectdata *conn)
769 CURLcode result = CURLE_OK;
770 struct IMAP *imap = conn->data->req.protop;
772 /* Check we have a UID */
774 failf(conn->data, "Cannot FETCH without a UID.");
775 return CURLE_URL_MALFORMAT;
778 /* Send the FETCH command */
779 result = imap_sendf(conn, "FETCH %s BODY[%s]",
781 imap->section ? imap->section : "");
784 state(conn, IMAP_FETCH);
789 /***********************************************************************
791 * imap_perform_append()
793 * Sends an APPEND command to initiate the upload of a message.
795 static CURLcode imap_perform_append(struct connectdata *conn)
797 CURLcode result = CURLE_OK;
798 struct IMAP *imap = conn->data->req.protop;
801 /* Check we have a mailbox */
803 failf(conn->data, "Cannot APPEND without a mailbox.");
804 return CURLE_URL_MALFORMAT;
807 /* Check we know the size of the upload */
808 if(conn->data->set.infilesize < 0) {
809 failf(conn->data, "Cannot APPEND with unknown input file size\n");
810 return CURLE_UPLOAD_FAILED;
813 /* Make sure the mailbox is in the correct atom format */
814 mailbox = imap_atom(imap->mailbox);
816 return CURLE_OUT_OF_MEMORY;
818 /* Send the APPEND command */
819 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
820 mailbox, conn->data->set.infilesize);
822 Curl_safefree(mailbox);
825 state(conn, IMAP_APPEND);
830 /***********************************************************************
832 * imap_perform_logout()
834 * Performs the logout action prior to sclose() being called.
836 static CURLcode imap_perform_logout(struct connectdata *conn)
838 CURLcode result = CURLE_OK;
840 /* Send the LOGOUT command */
841 result = imap_sendf(conn, "LOGOUT");
844 state(conn, IMAP_LOGOUT);
849 /* For the initial server greeting */
850 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
854 CURLcode result = CURLE_OK;
855 struct SessionHandle *data = conn->data;
857 (void)instate; /* no use for this yet */
859 if(imapcode != 'O') {
860 failf(data, "Got unexpected imap-server response");
861 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
864 result = imap_perform_capability(conn);
869 /* For CAPABILITY responses */
870 static CURLcode imap_state_capability_resp(struct connectdata *conn,
874 CURLcode result = CURLE_OK;
875 struct SessionHandle *data = conn->data;
876 struct imap_conn *imapc = &conn->proto.imapc;
877 const char *line = data->state.buffer;
880 (void)instate; /* no use for this yet */
882 /* Do we have a untagged response? */
883 if(imapcode == '*') {
886 /* Loop through the data line */
889 (*line == ' ' || *line == '\t' ||
890 *line == '\r' || *line == '\n')) {
898 /* Extract the word */
899 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
900 line[wordlen] != '\t' && line[wordlen] != '\r' &&
901 line[wordlen] != '\n';)
904 /* Does the server support the STARTTLS capability? */
905 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
906 imapc->tls_supported = TRUE;
908 /* Has the server explicitly disabled clear text authentication? */
909 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
910 imapc->login_disabled = TRUE;
912 /* Does the server support the SASL-IR capability? */
913 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
914 imapc->ir_supported = TRUE;
916 /* Do we have a SASL based authentication mechanism? */
917 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
921 /* Test the word for a matching authentication mechanism */
922 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
923 imapc->authmechs |= SASL_MECH_LOGIN;
924 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
925 imapc->authmechs |= SASL_MECH_PLAIN;
926 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
927 imapc->authmechs |= SASL_MECH_CRAM_MD5;
928 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
929 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
930 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
931 imapc->authmechs |= SASL_MECH_GSSAPI;
932 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
933 imapc->authmechs |= SASL_MECH_EXTERNAL;
934 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
935 imapc->authmechs |= SASL_MECH_NTLM;
936 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
937 imapc->authmechs |= SASL_MECH_XOAUTH2;
943 else if(imapcode == 'O') {
944 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
945 /* We don't have a SSL/TLS connection yet, but SSL is requested */
946 if(imapc->tls_supported)
947 /* Switch to TLS connection now */
948 result = imap_perform_starttls(conn);
949 else if(data->set.use_ssl == CURLUSESSL_TRY)
950 /* Fallback and carry on with authentication */
951 result = imap_perform_authenticate(conn);
953 failf(data, "STARTTLS not supported.");
954 result = CURLE_USE_SSL_FAILED;
958 result = imap_perform_authenticate(conn);
961 result = imap_perform_login(conn);
966 /* For STARTTLS responses */
967 static CURLcode imap_state_starttls_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(imapcode != 'O') {
977 if(data->set.use_ssl != CURLUSESSL_TRY) {
978 failf(data, "STARTTLS denied. %c", imapcode);
979 result = CURLE_USE_SSL_FAILED;
982 result = imap_perform_authenticate(conn);
985 result = imap_perform_upgrade_tls(conn);
990 /* For AUTHENTICATE PLAIN (without initial response) responses */
991 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
995 CURLcode result = CURLE_OK;
996 struct SessionHandle *data = conn->data;
998 char *plainauth = NULL;
1000 (void)instate; /* no use for this yet */
1002 if(imapcode != '+') {
1003 failf(data, "Access denied. %c", imapcode);
1004 result = CURLE_LOGIN_DENIED;
1007 /* Create the authorisation message */
1008 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
1011 /* Send the message */
1014 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
1017 state(conn, IMAP_AUTHENTICATE_FINAL);
1020 Curl_safefree(plainauth);
1027 /* For AUTHENTICATE LOGIN (without initial response) responses */
1028 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1032 CURLcode result = CURLE_OK;
1033 struct SessionHandle *data = conn->data;
1035 char *authuser = NULL;
1037 (void)instate; /* no use for this yet */
1039 if(imapcode != '+') {
1040 failf(data, "Access denied: %d", imapcode);
1041 result = CURLE_LOGIN_DENIED;
1044 /* Create the user message */
1045 result = Curl_sasl_create_login_message(data, conn->user,
1051 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1054 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1057 Curl_safefree(authuser);
1064 /* For AUTHENTICATE LOGIN user entry responses */
1065 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1069 CURLcode result = CURLE_OK;
1070 struct SessionHandle *data = conn->data;
1072 char *authpasswd = NULL;
1074 (void)instate; /* no use for this yet */
1076 if(imapcode != '+') {
1077 failf(data, "Access denied: %d", imapcode);
1078 result = CURLE_LOGIN_DENIED;
1081 /* Create the password message */
1082 result = Curl_sasl_create_login_message(data, conn->passwd,
1085 /* Send the password */
1088 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1091 state(conn, IMAP_AUTHENTICATE_FINAL);
1094 Curl_safefree(authpasswd);
1101 #ifndef CURL_DISABLE_CRYPTO_AUTH
1102 /* For AUTHENTICATE CRAM-MD5 responses */
1103 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1107 CURLcode result = CURLE_OK;
1108 struct SessionHandle *data = conn->data;
1110 char *chlg64 = NULL;
1111 char *rplyb64 = NULL;
1114 (void)instate; /* no use for this yet */
1116 if(imapcode != '+') {
1117 failf(data, "Access denied: %d", imapcode);
1118 return CURLE_LOGIN_DENIED;
1121 /* Get the challenge message */
1122 imap_get_message(data->state.buffer, &chlg64);
1124 /* Decode the challenge message */
1125 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
1127 /* Send the cancellation */
1128 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1131 state(conn, IMAP_AUTHENTICATE_CANCEL);
1134 /* Create the response message */
1135 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
1136 conn->passwd, &rplyb64, &len);
1137 if(!result && rplyb64) {
1138 /* Send the response */
1139 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1142 state(conn, IMAP_AUTHENTICATE_FINAL);
1146 Curl_safefree(chlg);
1147 Curl_safefree(rplyb64);
1152 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1153 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1157 CURLcode result = CURLE_OK;
1158 struct SessionHandle *data = conn->data;
1159 char *chlg64 = NULL;
1160 char *rplyb64 = NULL;
1167 (void)instate; /* no use for this yet */
1169 if(imapcode != '+') {
1170 failf(data, "Access denied: %d", imapcode);
1171 return CURLE_LOGIN_DENIED;
1174 /* Get the challenge message */
1175 imap_get_message(data->state.buffer, &chlg64);
1177 /* Decode the challange message */
1178 result = Curl_sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
1179 realm, sizeof(realm),
1180 algorithm, sizeof(algorithm));
1181 if(result || strcmp(algorithm, "md5-sess") != 0) {
1182 /* Send the cancellation */
1183 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1186 state(conn, IMAP_AUTHENTICATE_CANCEL);
1189 /* Create the response message */
1190 result = Curl_sasl_create_digest_md5_message(data, nonce, realm,
1191 conn->user, conn->passwd,
1192 "imap", &rplyb64, &len);
1193 if(!result && rplyb64) {
1194 /* Send the response */
1195 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1198 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1202 Curl_safefree(rplyb64);
1207 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1208 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1212 CURLcode result = CURLE_OK;
1213 struct SessionHandle *data = conn->data;
1215 (void)instate; /* no use for this yet */
1217 if(imapcode != '+') {
1218 failf(data, "Authentication failed: %d", imapcode);
1219 result = CURLE_LOGIN_DENIED;
1222 /* Send an empty response */
1223 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1226 state(conn, IMAP_AUTHENTICATE_FINAL);
1234 /* For AUTHENTICATE NTLM (without initial response) responses */
1235 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1239 CURLcode result = CURLE_OK;
1240 struct SessionHandle *data = conn->data;
1242 char *type1msg = NULL;
1244 (void)instate; /* no use for this yet */
1246 if(imapcode != '+') {
1247 failf(data, "Access denied: %d", imapcode);
1248 result = CURLE_LOGIN_DENIED;
1251 /* Create the type-1 message */
1252 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1256 /* Send the message */
1259 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1262 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1265 Curl_safefree(type1msg);
1272 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1273 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1277 CURLcode result = CURLE_OK;
1278 struct SessionHandle *data = conn->data;
1279 char *type2msg = NULL;
1280 char *type3msg = NULL;
1283 (void)instate; /* no use for this yet */
1285 if(imapcode != '+') {
1286 failf(data, "Access denied: %d", imapcode);
1287 result = CURLE_LOGIN_DENIED;
1290 /* Get the challenge message */
1291 imap_get_message(data->state.buffer, &type2msg);
1293 /* Decode the type-2 message */
1294 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1296 /* Send the cancellation */
1297 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1300 state(conn, IMAP_AUTHENTICATE_CANCEL);
1303 /* Create the type-3 message */
1304 result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1305 conn->passwd, &conn->ntlm,
1307 if(!result && type3msg) {
1308 /* Send the message */
1309 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1312 state(conn, IMAP_AUTHENTICATE_FINAL);
1317 Curl_safefree(type3msg);
1323 /* For AUTHENTICATE XOAUTH2 (without initial response) responses */
1324 static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn,
1328 CURLcode result = CURLE_OK;
1329 struct SessionHandle *data = conn->data;
1331 char *xoauth = NULL;
1333 (void)instate; /* no use for this yet */
1335 if(imapcode != '+') {
1336 failf(data, "Access denied: %d", imapcode);
1337 result = CURLE_LOGIN_DENIED;
1340 /* Create the authorisation message */
1341 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1342 conn->xoauth2_bearer,
1345 /* Send the message */
1348 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth);
1351 state(conn, IMAP_AUTHENTICATE_FINAL);
1354 Curl_safefree(xoauth);
1361 /* For AUTHENTICATE cancellation responses */
1362 static CURLcode imap_state_auth_cancel_resp(struct connectdata *conn,
1366 struct SessionHandle *data = conn->data;
1369 (void)instate; /* no use for this yet */
1371 failf(data, "Authentication cancelled");
1373 return CURLE_LOGIN_DENIED;
1376 /* For final responses in the AUTHENTICATE sequence */
1377 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1381 CURLcode result = CURLE_OK;
1382 struct SessionHandle *data = conn->data;
1384 (void)instate; /* no use for this yet */
1386 if(imapcode != 'O') {
1387 failf(data, "Authentication failed: %d", imapcode);
1388 result = CURLE_LOGIN_DENIED;
1391 /* End of connect phase */
1392 state(conn, IMAP_STOP);
1397 /* For LOGIN responses */
1398 static CURLcode imap_state_login_resp(struct connectdata *conn,
1402 CURLcode result = CURLE_OK;
1403 struct SessionHandle *data = conn->data;
1405 (void)instate; /* no use for this yet */
1407 if(imapcode != 'O') {
1408 failf(data, "Access denied. %c", imapcode);
1409 result = CURLE_LOGIN_DENIED;
1412 /* End of connect phase */
1413 state(conn, IMAP_STOP);
1418 /* For LIST responses */
1419 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1422 CURLcode result = CURLE_OK;
1423 char *line = conn->data->state.buffer;
1424 size_t len = strlen(line);
1426 (void)instate; /* No use for this yet */
1428 if(imapcode == '*') {
1429 /* Temporarily add the LF character back and send as body to the client */
1431 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1434 else if(imapcode != 'O')
1435 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1437 /* End of DO phase */
1438 state(conn, IMAP_STOP);
1443 /* For SELECT responses */
1444 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1447 CURLcode result = CURLE_OK;
1448 struct SessionHandle *data = conn->data;
1449 struct IMAP *imap = conn->data->req.protop;
1450 struct imap_conn *imapc = &conn->proto.imapc;
1451 const char *line = data->state.buffer;
1454 (void)instate; /* no use for this yet */
1456 if(imapcode == '*') {
1457 /* See if this is an UIDVALIDITY response */
1458 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1459 Curl_safefree(imapc->mailbox_uidvalidity);
1460 imapc->mailbox_uidvalidity = strdup(tmp);
1463 else if(imapcode == 'O') {
1464 /* Check if the UIDVALIDITY has been specified and matches */
1465 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1466 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1467 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1468 result = CURLE_REMOTE_FILE_NOT_FOUND;
1471 /* Note the currently opened mailbox on this connection */
1472 imapc->mailbox = strdup(imap->mailbox);
1475 result = imap_perform_list(conn);
1477 result = imap_perform_fetch(conn);
1481 failf(data, "Select failed");
1482 result = CURLE_LOGIN_DENIED;
1488 /* For the (first line of the) FETCH responses */
1489 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1492 CURLcode result = CURLE_OK;
1493 struct SessionHandle *data = conn->data;
1494 struct imap_conn *imapc = &conn->proto.imapc;
1495 struct pingpong *pp = &imapc->pp;
1496 const char *ptr = data->state.buffer;
1497 bool parsed = FALSE;
1500 (void)instate; /* no use for this yet */
1502 if(imapcode != '*') {
1503 Curl_pgrsSetDownloadSize(data, 0);
1504 state(conn, IMAP_STOP);
1505 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1508 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1509 the continuation data contained within the curly brackets */
1510 while(*ptr && (*ptr != '{'))
1515 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1516 if(endptr - ptr > 1 && endptr[0] == '}' &&
1517 endptr[1] == '\r' && endptr[2] == '\0')
1522 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1523 Curl_pgrsSetDownloadSize(data, size);
1526 /* At this point there is a bunch of data in the header "cache" that is
1527 actually body content, send it as body and then skip it. Do note
1528 that there may even be additional "headers" after the body. */
1529 size_t chunk = pp->cache_size;
1531 if(chunk > (size_t)size)
1532 /* The conversion from curl_off_t to size_t is always fine here */
1533 chunk = (size_t)size;
1535 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1539 data->req.bytecount += chunk;
1541 infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1542 " bytes are left for transfer\n", (curl_off_t)chunk,
1545 /* Have we used the entire cache or just part of it?*/
1546 if(pp->cache_size > chunk) {
1547 /* Only part of it so shrink the cache to fit the trailing data */
1548 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1549 pp->cache_size -= chunk;
1552 /* Free the cache */
1553 Curl_safefree(pp->cache);
1555 /* Reset the cache size */
1560 if(data->req.bytecount == size)
1561 /* The entire data is already transferred! */
1562 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1565 data->req.maxdownload = size;
1566 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1570 /* We don't know how to parse this line */
1571 failf(pp->conn->data, "Failed to parse FETCH response.");
1572 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1575 /* End of DO phase */
1576 state(conn, IMAP_STOP);
1581 /* For final FETCH responses performed after the download */
1582 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1586 CURLcode result = CURLE_OK;
1588 (void)instate; /* No use for this yet */
1591 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1593 /* End of DONE phase */
1594 state(conn, IMAP_STOP);
1599 /* For APPEND responses */
1600 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1603 CURLcode result = CURLE_OK;
1604 struct SessionHandle *data = conn->data;
1606 (void)instate; /* No use for this yet */
1608 if(imapcode != '+') {
1609 result = CURLE_UPLOAD_FAILED;
1612 /* Set the progress upload size */
1613 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1616 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1618 /* End of DO phase */
1619 state(conn, IMAP_STOP);
1625 /* For final APPEND responses performed after the upload */
1626 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1630 CURLcode result = CURLE_OK;
1632 (void)instate; /* No use for this yet */
1635 result = CURLE_UPLOAD_FAILED;
1637 /* End of DONE phase */
1638 state(conn, IMAP_STOP);
1643 static CURLcode imap_statemach_act(struct connectdata *conn)
1645 CURLcode result = CURLE_OK;
1646 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1648 struct imap_conn *imapc = &conn->proto.imapc;
1649 struct pingpong *pp = &imapc->pp;
1652 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1653 if(imapc->state == IMAP_UPGRADETLS)
1654 return imap_perform_upgrade_tls(conn);
1656 /* Flush any data that needs to be sent */
1658 return Curl_pp_flushsend(pp);
1661 /* Read the response from the server */
1662 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1666 /* Was there an error parsing the response line? */
1668 return CURLE_FTP_WEIRD_SERVER_REPLY;
1673 /* We have now received a full IMAP server response */
1674 switch(imapc->state) {
1675 case IMAP_SERVERGREET:
1676 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1679 case IMAP_CAPABILITY:
1680 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1684 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1687 case IMAP_AUTHENTICATE_PLAIN:
1688 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1691 case IMAP_AUTHENTICATE_LOGIN:
1692 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1695 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1696 result = imap_state_auth_login_password_resp(conn, imapcode,
1700 #ifndef CURL_DISABLE_CRYPTO_AUTH
1701 case IMAP_AUTHENTICATE_CRAMMD5:
1702 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1705 case IMAP_AUTHENTICATE_DIGESTMD5:
1706 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1709 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1710 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1715 case IMAP_AUTHENTICATE_NTLM:
1716 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1719 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1720 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1725 case IMAP_AUTHENTICATE_XOAUTH2:
1726 result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state);
1729 case IMAP_AUTHENTICATE_CANCEL:
1730 result = imap_state_auth_cancel_resp(conn, imapcode, imapc->state);
1733 case IMAP_AUTHENTICATE_FINAL:
1734 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1738 result = imap_state_login_resp(conn, imapcode, imapc->state);
1742 result = imap_state_list_resp(conn, imapcode, imapc->state);
1746 result = imap_state_select_resp(conn, imapcode, imapc->state);
1750 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1753 case IMAP_FETCH_FINAL:
1754 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1758 result = imap_state_append_resp(conn, imapcode, imapc->state);
1761 case IMAP_APPEND_FINAL:
1762 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1766 /* fallthrough, just stop! */
1768 /* internal error */
1769 state(conn, IMAP_STOP);
1772 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1777 /* Called repeatedly until done from multi.c */
1778 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1780 CURLcode result = CURLE_OK;
1781 struct imap_conn *imapc = &conn->proto.imapc;
1783 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1784 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1785 if(result || !imapc->ssldone)
1789 result = Curl_pp_statemach(&imapc->pp, FALSE);
1790 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1795 static CURLcode imap_block_statemach(struct connectdata *conn)
1797 CURLcode result = CURLE_OK;
1798 struct imap_conn *imapc = &conn->proto.imapc;
1800 while(imapc->state != IMAP_STOP && !result)
1801 result = Curl_pp_statemach(&imapc->pp, TRUE);
1806 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1808 static CURLcode imap_init(struct connectdata *conn)
1810 CURLcode result = CURLE_OK;
1811 struct SessionHandle *data = conn->data;
1814 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1816 result = CURLE_OUT_OF_MEMORY;
1821 /* For the IMAP "protocol connect" and "doing" phases only */
1822 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1825 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1828 /***********************************************************************
1832 * This function should do everything that is to be considered a part of the
1835 * The variable 'done' points to will be TRUE if the protocol-layer connect
1836 * phase is done when this function returns, or FALSE if not.
1838 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1840 CURLcode result = CURLE_OK;
1841 struct imap_conn *imapc = &conn->proto.imapc;
1842 struct pingpong *pp = &imapc->pp;
1844 *done = FALSE; /* default to not done yet */
1846 /* We always support persistent connections in IMAP */
1847 conn->bits.close = FALSE;
1849 /* Set the default response time-out */
1850 pp->response_time = RESP_TIMEOUT;
1851 pp->statemach_act = imap_statemach_act;
1852 pp->endofresp = imap_endofresp;
1855 /* Set the default preferred authentication mechanism */
1856 imapc->prefmech = SASL_AUTH_ANY;
1858 /* Initialise the pingpong layer */
1861 /* Parse the URL options */
1862 result = imap_parse_url_options(conn);
1866 /* Start off waiting for the server greeting response */
1867 state(conn, IMAP_SERVERGREET);
1869 /* Start off with an response id of '*' */
1870 strcpy(imapc->resptag, "*");
1872 result = imap_multi_statemach(conn, done);
1877 /***********************************************************************
1881 * The DONE function. This does what needs to be done after a single DO has
1884 * Input argument is already checked for validity.
1886 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1889 CURLcode result = CURLE_OK;
1890 struct SessionHandle *data = conn->data;
1891 struct IMAP *imap = data->req.protop;
1896 /* When the easy handle is removed from the multi interface while libcurl
1897 is still trying to resolve the host name, the IMAP struct is not yet
1898 initialized. However, the removal action calls Curl_done() which in
1899 turn calls this function, so we simply return success. */
1903 conn->bits.close = TRUE; /* marked for closure */
1904 result = status; /* use the already set error code */
1906 else if(!data->set.connect_only && !imap->custom &&
1907 (imap->uid || data->set.upload)) {
1908 /* Handle responses after FETCH or APPEND transfer has finished */
1909 if(!data->set.upload)
1910 state(conn, IMAP_FETCH_FINAL);
1912 /* End the APPEND command first by sending an empty line */
1913 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1915 state(conn, IMAP_APPEND_FINAL);
1918 /* Run the state-machine
1920 TODO: when the multi interface is used, this _really_ should be using
1921 the imap_multi_statemach function but we have no general support for
1922 non-blocking DONE operations, not in the multi state machine and with
1923 Curl_done() invokes on several places in the code!
1926 result = imap_block_statemach(conn);
1929 /* Cleanup our per-request based variables */
1930 Curl_safefree(imap->mailbox);
1931 Curl_safefree(imap->uidvalidity);
1932 Curl_safefree(imap->uid);
1933 Curl_safefree(imap->section);
1934 Curl_safefree(imap->custom);
1935 Curl_safefree(imap->custom_params);
1937 /* Clear the transfer mode for the next request */
1938 imap->transfer = FTPTRANSFER_BODY;
1943 /***********************************************************************
1947 * This is the actual DO function for IMAP. Fetch or append a message, or do
1948 * other things according to the options previously setup.
1950 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1953 /* This is IMAP and no proxy */
1954 CURLcode result = CURLE_OK;
1955 struct SessionHandle *data = conn->data;
1956 struct IMAP *imap = data->req.protop;
1957 struct imap_conn *imapc = &conn->proto.imapc;
1958 bool selected = FALSE;
1960 DEBUGF(infof(conn->data, "DO phase starts\n"));
1962 if(conn->data->set.opt_no_body) {
1963 /* Requested no body means no transfer */
1964 imap->transfer = FTPTRANSFER_INFO;
1967 *dophase_done = FALSE; /* not done yet */
1969 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1970 has already been selected on this connection */
1971 if(imap->mailbox && imapc->mailbox &&
1972 !strcmp(imap->mailbox, imapc->mailbox) &&
1973 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1974 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1977 /* Start the first command in the DO phase */
1978 if(conn->data->set.upload)
1979 /* APPEND can be executed directly */
1980 result = imap_perform_append(conn);
1981 else if(imap->custom && (selected || !imap->mailbox))
1982 /* Custom command using the same mailbox or no mailbox */
1983 result = imap_perform_list(conn);
1984 else if(!imap->custom && selected && imap->uid)
1985 /* FETCH from the same mailbox */
1986 result = imap_perform_fetch(conn);
1987 else if(imap->mailbox && !selected && (imap->custom || imap->uid))
1988 /* SELECT the mailbox */
1989 result = imap_perform_select(conn);
1992 result = imap_perform_list(conn);
1997 /* Run the state-machine */
1998 result = imap_multi_statemach(conn, dophase_done);
2000 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
2003 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2008 /***********************************************************************
2012 * This function is registered as 'curl_do' function. It decodes the path
2013 * parts etc as a wrapper to the actual DO function (imap_perform).
2015 * The input argument is already checked for validity.
2017 static CURLcode imap_do(struct connectdata *conn, bool *done)
2019 CURLcode result = CURLE_OK;
2021 *done = FALSE; /* default to false */
2023 /* Parse the URL path */
2024 result = imap_parse_url_path(conn);
2028 /* Parse the custom request */
2029 result = imap_parse_custom_request(conn);
2033 result = imap_regular_transfer(conn, done);
2038 /***********************************************************************
2042 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
2043 * resources. BLOCKING.
2045 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2047 struct imap_conn *imapc = &conn->proto.imapc;
2049 /* We cannot send quit unconditionally. If this connection is stale or
2050 bad in any way, sending quit and waiting around here will make the
2051 disconnect wait in vain and cause more problems than we need to. */
2053 /* The IMAP session may or may not have been allocated/setup at this
2055 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
2056 if(!imap_perform_logout(conn))
2057 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
2059 /* Disconnect from the server */
2060 Curl_pp_disconnect(&imapc->pp);
2062 /* Cleanup the SASL module */
2063 Curl_sasl_cleanup(conn, imapc->authused);
2065 /* Cleanup our connection based variables */
2066 Curl_safefree(imapc->mailbox);
2067 Curl_safefree(imapc->mailbox_uidvalidity);
2072 /* Call this when the DO phase has completed */
2073 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2075 struct IMAP *imap = conn->data->req.protop;
2079 if(imap->transfer != FTPTRANSFER_BODY)
2080 /* no data to transfer */
2081 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2086 /* Called from multi.c while DOing */
2087 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2089 CURLcode result = imap_multi_statemach(conn, dophase_done);
2092 DEBUGF(infof(conn->data, "DO phase failed\n"));
2093 else if(*dophase_done) {
2094 result = imap_dophase_done(conn, FALSE /* not connected */);
2096 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2102 /***********************************************************************
2104 * imap_regular_transfer()
2106 * The input argument is already checked for validity.
2108 * Performs all commands done before a regular transfer between a local and a
2111 static CURLcode imap_regular_transfer(struct connectdata *conn,
2114 CURLcode result = CURLE_OK;
2115 bool connected = FALSE;
2116 struct SessionHandle *data = conn->data;
2118 /* Make sure size is unknown at this point */
2119 data->req.size = -1;
2121 /* Set the progress data */
2122 Curl_pgrsSetUploadCounter(data, 0);
2123 Curl_pgrsSetDownloadCounter(data, 0);
2124 Curl_pgrsSetUploadSize(data, 0);
2125 Curl_pgrsSetDownloadSize(data, 0);
2127 /* Carry out the perform */
2128 result = imap_perform(conn, &connected, dophase_done);
2130 /* Perform post DO phase operations if necessary */
2131 if(!result && *dophase_done)
2132 result = imap_dophase_done(conn, connected);
2137 static CURLcode imap_setup_connection(struct connectdata *conn)
2139 struct SessionHandle *data = conn->data;
2141 /* Initialise the IMAP layer */
2142 CURLcode result = imap_init(conn);
2146 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2147 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2148 switch and use HTTP operations only */
2149 #ifndef CURL_DISABLE_HTTP
2150 if(conn->handler == &Curl_handler_imap)
2151 conn->handler = &Curl_handler_imap_proxy;
2154 conn->handler = &Curl_handler_imaps_proxy;
2156 failf(data, "IMAPS not supported!");
2157 return CURLE_UNSUPPORTED_PROTOCOL;
2161 /* set it up as an HTTP connection instead */
2162 return conn->handler->setup_connection(conn);
2164 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2165 return CURLE_UNSUPPORTED_PROTOCOL;
2169 data->state.path++; /* don't include the initial slash */
2174 /***********************************************************************
2178 * Sends the formated string as an IMAP command to the server.
2180 * Designed to never block.
2182 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
2184 CURLcode result = CURLE_OK;
2185 struct imap_conn *imapc = &conn->proto.imapc;
2191 /* Calculate the next command ID wrapping at 3 digits */
2192 imapc->cmdid = (imapc->cmdid + 1) % 1000;
2194 /* Calculate the tag based on the connection ID and command ID */
2195 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
2196 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
2198 /* Prefix the format with the tag */
2199 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
2201 return CURLE_OUT_OF_MEMORY;
2203 /* Send the data with the tag */
2205 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
2208 Curl_safefree(taggedfmt);
2213 /***********************************************************************
2217 * Checks the input string for characters that need escaping and returns an
2218 * atom ready for sending to the server.
2220 * The returned string needs to be freed.
2223 static char *imap_atom(const char *str)
2227 size_t backsp_count = 0;
2228 size_t quote_count = 0;
2229 bool space_exists = FALSE;
2231 char *newstr = NULL;
2236 /* Count any unescapped characters */
2244 space_exists = TRUE;
2249 /* Does the input contain any unescapped characters? */
2250 if(!backsp_count && !quote_count && !space_exists)
2253 /* Calculate the new string length */
2254 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
2256 /* Allocate the new string */
2257 newstr = (char *) malloc((newlen + 1) * sizeof(char));
2261 /* Surround the string in quotes if necessary */
2265 newstr[newlen - 1] = '"';
2269 /* Copy the string, escaping backslash and quote characters along the way */
2272 if(*p1 == '\\' || *p1 == '"') {
2283 /* Terminate the string */
2284 newstr[newlen] = '\0';
2289 /***********************************************************************
2293 * Portable test of whether the specified char is a "bchar" as defined in the
2294 * grammar of RFC-5092.
2296 static bool imap_is_bchar(char ch)
2300 case ':': case '@': case '/':
2301 /* bchar -> achar */
2303 /* bchar -> achar -> uchar -> unreserved */
2304 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2305 case '7': case '8': case '9':
2306 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2307 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2308 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2309 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2310 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2311 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2312 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2313 case 'v': case 'w': case 'x': case 'y': case 'z':
2314 case '-': case '.': case '_': case '~':
2315 /* bchar -> achar -> uchar -> sub-delims-sh */
2316 case '!': case '$': case '\'': case '(': case ')': case '*':
2318 /* bchar -> achar -> uchar -> pct-encoded */
2319 case '%': /* HEXDIG chars are already included above */
2327 /***********************************************************************
2329 * imap_parse_url_options()
2331 * Parse the URL login options.
2333 static CURLcode imap_parse_url_options(struct connectdata *conn)
2335 CURLcode result = CURLE_OK;
2336 struct imap_conn *imapc = &conn->proto.imapc;
2337 const char *options = conn->options;
2338 const char *ptr = options;
2341 const char *key = ptr;
2343 while(*ptr && *ptr != '=')
2346 if(strnequal(key, "AUTH", 4)) {
2347 const char *value = ptr + 1;
2349 if(strequal(value, "*"))
2350 imapc->prefmech = SASL_AUTH_ANY;
2351 else if(strequal(value, SASL_MECH_STRING_LOGIN))
2352 imapc->prefmech = SASL_MECH_LOGIN;
2353 else if(strequal(value, SASL_MECH_STRING_PLAIN))
2354 imapc->prefmech = SASL_MECH_PLAIN;
2355 else if(strequal(value, SASL_MECH_STRING_CRAM_MD5))
2356 imapc->prefmech = SASL_MECH_CRAM_MD5;
2357 else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5))
2358 imapc->prefmech = SASL_MECH_DIGEST_MD5;
2359 else if(strequal(value, SASL_MECH_STRING_GSSAPI))
2360 imapc->prefmech = SASL_MECH_GSSAPI;
2361 else if(strequal(value, SASL_MECH_STRING_NTLM))
2362 imapc->prefmech = SASL_MECH_NTLM;
2363 else if(strequal(value, SASL_MECH_STRING_XOAUTH2))
2364 imapc->prefmech = SASL_MECH_XOAUTH2;
2366 imapc->prefmech = SASL_AUTH_NONE;
2369 result = CURLE_URL_MALFORMAT;
2375 /***********************************************************************
2377 * imap_parse_url_path()
2379 * Parse the URL path into separate path components.
2382 static CURLcode imap_parse_url_path(struct connectdata *conn)
2384 /* The imap struct is already initialised in imap_connect() */
2385 CURLcode result = CURLE_OK;
2386 struct SessionHandle *data = conn->data;
2387 struct IMAP *imap = data->req.protop;
2388 const char *begin = data->state.path;
2389 const char *ptr = begin;
2391 /* See how much of the URL is a valid path and decode it */
2392 while(imap_is_bchar(*ptr))
2396 /* Remove the trailing slash if present */
2397 const char *end = ptr;
2398 if(end > begin && end[-1] == '/')
2401 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2407 imap->mailbox = NULL;
2409 /* There can be any number of parameters in the form ";NAME=VALUE" */
2410 while(*ptr == ';') {
2415 /* Find the length of the name parameter */
2417 while(*ptr && *ptr != '=')
2421 return CURLE_URL_MALFORMAT;
2423 /* Decode the name parameter */
2424 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2428 /* Find the length of the value parameter */
2430 while(imap_is_bchar(*ptr))
2433 /* Decode the value parameter */
2434 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2436 Curl_safefree(name);
2440 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2442 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2443 stripping of the trailing slash character if it is present.
2445 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2446 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2447 if(valuelen > 0 && value[valuelen - 1] == '/')
2448 value[valuelen - 1] = '\0';
2450 imap->uidvalidity = value;
2453 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2454 if(valuelen > 0 && value[valuelen - 1] == '/')
2455 value[valuelen - 1] = '\0';
2460 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2461 if(valuelen > 0 && value[valuelen - 1] == '/')
2462 value[valuelen - 1] = '\0';
2464 imap->section = value;
2468 Curl_safefree(name);
2469 Curl_safefree(value);
2471 return CURLE_URL_MALFORMAT;
2474 Curl_safefree(name);
2475 Curl_safefree(value);
2478 /* Any extra stuff at the end of the URL is an error */
2480 return CURLE_URL_MALFORMAT;
2485 /***********************************************************************
2487 * imap_parse_custom_request()
2489 * Parse the custom request.
2491 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2493 CURLcode result = CURLE_OK;
2494 struct SessionHandle *data = conn->data;
2495 struct IMAP *imap = data->req.protop;
2496 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2499 /* URL decode the custom request */
2500 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2502 /* Extract the parameters if specified */
2504 const char *params = imap->custom;
2506 while(*params && *params != ' ')
2510 imap->custom_params = strdup(params);
2511 imap->custom[params - imap->custom] = '\0';
2513 if(!imap->custom_params)
2514 result = CURLE_OUT_OF_MEMORY;
2522 #endif /* CURL_DISABLE_IMAP */