1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, 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"
74 #include "vtls/vtls.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);
108 static CURLcode imap_calc_sasl_details(struct connectdata *conn,
110 char **initresp, size_t *len,
111 imapstate *state1, imapstate *state2);
114 * IMAP protocol handler.
117 const struct Curl_handler Curl_handler_imap = {
119 imap_setup_connection, /* setup_connection */
121 imap_done, /* done */
122 ZERO_NULL, /* do_more */
123 imap_connect, /* connect_it */
124 imap_multi_statemach, /* connecting */
125 imap_doing, /* doing */
126 imap_getsock, /* proto_getsock */
127 imap_getsock, /* doing_getsock */
128 ZERO_NULL, /* domore_getsock */
129 ZERO_NULL, /* perform_getsock */
130 imap_disconnect, /* disconnect */
131 ZERO_NULL, /* readwrite */
132 PORT_IMAP, /* defport */
133 CURLPROTO_IMAP, /* protocol */
134 PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD /* flags */
139 * IMAPS protocol handler.
142 const struct Curl_handler Curl_handler_imaps = {
143 "IMAPS", /* scheme */
144 imap_setup_connection, /* setup_connection */
146 imap_done, /* done */
147 ZERO_NULL, /* do_more */
148 imap_connect, /* connect_it */
149 imap_multi_statemach, /* connecting */
150 imap_doing, /* doing */
151 imap_getsock, /* proto_getsock */
152 imap_getsock, /* doing_getsock */
153 ZERO_NULL, /* domore_getsock */
154 ZERO_NULL, /* perform_getsock */
155 imap_disconnect, /* disconnect */
156 ZERO_NULL, /* readwrite */
157 PORT_IMAPS, /* defport */
158 CURLPROTO_IMAPS, /* protocol */
159 PROTOPT_CLOSEACTION | PROTOPT_SSL |
160 PROTOPT_NEEDSPWD /* flags */
164 #ifndef CURL_DISABLE_HTTP
166 * HTTP-proxyed IMAP protocol handler.
169 static const struct Curl_handler Curl_handler_imap_proxy = {
171 Curl_http_setup_conn, /* setup_connection */
172 Curl_http, /* do_it */
173 Curl_http_done, /* done */
174 ZERO_NULL, /* do_more */
175 ZERO_NULL, /* connect_it */
176 ZERO_NULL, /* connecting */
177 ZERO_NULL, /* doing */
178 ZERO_NULL, /* proto_getsock */
179 ZERO_NULL, /* doing_getsock */
180 ZERO_NULL, /* domore_getsock */
181 ZERO_NULL, /* perform_getsock */
182 ZERO_NULL, /* disconnect */
183 ZERO_NULL, /* readwrite */
184 PORT_IMAP, /* defport */
185 CURLPROTO_HTTP, /* protocol */
186 PROTOPT_NONE /* flags */
191 * HTTP-proxyed IMAPS protocol handler.
194 static const struct Curl_handler Curl_handler_imaps_proxy = {
195 "IMAPS", /* scheme */
196 Curl_http_setup_conn, /* setup_connection */
197 Curl_http, /* do_it */
198 Curl_http_done, /* done */
199 ZERO_NULL, /* do_more */
200 ZERO_NULL, /* connect_it */
201 ZERO_NULL, /* connecting */
202 ZERO_NULL, /* doing */
203 ZERO_NULL, /* proto_getsock */
204 ZERO_NULL, /* doing_getsock */
205 ZERO_NULL, /* domore_getsock */
206 ZERO_NULL, /* perform_getsock */
207 ZERO_NULL, /* disconnect */
208 ZERO_NULL, /* readwrite */
209 PORT_IMAPS, /* defport */
210 CURLPROTO_HTTP, /* protocol */
211 PROTOPT_NONE /* flags */
217 static void imap_to_imaps(struct connectdata *conn)
219 conn->handler = &Curl_handler_imaps;
222 #define imap_to_imaps(x) Curl_nop_stmt
225 /***********************************************************************
229 * Determines whether the untagged response is related to the specified
230 * command by checking if it is in format "* <command-name> ..." or
231 * "* <number> <command-name> ...".
233 * The "* " marker is assumed to have already been checked by the caller.
235 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
237 const char *end = line + len;
238 size_t cmd_len = strlen(cmd);
240 /* Skip the untagged response marker */
243 /* Do we have a number after the marker? */
244 if(line < end && ISDIGIT(*line)) {
245 /* Skip the number */
248 while(line < end && ISDIGIT(*line));
250 /* Do we have the space character? */
251 if(line == end || *line != ' ')
257 /* Does the command name match and is it followed by a space character or at
259 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
260 (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
266 /***********************************************************************
270 * Checks whether the given string is a valid tagged, untagged or continuation
271 * response which can be processed by the response handler.
273 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
276 struct IMAP *imap = conn->data->req.protop;
277 struct imap_conn *imapc = &conn->proto.imapc;
278 const char *id = imapc->resptag;
279 size_t id_len = strlen(id);
281 /* Do we have a tagged command response? */
282 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
286 if(len >= 2 && !memcmp(line, "OK", 2))
288 else if(len >= 2 && !memcmp(line, "NO", 2))
290 else if(len >= 3 && !memcmp(line, "BAD", 3))
293 failf(conn->data, "Bad tagged response");
300 /* Do we have an untagged command response? */
301 if(len >= 2 && !memcmp("* ", line, 2)) {
302 switch(imapc->state) {
303 /* States which are interested in untagged responses */
304 case IMAP_CAPABILITY:
305 if(!imap_matchresp(line, len, "CAPABILITY"))
310 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
311 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
312 (strcmp(imap->custom, "STORE") ||
313 !imap_matchresp(line, len, "FETCH")) &&
314 strcmp(imap->custom, "SELECT") &&
315 strcmp(imap->custom, "EXAMINE") &&
316 strcmp(imap->custom, "SEARCH") &&
317 strcmp(imap->custom, "EXPUNGE") &&
318 strcmp(imap->custom, "LSUB") &&
319 strcmp(imap->custom, "UID") &&
320 strcmp(imap->custom, "NOOP")))
325 /* SELECT is special in that its untagged responses do not have a
326 common prefix so accept anything! */
330 if(!imap_matchresp(line, len, "FETCH"))
335 if(!imap_matchresp(line, len, "SEARCH"))
339 /* Ignore other untagged responses */
348 /* Do we have a continuation response? This should be a + symbol followed by
349 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
350 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
351 some e-mail servers ignore this and only send a single + instead. */
352 if((len == 3 && !memcmp("+", line, 1)) ||
353 (len >= 2 && !memcmp("+ ", line, 2))) {
354 switch(imapc->state) {
355 /* States which are interested in continuation responses */
356 case IMAP_AUTHENTICATE_PLAIN:
357 case IMAP_AUTHENTICATE_LOGIN:
358 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
359 case IMAP_AUTHENTICATE_CRAMMD5:
360 case IMAP_AUTHENTICATE_DIGESTMD5:
361 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
362 case IMAP_AUTHENTICATE_NTLM:
363 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
364 case IMAP_AUTHENTICATE_XOAUTH2:
365 case IMAP_AUTHENTICATE_FINAL:
371 failf(conn->data, "Unexpected continuation response");
379 return FALSE; /* Nothing for us */
382 /***********************************************************************
386 * Gets the authentication message from the response buffer.
388 static void imap_get_message(char *buffer, char** outptr)
391 char* message = NULL;
393 /* Find the start of the message */
394 for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
397 /* Find the end of the message */
398 for(len = strlen(message); len--;)
399 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
400 message[len] != '\t')
403 /* Terminate the message */
411 /***********************************************************************
415 * This is the ONLY way to change IMAP state!
417 static void state(struct connectdata *conn, imapstate newstate)
419 struct imap_conn *imapc = &conn->proto.imapc;
420 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
421 /* for debug purposes */
422 static const char * const names[]={
428 "AUTHENTICATE_PLAIN",
429 "AUTHENTICATE_LOGIN",
430 "AUTHENTICATE_LOGIN_PASSWD",
431 "AUTHENTICATE_CRAMMD5",
432 "AUTHENTICATE_DIGESTMD5",
433 "AUTHENTICATE_DIGESTMD5_RESP",
435 "AUTHENTICATE_NTLM_TYPE2MSG",
436 "AUTHENTICATE_XOAUTH2",
437 "AUTHENTICATE_CANCEL",
438 "AUTHENTICATE_FINAL",
451 if(imapc->state != newstate)
452 infof(conn->data, "IMAP %p state change from %s to %s\n",
453 (void *)imapc, names[imapc->state], names[newstate]);
456 imapc->state = newstate;
459 /***********************************************************************
461 * imap_perform_capability()
463 * Sends the CAPABILITY command in order to obtain a list of server side
464 * supported capabilities.
466 static CURLcode imap_perform_capability(struct connectdata *conn)
468 CURLcode result = CURLE_OK;
469 struct imap_conn *imapc = &conn->proto.imapc;
471 imapc->authmechs = 0; /* No known authentication mechanisms yet */
472 imapc->authused = 0; /* Clear the authentication mechanism used */
473 imapc->tls_supported = FALSE; /* Clear the TLS capability */
475 /* Send the CAPABILITY command */
476 result = imap_sendf(conn, "CAPABILITY");
479 state(conn, IMAP_CAPABILITY);
484 /***********************************************************************
486 * imap_perform_starttls()
488 * Sends the STARTTLS command to start the upgrade to TLS.
490 static CURLcode imap_perform_starttls(struct connectdata *conn)
492 CURLcode result = CURLE_OK;
494 /* Send the STARTTLS command */
495 result = imap_sendf(conn, "STARTTLS");
498 state(conn, IMAP_STARTTLS);
503 /***********************************************************************
505 * imap_perform_upgrade_tls()
507 * Performs the upgrade to TLS.
509 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
511 CURLcode result = CURLE_OK;
512 struct imap_conn *imapc = &conn->proto.imapc;
514 /* Start the SSL connection */
515 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
518 if(imapc->state != IMAP_UPGRADETLS)
519 state(conn, IMAP_UPGRADETLS);
523 result = imap_perform_capability(conn);
530 /***********************************************************************
532 * imap_perform_login()
534 * Sends a clear text LOGIN command to authenticate with.
536 static CURLcode imap_perform_login(struct connectdata *conn)
538 CURLcode result = CURLE_OK;
542 /* Check we have a username and password to authenticate with and end the
543 connect phase if we don't */
544 if(!conn->bits.user_passwd) {
545 state(conn, IMAP_STOP);
550 /* Make sure the username and password are in the correct atom format */
551 user = imap_atom(conn->user);
552 passwd = imap_atom(conn->passwd);
554 /* Send the LOGIN command */
555 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
556 passwd ? passwd : "");
559 Curl_safefree(passwd);
562 state(conn, IMAP_LOGIN);
567 /***********************************************************************
569 * imap_perform_authenticate()
571 * Sends an AUTHENTICATE command allowing the client to login with the given
572 * SASL authentication mechanism.
574 static CURLcode imap_perform_authenticate(struct connectdata *conn,
576 const char *initresp,
577 imapstate state1, imapstate state2)
579 CURLcode result = CURLE_OK;
582 /* Send the AUTHENTICATE command with the initial response */
583 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
589 /* Send the AUTHENTICATE command */
590 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
599 /***********************************************************************
601 * imap_perform_authentication()
603 * Initiates the authentication sequence, with the appropriate SASL
604 * authentication mechanism, falling back to clear text should a common
605 * mechanism not be available between the client and server.
607 static CURLcode imap_perform_authentication(struct connectdata *conn)
609 CURLcode result = CURLE_OK;
610 struct imap_conn *imapc = &conn->proto.imapc;
611 const char *mech = NULL;
612 char *initresp = NULL;
614 imapstate state1 = IMAP_STOP;
615 imapstate state2 = IMAP_STOP;
617 /* Check we have a username and password to authenticate with and end the
618 connect phase if we don't */
619 if(!conn->bits.user_passwd) {
620 state(conn, IMAP_STOP);
625 /* Calculate the SASL login details */
626 result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
630 if(mech && (imapc->preftype & IMAP_TYPE_SASL)) {
631 /* Perform SASL based authentication */
632 result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
634 Curl_safefree(initresp);
636 else if((!imapc->login_disabled) &&
637 (imapc->preftype & IMAP_TYPE_CLEARTEXT))
638 /* Perform clear text authentication */
639 result = imap_perform_login(conn);
641 /* Other mechanisms not supported */
642 infof(conn->data, "No known authentication mechanisms supported!\n");
643 result = CURLE_LOGIN_DENIED;
650 /***********************************************************************
652 * imap_perform_list()
654 * Sends a LIST command or an alternative custom request.
656 static CURLcode imap_perform_list(struct connectdata *conn)
658 CURLcode result = CURLE_OK;
659 struct SessionHandle *data = conn->data;
660 struct IMAP *imap = data->req.protop;
664 /* Send the custom request */
665 result = imap_sendf(conn, "%s%s", imap->custom,
666 imap->custom_params ? imap->custom_params : "");
668 /* Make sure the mailbox is in the correct atom format */
669 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
671 return CURLE_OUT_OF_MEMORY;
673 /* Send the LIST command */
674 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
676 Curl_safefree(mailbox);
680 state(conn, IMAP_LIST);
685 /***********************************************************************
687 * imap_perform_select()
689 * Sends a SELECT command to ask the server to change the selected mailbox.
691 static CURLcode imap_perform_select(struct connectdata *conn)
693 CURLcode result = CURLE_OK;
694 struct SessionHandle *data = conn->data;
695 struct IMAP *imap = data->req.protop;
696 struct imap_conn *imapc = &conn->proto.imapc;
699 /* Invalidate old information as we are switching mailboxes */
700 Curl_safefree(imapc->mailbox);
701 Curl_safefree(imapc->mailbox_uidvalidity);
703 /* Check we have a mailbox */
705 failf(conn->data, "Cannot SELECT without a mailbox.");
706 return CURLE_URL_MALFORMAT;
709 /* Make sure the mailbox is in the correct atom format */
710 mailbox = imap_atom(imap->mailbox);
712 return CURLE_OUT_OF_MEMORY;
714 /* Send the SELECT command */
715 result = imap_sendf(conn, "SELECT %s", mailbox);
717 Curl_safefree(mailbox);
720 state(conn, IMAP_SELECT);
725 /***********************************************************************
727 * imap_perform_fetch()
729 * Sends a FETCH command to initiate the download of a message.
731 static CURLcode imap_perform_fetch(struct connectdata *conn)
733 CURLcode result = CURLE_OK;
734 struct IMAP *imap = conn->data->req.protop;
736 /* Check we have a UID */
738 failf(conn->data, "Cannot FETCH without a UID.");
739 return CURLE_URL_MALFORMAT;
742 /* Send the FETCH command */
744 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
746 imap->section ? imap->section : "",
749 result = imap_sendf(conn, "FETCH %s BODY[%s]",
751 imap->section ? imap->section : "");
754 state(conn, IMAP_FETCH);
759 /***********************************************************************
761 * imap_perform_append()
763 * Sends an APPEND command to initiate the upload of a message.
765 static CURLcode imap_perform_append(struct connectdata *conn)
767 CURLcode result = CURLE_OK;
768 struct IMAP *imap = conn->data->req.protop;
771 /* Check we have a mailbox */
773 failf(conn->data, "Cannot APPEND without a mailbox.");
774 return CURLE_URL_MALFORMAT;
777 /* Check we know the size of the upload */
778 if(conn->data->state.infilesize < 0) {
779 failf(conn->data, "Cannot APPEND with unknown input file size\n");
780 return CURLE_UPLOAD_FAILED;
783 /* Make sure the mailbox is in the correct atom format */
784 mailbox = imap_atom(imap->mailbox);
786 return CURLE_OUT_OF_MEMORY;
788 /* Send the APPEND command */
789 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
790 mailbox, conn->data->state.infilesize);
792 Curl_safefree(mailbox);
795 state(conn, IMAP_APPEND);
800 /***********************************************************************
802 * imap_perform_search()
804 * Sends a SEARCH command.
806 static CURLcode imap_perform_search(struct connectdata *conn)
808 CURLcode result = CURLE_OK;
809 struct IMAP *imap = conn->data->req.protop;
811 /* Check we have a query string */
813 failf(conn->data, "Cannot SEARCH without a query string.");
814 return CURLE_URL_MALFORMAT;
817 /* Send the SEARCH command */
818 result = imap_sendf(conn, "SEARCH %s", imap->query);
821 state(conn, IMAP_SEARCH);
826 /***********************************************************************
828 * imap_perform_logout()
830 * Performs the logout action prior to sclose() being called.
832 static CURLcode imap_perform_logout(struct connectdata *conn)
834 CURLcode result = CURLE_OK;
836 /* Send the LOGOUT command */
837 result = imap_sendf(conn, "LOGOUT");
840 state(conn, IMAP_LOGOUT);
845 /* For the initial server greeting */
846 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
850 CURLcode result = CURLE_OK;
851 struct SessionHandle *data = conn->data;
853 (void)instate; /* no use for this yet */
855 if(imapcode != 'O') {
856 failf(data, "Got unexpected imap-server response");
857 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
860 result = imap_perform_capability(conn);
865 /* For CAPABILITY responses */
866 static CURLcode imap_state_capability_resp(struct connectdata *conn,
870 CURLcode result = CURLE_OK;
871 struct SessionHandle *data = conn->data;
872 struct imap_conn *imapc = &conn->proto.imapc;
873 const char *line = data->state.buffer;
876 (void)instate; /* no use for this yet */
878 /* Do we have a untagged response? */
879 if(imapcode == '*') {
882 /* Loop through the data line */
885 (*line == ' ' || *line == '\t' ||
886 *line == '\r' || *line == '\n')) {
894 /* Extract the word */
895 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
896 line[wordlen] != '\t' && line[wordlen] != '\r' &&
897 line[wordlen] != '\n';)
900 /* Does the server support the STARTTLS capability? */
901 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
902 imapc->tls_supported = TRUE;
904 /* Has the server explicitly disabled clear text authentication? */
905 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
906 imapc->login_disabled = TRUE;
908 /* Does the server support the SASL-IR capability? */
909 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
910 imapc->ir_supported = TRUE;
912 /* Do we have a SASL based authentication mechanism? */
913 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
917 /* Test the word for a matching authentication mechanism */
918 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
919 imapc->authmechs |= SASL_MECH_LOGIN;
920 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
921 imapc->authmechs |= SASL_MECH_PLAIN;
922 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
923 imapc->authmechs |= SASL_MECH_CRAM_MD5;
924 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
925 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
926 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
927 imapc->authmechs |= SASL_MECH_GSSAPI;
928 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
929 imapc->authmechs |= SASL_MECH_EXTERNAL;
930 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
931 imapc->authmechs |= SASL_MECH_NTLM;
932 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
933 imapc->authmechs |= SASL_MECH_XOAUTH2;
939 else if(imapcode == 'O') {
940 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
941 /* We don't have a SSL/TLS connection yet, but SSL is requested */
942 if(imapc->tls_supported)
943 /* Switch to TLS connection now */
944 result = imap_perform_starttls(conn);
945 else if(data->set.use_ssl == CURLUSESSL_TRY)
946 /* Fallback and carry on with authentication */
947 result = imap_perform_authentication(conn);
949 failf(data, "STARTTLS not supported.");
950 result = CURLE_USE_SSL_FAILED;
954 result = imap_perform_authentication(conn);
957 result = imap_perform_authentication(conn);
962 /* For STARTTLS responses */
963 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
967 CURLcode result = CURLE_OK;
968 struct SessionHandle *data = conn->data;
970 (void)instate; /* no use for this yet */
972 if(imapcode != 'O') {
973 if(data->set.use_ssl != CURLUSESSL_TRY) {
974 failf(data, "STARTTLS denied. %c", imapcode);
975 result = CURLE_USE_SSL_FAILED;
978 result = imap_perform_authentication(conn);
981 result = imap_perform_upgrade_tls(conn);
986 /* For AUTHENTICATE PLAIN (without initial response) responses */
987 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
991 CURLcode result = CURLE_OK;
992 struct SessionHandle *data = conn->data;
994 char *plainauth = NULL;
996 (void)instate; /* no use for this yet */
998 if(imapcode != '+') {
999 failf(data, "Access denied. %c", imapcode);
1000 result = CURLE_LOGIN_DENIED;
1003 /* Create the authorisation message */
1004 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
1006 if(!result && plainauth) {
1007 /* Send the message */
1008 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
1011 state(conn, IMAP_AUTHENTICATE_FINAL);
1015 Curl_safefree(plainauth);
1020 /* For AUTHENTICATE LOGIN (without initial response) responses */
1021 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1025 CURLcode result = CURLE_OK;
1026 struct SessionHandle *data = conn->data;
1028 char *authuser = NULL;
1030 (void)instate; /* no use for this yet */
1032 if(imapcode != '+') {
1033 failf(data, "Access denied: %d", imapcode);
1034 result = CURLE_LOGIN_DENIED;
1037 /* Create the user message */
1038 result = Curl_sasl_create_login_message(data, conn->user,
1040 if(!result && authuser) {
1042 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1045 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1049 Curl_safefree(authuser);
1054 /* For AUTHENTICATE LOGIN user entry responses */
1055 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1059 CURLcode result = CURLE_OK;
1060 struct SessionHandle *data = conn->data;
1062 char *authpasswd = NULL;
1064 (void)instate; /* no use for this yet */
1066 if(imapcode != '+') {
1067 failf(data, "Access denied: %d", imapcode);
1068 result = CURLE_LOGIN_DENIED;
1071 /* Create the password message */
1072 result = Curl_sasl_create_login_message(data, conn->passwd,
1074 if(!result && authpasswd) {
1075 /* Send the password */
1076 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1079 state(conn, IMAP_AUTHENTICATE_FINAL);
1083 Curl_safefree(authpasswd);
1088 #ifndef CURL_DISABLE_CRYPTO_AUTH
1089 /* For AUTHENTICATE CRAM-MD5 responses */
1090 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1094 CURLcode result = CURLE_OK;
1095 struct SessionHandle *data = conn->data;
1097 char *chlg64 = NULL;
1098 char *rplyb64 = NULL;
1101 (void)instate; /* no use for this yet */
1103 if(imapcode != '+') {
1104 failf(data, "Access denied: %d", imapcode);
1105 return CURLE_LOGIN_DENIED;
1108 /* Get the challenge message */
1109 imap_get_message(data->state.buffer, &chlg64);
1111 /* Decode the challenge message */
1112 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
1114 /* Send the cancellation */
1115 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1118 state(conn, IMAP_AUTHENTICATE_CANCEL);
1121 /* Create the response message */
1122 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
1123 conn->passwd, &rplyb64, &len);
1124 if(!result && rplyb64) {
1125 /* Send the response */
1126 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1129 state(conn, IMAP_AUTHENTICATE_FINAL);
1133 Curl_safefree(chlg);
1134 Curl_safefree(rplyb64);
1139 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1140 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1144 CURLcode result = CURLE_OK;
1145 struct SessionHandle *data = conn->data;
1146 char *chlg64 = NULL;
1147 char *rplyb64 = NULL;
1150 (void)instate; /* no use for this yet */
1152 if(imapcode != '+') {
1153 failf(data, "Access denied: %d", imapcode);
1154 return CURLE_LOGIN_DENIED;
1157 /* Get the challenge message */
1158 imap_get_message(data->state.buffer, &chlg64);
1160 /* Create the response message */
1161 result = Curl_sasl_create_digest_md5_message(data, chlg64,
1162 conn->user, conn->passwd,
1163 "imap", &rplyb64, &len);
1165 if(result == CURLE_BAD_CONTENT_ENCODING) {
1166 /* Send the cancellation */
1167 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1170 state(conn, IMAP_AUTHENTICATE_CANCEL);
1174 /* Send the response */
1175 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1178 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1181 Curl_safefree(rplyb64);
1186 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1187 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1191 CURLcode result = CURLE_OK;
1192 struct SessionHandle *data = conn->data;
1194 (void)instate; /* no use for this yet */
1196 if(imapcode != '+') {
1197 failf(data, "Authentication failed: %d", imapcode);
1198 result = CURLE_LOGIN_DENIED;
1201 /* Send an empty response */
1202 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1205 state(conn, IMAP_AUTHENTICATE_FINAL);
1213 /* For AUTHENTICATE NTLM (without initial response) responses */
1214 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1218 CURLcode result = CURLE_OK;
1219 struct SessionHandle *data = conn->data;
1221 char *type1msg = NULL;
1223 (void)instate; /* no use for this yet */
1225 if(imapcode != '+') {
1226 failf(data, "Access denied: %d", imapcode);
1227 result = CURLE_LOGIN_DENIED;
1230 /* Create the type-1 message */
1231 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1234 if(!result && type1msg) {
1235 /* Send the message */
1236 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1239 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1243 Curl_safefree(type1msg);
1248 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1249 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1253 CURLcode result = CURLE_OK;
1254 struct SessionHandle *data = conn->data;
1255 char *type2msg = NULL;
1256 char *type3msg = NULL;
1259 (void)instate; /* no use for this yet */
1261 if(imapcode != '+') {
1262 failf(data, "Access denied: %d", imapcode);
1263 result = CURLE_LOGIN_DENIED;
1266 /* Get the challenge message */
1267 imap_get_message(data->state.buffer, &type2msg);
1269 /* Decode the type-2 message */
1270 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1272 /* Send the cancellation */
1273 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1276 state(conn, IMAP_AUTHENTICATE_CANCEL);
1279 /* Create the type-3 message */
1280 result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1281 conn->passwd, &conn->ntlm,
1283 if(!result && type3msg) {
1284 /* Send the message */
1285 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1288 state(conn, IMAP_AUTHENTICATE_FINAL);
1293 Curl_safefree(type3msg);
1299 /* For AUTHENTICATE XOAUTH2 (without initial response) responses */
1300 static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn,
1304 CURLcode result = CURLE_OK;
1305 struct SessionHandle *data = conn->data;
1307 char *xoauth = NULL;
1309 (void)instate; /* no use for this yet */
1311 if(imapcode != '+') {
1312 failf(data, "Access denied: %d", imapcode);
1313 result = CURLE_LOGIN_DENIED;
1316 /* Create the authorisation message */
1317 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1318 conn->xoauth2_bearer,
1320 if(!result && xoauth) {
1321 /* Send the message */
1322 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth);
1325 state(conn, IMAP_AUTHENTICATE_FINAL);
1329 Curl_safefree(xoauth);
1334 /* For AUTHENTICATE cancellation responses */
1335 static CURLcode imap_state_auth_cancel_resp(struct connectdata *conn,
1339 CURLcode result = CURLE_OK;
1340 struct SessionHandle *data = conn->data;
1341 struct imap_conn *imapc = &conn->proto.imapc;
1342 const char *mech = NULL;
1343 char *initresp = NULL;
1345 imapstate state1 = IMAP_STOP;
1346 imapstate state2 = IMAP_STOP;
1349 (void)instate; /* no use for this yet */
1351 /* Remove the offending mechanism from the supported list */
1352 imapc->authmechs ^= imapc->authused;
1354 /* Calculate alternative SASL login details */
1355 result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1359 /* Do we have any mechanisms left or can we fallback to clear text? */
1361 /* Retry SASL based authentication */
1362 result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
1364 Curl_safefree(initresp);
1366 else if((!imapc->login_disabled) &&
1367 (imapc->preftype & IMAP_TYPE_CLEARTEXT))
1368 /* Perform clear text authentication */
1369 result = imap_perform_login(conn);
1371 failf(data, "Authentication cancelled");
1373 result = CURLE_LOGIN_DENIED;
1380 /* For final responses in the AUTHENTICATE sequence */
1381 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1385 CURLcode result = CURLE_OK;
1386 struct SessionHandle *data = conn->data;
1388 (void)instate; /* no use for this yet */
1390 if(imapcode != 'O') {
1391 failf(data, "Authentication failed: %d", imapcode);
1392 result = CURLE_LOGIN_DENIED;
1395 /* End of connect phase */
1396 state(conn, IMAP_STOP);
1401 /* For LOGIN responses */
1402 static CURLcode imap_state_login_resp(struct connectdata *conn,
1406 CURLcode result = CURLE_OK;
1407 struct SessionHandle *data = conn->data;
1409 (void)instate; /* no use for this yet */
1411 if(imapcode != 'O') {
1412 failf(data, "Access denied. %c", imapcode);
1413 result = CURLE_LOGIN_DENIED;
1416 /* End of connect phase */
1417 state(conn, IMAP_STOP);
1422 /* For LIST responses */
1423 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1426 CURLcode result = CURLE_OK;
1427 char *line = conn->data->state.buffer;
1428 size_t len = strlen(line);
1430 (void)instate; /* No use for this yet */
1432 if(imapcode == '*') {
1433 /* Temporarily add the LF character back and send as body to the client */
1435 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1438 else if(imapcode != 'O')
1439 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1441 /* End of DO phase */
1442 state(conn, IMAP_STOP);
1447 /* For SELECT responses */
1448 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1451 CURLcode result = CURLE_OK;
1452 struct SessionHandle *data = conn->data;
1453 struct IMAP *imap = conn->data->req.protop;
1454 struct imap_conn *imapc = &conn->proto.imapc;
1455 const char *line = data->state.buffer;
1458 (void)instate; /* no use for this yet */
1460 if(imapcode == '*') {
1461 /* See if this is an UIDVALIDITY response */
1462 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1463 Curl_safefree(imapc->mailbox_uidvalidity);
1464 imapc->mailbox_uidvalidity = strdup(tmp);
1467 else if(imapcode == 'O') {
1468 /* Check if the UIDVALIDITY has been specified and matches */
1469 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1470 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1471 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1472 result = CURLE_REMOTE_FILE_NOT_FOUND;
1475 /* Note the currently opened mailbox on this connection */
1476 imapc->mailbox = strdup(imap->mailbox);
1479 result = imap_perform_list(conn);
1480 else if(imap->query)
1481 result = imap_perform_search(conn);
1483 result = imap_perform_fetch(conn);
1487 failf(data, "Select failed");
1488 result = CURLE_LOGIN_DENIED;
1494 /* For the (first line of the) FETCH responses */
1495 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1498 CURLcode result = CURLE_OK;
1499 struct SessionHandle *data = conn->data;
1500 struct imap_conn *imapc = &conn->proto.imapc;
1501 struct pingpong *pp = &imapc->pp;
1502 const char *ptr = data->state.buffer;
1503 bool parsed = FALSE;
1506 (void)instate; /* no use for this yet */
1508 if(imapcode != '*') {
1509 Curl_pgrsSetDownloadSize(data, 0);
1510 state(conn, IMAP_STOP);
1511 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1514 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1515 the continuation data contained within the curly brackets */
1516 while(*ptr && (*ptr != '{'))
1521 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1522 if(endptr - ptr > 1 && endptr[0] == '}' &&
1523 endptr[1] == '\r' && endptr[2] == '\0')
1528 infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1530 Curl_pgrsSetDownloadSize(data, size);
1533 /* At this point there is a bunch of data in the header "cache" that is
1534 actually body content, send it as body and then skip it. Do note
1535 that there may even be additional "headers" after the body. */
1536 size_t chunk = pp->cache_size;
1538 if(chunk > (size_t)size)
1539 /* The conversion from curl_off_t to size_t is always fine here */
1540 chunk = (size_t)size;
1542 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1546 data->req.bytecount += chunk;
1548 infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1549 " bytes, %" CURL_FORMAT_CURL_OFF_TU
1550 " bytes are left for transfer\n", (curl_off_t)chunk,
1553 /* Have we used the entire cache or just part of it?*/
1554 if(pp->cache_size > chunk) {
1555 /* Only part of it so shrink the cache to fit the trailing data */
1556 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1557 pp->cache_size -= chunk;
1560 /* Free the cache */
1561 Curl_safefree(pp->cache);
1563 /* Reset the cache size */
1568 if(data->req.bytecount == size)
1569 /* The entire data is already transferred! */
1570 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1573 data->req.maxdownload = size;
1574 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1578 /* We don't know how to parse this line */
1579 failf(pp->conn->data, "Failed to parse FETCH response.");
1580 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1583 /* End of DO phase */
1584 state(conn, IMAP_STOP);
1589 /* For final FETCH responses performed after the download */
1590 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1594 CURLcode result = CURLE_OK;
1596 (void)instate; /* No use for this yet */
1599 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1601 /* End of DONE phase */
1602 state(conn, IMAP_STOP);
1607 /* For APPEND responses */
1608 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1611 CURLcode result = CURLE_OK;
1612 struct SessionHandle *data = conn->data;
1614 (void)instate; /* No use for this yet */
1616 if(imapcode != '+') {
1617 result = CURLE_UPLOAD_FAILED;
1620 /* Set the progress upload size */
1621 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1624 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1626 /* End of DO phase */
1627 state(conn, IMAP_STOP);
1633 /* For final APPEND responses performed after the upload */
1634 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1638 CURLcode result = CURLE_OK;
1640 (void)instate; /* No use for this yet */
1643 result = CURLE_UPLOAD_FAILED;
1645 /* End of DONE phase */
1646 state(conn, IMAP_STOP);
1651 /* For SEARCH responses */
1652 static CURLcode imap_state_search_resp(struct connectdata *conn, int imapcode,
1655 CURLcode result = CURLE_OK;
1656 char *line = conn->data->state.buffer;
1657 size_t len = strlen(line);
1659 (void)instate; /* No use for this yet */
1661 if(imapcode == '*') {
1662 /* Temporarily add the LF character back and send as body to the client */
1664 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1667 else if(imapcode != 'O')
1668 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1670 /* End of DO phase */
1671 state(conn, IMAP_STOP);
1676 static CURLcode imap_statemach_act(struct connectdata *conn)
1678 CURLcode result = CURLE_OK;
1679 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1681 struct imap_conn *imapc = &conn->proto.imapc;
1682 struct pingpong *pp = &imapc->pp;
1685 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1686 if(imapc->state == IMAP_UPGRADETLS)
1687 return imap_perform_upgrade_tls(conn);
1689 /* Flush any data that needs to be sent */
1691 return Curl_pp_flushsend(pp);
1694 /* Read the response from the server */
1695 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1699 /* Was there an error parsing the response line? */
1701 return CURLE_FTP_WEIRD_SERVER_REPLY;
1706 /* We have now received a full IMAP server response */
1707 switch(imapc->state) {
1708 case IMAP_SERVERGREET:
1709 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1712 case IMAP_CAPABILITY:
1713 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1717 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1720 case IMAP_AUTHENTICATE_PLAIN:
1721 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1724 case IMAP_AUTHENTICATE_LOGIN:
1725 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1728 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1729 result = imap_state_auth_login_password_resp(conn, imapcode,
1733 #ifndef CURL_DISABLE_CRYPTO_AUTH
1734 case IMAP_AUTHENTICATE_CRAMMD5:
1735 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1738 case IMAP_AUTHENTICATE_DIGESTMD5:
1739 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1742 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1743 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1748 case IMAP_AUTHENTICATE_NTLM:
1749 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1752 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1753 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1758 case IMAP_AUTHENTICATE_XOAUTH2:
1759 result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state);
1762 case IMAP_AUTHENTICATE_CANCEL:
1763 result = imap_state_auth_cancel_resp(conn, imapcode, imapc->state);
1766 case IMAP_AUTHENTICATE_FINAL:
1767 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1771 result = imap_state_login_resp(conn, imapcode, imapc->state);
1775 result = imap_state_list_resp(conn, imapcode, imapc->state);
1779 result = imap_state_select_resp(conn, imapcode, imapc->state);
1783 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1786 case IMAP_FETCH_FINAL:
1787 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1791 result = imap_state_append_resp(conn, imapcode, imapc->state);
1794 case IMAP_APPEND_FINAL:
1795 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1799 result = imap_state_search_resp(conn, imapcode, imapc->state);
1803 /* fallthrough, just stop! */
1805 /* internal error */
1806 state(conn, IMAP_STOP);
1809 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1814 /* Called repeatedly until done from multi.c */
1815 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1817 CURLcode result = CURLE_OK;
1818 struct imap_conn *imapc = &conn->proto.imapc;
1820 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1821 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1822 if(result || !imapc->ssldone)
1826 result = Curl_pp_statemach(&imapc->pp, FALSE);
1827 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1832 static CURLcode imap_block_statemach(struct connectdata *conn)
1834 CURLcode result = CURLE_OK;
1835 struct imap_conn *imapc = &conn->proto.imapc;
1837 while(imapc->state != IMAP_STOP && !result)
1838 result = Curl_pp_statemach(&imapc->pp, TRUE);
1843 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1845 static CURLcode imap_init(struct connectdata *conn)
1847 CURLcode result = CURLE_OK;
1848 struct SessionHandle *data = conn->data;
1851 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1853 result = CURLE_OUT_OF_MEMORY;
1858 /* For the IMAP "protocol connect" and "doing" phases only */
1859 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1862 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1865 /***********************************************************************
1869 * This function should do everything that is to be considered a part of the
1872 * The variable 'done' points to will be TRUE if the protocol-layer connect
1873 * phase is done when this function returns, or FALSE if not.
1875 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1877 CURLcode result = CURLE_OK;
1878 struct imap_conn *imapc = &conn->proto.imapc;
1879 struct pingpong *pp = &imapc->pp;
1881 *done = FALSE; /* default to not done yet */
1883 /* We always support persistent connections in IMAP */
1884 connkeep(conn, "IMAP default");
1886 /* Set the default response time-out */
1887 pp->response_time = RESP_TIMEOUT;
1888 pp->statemach_act = imap_statemach_act;
1889 pp->endofresp = imap_endofresp;
1892 /* Set the default preferred authentication type and mechanism */
1893 imapc->preftype = IMAP_TYPE_ANY;
1894 imapc->prefmech = SASL_AUTH_ANY;
1896 /* Initialise the pingpong layer */
1899 /* Parse the URL options */
1900 result = imap_parse_url_options(conn);
1904 /* Start off waiting for the server greeting response */
1905 state(conn, IMAP_SERVERGREET);
1907 /* Start off with an response id of '*' */
1908 strcpy(imapc->resptag, "*");
1910 result = imap_multi_statemach(conn, done);
1915 /***********************************************************************
1919 * The DONE function. This does what needs to be done after a single DO has
1922 * Input argument is already checked for validity.
1924 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1927 CURLcode result = CURLE_OK;
1928 struct SessionHandle *data = conn->data;
1929 struct IMAP *imap = data->req.protop;
1934 /* When the easy handle is removed from the multi interface while libcurl
1935 is still trying to resolve the host name, the IMAP struct is not yet
1936 initialized. However, the removal action calls Curl_done() which in
1937 turn calls this function, so we simply return success. */
1941 connclose(conn, "IMAP done with bad status"); /* marked for closure */
1942 result = status; /* use the already set error code */
1944 else if(!data->set.connect_only && !imap->custom &&
1945 (imap->uid || data->set.upload)) {
1946 /* Handle responses after FETCH or APPEND transfer has finished */
1947 if(!data->set.upload)
1948 state(conn, IMAP_FETCH_FINAL);
1950 /* End the APPEND command first by sending an empty line */
1951 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1953 state(conn, IMAP_APPEND_FINAL);
1956 /* Run the state-machine
1958 TODO: when the multi interface is used, this _really_ should be using
1959 the imap_multi_statemach function but we have no general support for
1960 non-blocking DONE operations, not in the multi state machine and with
1961 Curl_done() invokes on several places in the code!
1964 result = imap_block_statemach(conn);
1967 /* Cleanup our per-request based variables */
1968 Curl_safefree(imap->mailbox);
1969 Curl_safefree(imap->uidvalidity);
1970 Curl_safefree(imap->uid);
1971 Curl_safefree(imap->section);
1972 Curl_safefree(imap->partial);
1973 Curl_safefree(imap->query);
1974 Curl_safefree(imap->custom);
1975 Curl_safefree(imap->custom_params);
1977 /* Clear the transfer mode for the next request */
1978 imap->transfer = FTPTRANSFER_BODY;
1983 /***********************************************************************
1987 * This is the actual DO function for IMAP. Fetch or append a message, or do
1988 * other things according to the options previously setup.
1990 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1993 /* This is IMAP and no proxy */
1994 CURLcode result = CURLE_OK;
1995 struct SessionHandle *data = conn->data;
1996 struct IMAP *imap = data->req.protop;
1997 struct imap_conn *imapc = &conn->proto.imapc;
1998 bool selected = FALSE;
2000 DEBUGF(infof(conn->data, "DO phase starts\n"));
2002 if(conn->data->set.opt_no_body) {
2003 /* Requested no body means no transfer */
2004 imap->transfer = FTPTRANSFER_INFO;
2007 *dophase_done = FALSE; /* not done yet */
2009 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
2010 has already been selected on this connection */
2011 if(imap->mailbox && imapc->mailbox &&
2012 !strcmp(imap->mailbox, imapc->mailbox) &&
2013 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
2014 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
2017 /* Start the first command in the DO phase */
2018 if(conn->data->set.upload)
2019 /* APPEND can be executed directly */
2020 result = imap_perform_append(conn);
2021 else if(imap->custom && (selected || !imap->mailbox))
2022 /* Custom command using the same mailbox or no mailbox */
2023 result = imap_perform_list(conn);
2024 else if(!imap->custom && selected && imap->uid)
2025 /* FETCH from the same mailbox */
2026 result = imap_perform_fetch(conn);
2027 else if(!imap->custom && selected && imap->query)
2028 /* SEARCH the current mailbox */
2029 result = imap_perform_search(conn);
2030 else if(imap->mailbox && !selected &&
2031 (imap->custom || imap->uid || imap->query))
2032 /* SELECT the mailbox */
2033 result = imap_perform_select(conn);
2036 result = imap_perform_list(conn);
2041 /* Run the state-machine */
2042 result = imap_multi_statemach(conn, dophase_done);
2044 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
2047 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2052 /***********************************************************************
2056 * This function is registered as 'curl_do' function. It decodes the path
2057 * parts etc as a wrapper to the actual DO function (imap_perform).
2059 * The input argument is already checked for validity.
2061 static CURLcode imap_do(struct connectdata *conn, bool *done)
2063 CURLcode result = CURLE_OK;
2065 *done = FALSE; /* default to false */
2067 /* Parse the URL path */
2068 result = imap_parse_url_path(conn);
2072 /* Parse the custom request */
2073 result = imap_parse_custom_request(conn);
2077 result = imap_regular_transfer(conn, done);
2082 /***********************************************************************
2086 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
2087 * resources. BLOCKING.
2089 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2091 struct imap_conn *imapc = &conn->proto.imapc;
2093 /* We cannot send quit unconditionally. If this connection is stale or
2094 bad in any way, sending quit and waiting around here will make the
2095 disconnect wait in vain and cause more problems than we need to. */
2097 /* The IMAP session may or may not have been allocated/setup at this
2099 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
2100 if(!imap_perform_logout(conn))
2101 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
2103 /* Disconnect from the server */
2104 Curl_pp_disconnect(&imapc->pp);
2106 /* Cleanup the SASL module */
2107 Curl_sasl_cleanup(conn, imapc->authused);
2109 /* Cleanup our connection based variables */
2110 Curl_safefree(imapc->mailbox);
2111 Curl_safefree(imapc->mailbox_uidvalidity);
2116 /* Call this when the DO phase has completed */
2117 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2119 struct IMAP *imap = conn->data->req.protop;
2123 if(imap->transfer != FTPTRANSFER_BODY)
2124 /* no data to transfer */
2125 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2130 /* Called from multi.c while DOing */
2131 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2133 CURLcode result = imap_multi_statemach(conn, dophase_done);
2136 DEBUGF(infof(conn->data, "DO phase failed\n"));
2137 else if(*dophase_done) {
2138 result = imap_dophase_done(conn, FALSE /* not connected */);
2140 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2146 /***********************************************************************
2148 * imap_regular_transfer()
2150 * The input argument is already checked for validity.
2152 * Performs all commands done before a regular transfer between a local and a
2155 static CURLcode imap_regular_transfer(struct connectdata *conn,
2158 CURLcode result = CURLE_OK;
2159 bool connected = FALSE;
2160 struct SessionHandle *data = conn->data;
2162 /* Make sure size is unknown at this point */
2163 data->req.size = -1;
2165 /* Set the progress data */
2166 Curl_pgrsSetUploadCounter(data, 0);
2167 Curl_pgrsSetDownloadCounter(data, 0);
2168 Curl_pgrsSetUploadSize(data, 0);
2169 Curl_pgrsSetDownloadSize(data, 0);
2171 /* Carry out the perform */
2172 result = imap_perform(conn, &connected, dophase_done);
2174 /* Perform post DO phase operations if necessary */
2175 if(!result && *dophase_done)
2176 result = imap_dophase_done(conn, connected);
2181 static CURLcode imap_setup_connection(struct connectdata *conn)
2183 struct SessionHandle *data = conn->data;
2185 /* Initialise the IMAP layer */
2186 CURLcode result = imap_init(conn);
2190 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2191 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2192 switch and use HTTP operations only */
2193 #ifndef CURL_DISABLE_HTTP
2194 if(conn->handler == &Curl_handler_imap)
2195 conn->handler = &Curl_handler_imap_proxy;
2198 conn->handler = &Curl_handler_imaps_proxy;
2200 failf(data, "IMAPS not supported!");
2201 return CURLE_UNSUPPORTED_PROTOCOL;
2205 /* set it up as an HTTP connection instead */
2206 return conn->handler->setup_connection(conn);
2208 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2209 return CURLE_UNSUPPORTED_PROTOCOL;
2213 data->state.path++; /* don't include the initial slash */
2218 /***********************************************************************
2222 * Sends the formated string as an IMAP command to the server.
2224 * Designed to never block.
2226 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
2228 CURLcode result = CURLE_OK;
2229 struct imap_conn *imapc = &conn->proto.imapc;
2235 /* Calculate the next command ID wrapping at 3 digits */
2236 imapc->cmdid = (imapc->cmdid + 1) % 1000;
2238 /* Calculate the tag based on the connection ID and command ID */
2239 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
2240 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
2242 /* Prefix the format with the tag */
2243 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
2245 return CURLE_OUT_OF_MEMORY;
2247 /* Send the data with the tag */
2249 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
2252 Curl_safefree(taggedfmt);
2257 /***********************************************************************
2261 * Checks the input string for characters that need escaping and returns an
2262 * atom ready for sending to the server.
2264 * The returned string needs to be freed.
2267 static char *imap_atom(const char *str)
2271 size_t backsp_count = 0;
2272 size_t quote_count = 0;
2273 bool space_exists = FALSE;
2275 char *newstr = NULL;
2280 /* Count any unescapped characters */
2288 space_exists = TRUE;
2293 /* Does the input contain any unescapped characters? */
2294 if(!backsp_count && !quote_count && !space_exists)
2297 /* Calculate the new string length */
2298 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
2300 /* Allocate the new string */
2301 newstr = (char *) malloc((newlen + 1) * sizeof(char));
2305 /* Surround the string in quotes if necessary */
2309 newstr[newlen - 1] = '"';
2313 /* Copy the string, escaping backslash and quote characters along the way */
2316 if(*p1 == '\\' || *p1 == '"') {
2327 /* Terminate the string */
2328 newstr[newlen] = '\0';
2333 /***********************************************************************
2337 * Portable test of whether the specified char is a "bchar" as defined in the
2338 * grammar of RFC-5092.
2340 static bool imap_is_bchar(char ch)
2344 case ':': case '@': case '/':
2345 /* bchar -> achar */
2347 /* bchar -> achar -> uchar -> unreserved */
2348 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2349 case '7': case '8': case '9':
2350 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2351 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2352 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2353 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2354 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2355 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2356 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2357 case 'v': case 'w': case 'x': case 'y': case 'z':
2358 case '-': case '.': case '_': case '~':
2359 /* bchar -> achar -> uchar -> sub-delims-sh */
2360 case '!': case '$': case '\'': case '(': case ')': case '*':
2362 /* bchar -> achar -> uchar -> pct-encoded */
2363 case '%': /* HEXDIG chars are already included above */
2371 /***********************************************************************
2373 * imap_parse_url_options()
2375 * Parse the URL login options.
2377 static CURLcode imap_parse_url_options(struct connectdata *conn)
2379 CURLcode result = CURLE_OK;
2380 struct imap_conn *imapc = &conn->proto.imapc;
2381 const char *options = conn->options;
2382 const char *ptr = options;
2385 while(ptr && *ptr) {
2386 const char *key = ptr;
2388 while(*ptr && *ptr != '=')
2391 if(strnequal(key, "AUTH", 4)) {
2393 const char *value = ++ptr;
2397 imapc->preftype = IMAP_TYPE_NONE;
2398 imapc->prefmech = SASL_AUTH_NONE;
2401 while(*ptr && *ptr != ';') {
2406 if(strnequal(value, "*", len)) {
2407 imapc->preftype = IMAP_TYPE_ANY;
2408 imapc->prefmech = SASL_AUTH_ANY;
2410 else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
2411 imapc->preftype = IMAP_TYPE_SASL;
2412 imapc->prefmech |= SASL_MECH_LOGIN;
2414 else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
2415 imapc->preftype = IMAP_TYPE_SASL;
2416 imapc->prefmech |= SASL_MECH_PLAIN;
2418 else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
2419 imapc->preftype = IMAP_TYPE_SASL;
2420 imapc->prefmech |= SASL_MECH_CRAM_MD5;
2422 else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
2423 imapc->preftype = IMAP_TYPE_SASL;
2424 imapc->prefmech |= SASL_MECH_DIGEST_MD5;
2426 else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
2427 imapc->preftype = IMAP_TYPE_SASL;
2428 imapc->prefmech |= SASL_MECH_GSSAPI;
2430 else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
2431 imapc->preftype = IMAP_TYPE_SASL;
2432 imapc->prefmech |= SASL_MECH_NTLM;
2434 else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
2435 imapc->preftype = IMAP_TYPE_SASL;
2436 imapc->prefmech |= SASL_MECH_XOAUTH2;
2443 result = CURLE_URL_MALFORMAT;
2449 /***********************************************************************
2451 * imap_parse_url_path()
2453 * Parse the URL path into separate path components.
2456 static CURLcode imap_parse_url_path(struct connectdata *conn)
2458 /* The imap struct is already initialised in imap_connect() */
2459 CURLcode result = CURLE_OK;
2460 struct SessionHandle *data = conn->data;
2461 struct IMAP *imap = data->req.protop;
2462 const char *begin = data->state.path;
2463 const char *ptr = begin;
2465 /* See how much of the URL is a valid path and decode it */
2466 while(imap_is_bchar(*ptr))
2470 /* Remove the trailing slash if present */
2471 const char *end = ptr;
2472 if(end > begin && end[-1] == '/')
2475 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2481 imap->mailbox = NULL;
2483 /* There can be any number of parameters in the form ";NAME=VALUE" */
2484 while(*ptr == ';') {
2489 /* Find the length of the name parameter */
2491 while(*ptr && *ptr != '=')
2495 return CURLE_URL_MALFORMAT;
2497 /* Decode the name parameter */
2498 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2502 /* Find the length of the value parameter */
2504 while(imap_is_bchar(*ptr))
2507 /* Decode the value parameter */
2508 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2510 Curl_safefree(name);
2514 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2516 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2517 PARTIAL) stripping of the trailing slash character if it is present.
2519 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2520 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2521 if(valuelen > 0 && value[valuelen - 1] == '/')
2522 value[valuelen - 1] = '\0';
2524 imap->uidvalidity = value;
2527 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2528 if(valuelen > 0 && value[valuelen - 1] == '/')
2529 value[valuelen - 1] = '\0';
2534 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2535 if(valuelen > 0 && value[valuelen - 1] == '/')
2536 value[valuelen - 1] = '\0';
2538 imap->section = value;
2541 else if(Curl_raw_equal(name, "PARTIAL") && !imap->partial) {
2542 if(valuelen > 0 && value[valuelen - 1] == '/')
2543 value[valuelen - 1] = '\0';
2545 imap->partial = value;
2549 Curl_safefree(name);
2550 Curl_safefree(value);
2552 return CURLE_URL_MALFORMAT;
2555 Curl_safefree(name);
2556 Curl_safefree(value);
2559 /* Does the URL contain a query parameter? Only valid when we have a mailbox
2560 and no UID as per RFC-5092 */
2561 if(imap->mailbox && !imap->uid && *ptr == '?') {
2562 /* Find the length of the query parameter */
2564 while(imap_is_bchar(*ptr))
2567 /* Decode the query parameter */
2568 result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2574 /* Any extra stuff at the end of the URL is an error */
2576 return CURLE_URL_MALFORMAT;
2581 /***********************************************************************
2583 * imap_parse_custom_request()
2585 * Parse the custom request.
2587 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2589 CURLcode result = CURLE_OK;
2590 struct SessionHandle *data = conn->data;
2591 struct IMAP *imap = data->req.protop;
2592 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2595 /* URL decode the custom request */
2596 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2598 /* Extract the parameters if specified */
2600 const char *params = imap->custom;
2602 while(*params && *params != ' ')
2606 imap->custom_params = strdup(params);
2607 imap->custom[params - imap->custom] = '\0';
2609 if(!imap->custom_params)
2610 result = CURLE_OUT_OF_MEMORY;
2618 /***********************************************************************
2620 * imap_calc_sasl_details()
2622 * Calculate the required login details for SASL authentication.
2624 static CURLcode imap_calc_sasl_details(struct connectdata *conn,
2626 char **initresp, size_t *len,
2627 imapstate *state1, imapstate *state2)
2629 CURLcode result = CURLE_OK;
2630 struct SessionHandle *data = conn->data;
2631 struct imap_conn *imapc = &conn->proto.imapc;
2633 /* Calculate the supported authentication mechanism, by decreasing order of
2634 security, as well as the initial response where appropriate */
2635 #ifndef CURL_DISABLE_CRYPTO_AUTH
2636 if((imapc->authmechs & SASL_MECH_DIGEST_MD5) &&
2637 (imapc->prefmech & SASL_MECH_DIGEST_MD5)) {
2638 *mech = SASL_MECH_STRING_DIGEST_MD5;
2639 *state1 = IMAP_AUTHENTICATE_DIGESTMD5;
2640 imapc->authused = SASL_MECH_DIGEST_MD5;
2642 else if((imapc->authmechs & SASL_MECH_CRAM_MD5) &&
2643 (imapc->prefmech & SASL_MECH_CRAM_MD5)) {
2644 *mech = SASL_MECH_STRING_CRAM_MD5;
2645 *state1 = IMAP_AUTHENTICATE_CRAMMD5;
2646 imapc->authused = SASL_MECH_CRAM_MD5;
2651 if((imapc->authmechs & SASL_MECH_NTLM) &&
2652 (imapc->prefmech & SASL_MECH_NTLM)) {
2653 *mech = SASL_MECH_STRING_NTLM;
2654 *state1 = IMAP_AUTHENTICATE_NTLM;
2655 *state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
2656 imapc->authused = SASL_MECH_NTLM;
2658 if(imapc->ir_supported || data->set.sasl_ir)
2659 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2665 if(((imapc->authmechs & SASL_MECH_XOAUTH2) &&
2666 (imapc->prefmech & SASL_MECH_XOAUTH2) &&
2667 (imapc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2668 *mech = SASL_MECH_STRING_XOAUTH2;
2669 *state1 = IMAP_AUTHENTICATE_XOAUTH2;
2670 *state2 = IMAP_AUTHENTICATE_FINAL;
2671 imapc->authused = SASL_MECH_XOAUTH2;
2673 if(imapc->ir_supported || data->set.sasl_ir)
2674 result = Curl_sasl_create_xoauth2_message(data, conn->user,
2675 conn->xoauth2_bearer,
2678 else if((imapc->authmechs & SASL_MECH_LOGIN) &&
2679 (imapc->prefmech & SASL_MECH_LOGIN)) {
2680 *mech = SASL_MECH_STRING_LOGIN;
2681 *state1 = IMAP_AUTHENTICATE_LOGIN;
2682 *state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
2683 imapc->authused = SASL_MECH_LOGIN;
2685 if(imapc->ir_supported || data->set.sasl_ir)
2686 result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2688 else if((imapc->authmechs & SASL_MECH_PLAIN) &&
2689 (imapc->prefmech & SASL_MECH_PLAIN)) {
2690 *mech = SASL_MECH_STRING_PLAIN;
2691 *state1 = IMAP_AUTHENTICATE_PLAIN;
2692 *state2 = IMAP_AUTHENTICATE_FINAL;
2693 imapc->authused = SASL_MECH_PLAIN;
2695 if(imapc->ir_supported || data->set.sasl_ir)
2696 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2703 #endif /* CURL_DISABLE_IMAP */