1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, 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 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28 * RFC4959 IMAP Extension for SASL Initial Client Response
29 * RFC5092 IMAP URL Scheme
30 * RFC6749 OAuth 2.0 Authorization Framework
31 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
33 ***************************************************************************/
35 #include "curl_setup.h"
37 #ifndef CURL_DISABLE_IMAP
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
46 #include <sys/utsname.h>
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
58 #define in_addr_t unsigned long
61 #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_GSSAPI",
437 "AUTHENTICATE_GSSAPI_TOKEN",
438 "AUTHENTICATE_GSSAPI_NO_DATA",
439 "AUTHENTICATE_XOAUTH2",
440 "AUTHENTICATE_CANCEL",
441 "AUTHENTICATE_FINAL",
454 if(imapc->state != newstate)
455 infof(conn->data, "IMAP %p state change from %s to %s\n",
456 (void *)imapc, names[imapc->state], names[newstate]);
459 imapc->state = newstate;
462 /***********************************************************************
464 * imap_perform_capability()
466 * Sends the CAPABILITY command in order to obtain a list of server side
467 * supported capabilities.
469 static CURLcode imap_perform_capability(struct connectdata *conn)
471 CURLcode result = CURLE_OK;
472 struct imap_conn *imapc = &conn->proto.imapc;
474 imapc->authmechs = 0; /* No known authentication mechanisms yet */
475 imapc->authused = 0; /* Clear the authentication mechanism used */
476 imapc->tls_supported = FALSE; /* Clear the TLS capability */
478 /* Send the CAPABILITY command */
479 result = imap_sendf(conn, "CAPABILITY");
482 state(conn, IMAP_CAPABILITY);
487 /***********************************************************************
489 * imap_perform_starttls()
491 * Sends the STARTTLS command to start the upgrade to TLS.
493 static CURLcode imap_perform_starttls(struct connectdata *conn)
495 CURLcode result = CURLE_OK;
497 /* Send the STARTTLS command */
498 result = imap_sendf(conn, "STARTTLS");
501 state(conn, IMAP_STARTTLS);
506 /***********************************************************************
508 * imap_perform_upgrade_tls()
510 * Performs the upgrade to TLS.
512 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
514 CURLcode result = CURLE_OK;
515 struct imap_conn *imapc = &conn->proto.imapc;
517 /* Start the SSL connection */
518 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
521 if(imapc->state != IMAP_UPGRADETLS)
522 state(conn, IMAP_UPGRADETLS);
526 result = imap_perform_capability(conn);
533 /***********************************************************************
535 * imap_perform_login()
537 * Sends a clear text LOGIN command to authenticate with.
539 static CURLcode imap_perform_login(struct connectdata *conn)
541 CURLcode result = CURLE_OK;
545 /* Check we have a username and password to authenticate with and end the
546 connect phase if we don't */
547 if(!conn->bits.user_passwd) {
548 state(conn, IMAP_STOP);
553 /* Make sure the username and password are in the correct atom format */
554 user = imap_atom(conn->user);
555 passwd = imap_atom(conn->passwd);
557 /* Send the LOGIN command */
558 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
559 passwd ? passwd : "");
562 Curl_safefree(passwd);
565 state(conn, IMAP_LOGIN);
570 /***********************************************************************
572 * imap_perform_authenticate()
574 * Sends an AUTHENTICATE command allowing the client to login with the given
575 * SASL authentication mechanism.
577 static CURLcode imap_perform_authenticate(struct connectdata *conn,
579 const char *initresp,
580 imapstate state1, imapstate state2)
582 CURLcode result = CURLE_OK;
585 /* Send the AUTHENTICATE command with the initial response */
586 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
592 /* Send the AUTHENTICATE command */
593 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
602 /***********************************************************************
604 * imap_perform_authentication()
606 * Initiates the authentication sequence, with the appropriate SASL
607 * authentication mechanism, falling back to clear text should a common
608 * mechanism not be available between the client and server.
610 static CURLcode imap_perform_authentication(struct connectdata *conn)
612 CURLcode result = CURLE_OK;
613 struct imap_conn *imapc = &conn->proto.imapc;
614 const char *mech = NULL;
615 char *initresp = NULL;
617 imapstate state1 = IMAP_STOP;
618 imapstate state2 = IMAP_STOP;
620 /* Check we have a username and password to authenticate with and end the
621 connect phase if we don't */
622 if(!conn->bits.user_passwd) {
623 state(conn, IMAP_STOP);
628 /* Calculate the SASL login details */
629 result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
633 if(mech && (imapc->preftype & IMAP_TYPE_SASL)) {
634 /* Perform SASL based authentication */
635 result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
637 else if((!imapc->login_disabled) &&
638 (imapc->preftype & IMAP_TYPE_CLEARTEXT))
639 /* Perform clear text authentication */
640 result = imap_perform_login(conn);
642 /* Other mechanisms not supported */
643 infof(conn->data, "No known authentication mechanisms supported!\n");
644 result = CURLE_LOGIN_DENIED;
648 Curl_safefree(initresp);
653 /***********************************************************************
655 * imap_perform_list()
657 * Sends a LIST command or an alternative custom request.
659 static CURLcode imap_perform_list(struct connectdata *conn)
661 CURLcode result = CURLE_OK;
662 struct SessionHandle *data = conn->data;
663 struct IMAP *imap = data->req.protop;
667 /* Send the custom request */
668 result = imap_sendf(conn, "%s%s", imap->custom,
669 imap->custom_params ? imap->custom_params : "");
671 /* Make sure the mailbox is in the correct atom format */
672 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
674 return CURLE_OUT_OF_MEMORY;
676 /* Send the LIST command */
677 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
679 Curl_safefree(mailbox);
683 state(conn, IMAP_LIST);
688 /***********************************************************************
690 * imap_perform_select()
692 * Sends a SELECT command to ask the server to change the selected mailbox.
694 static CURLcode imap_perform_select(struct connectdata *conn)
696 CURLcode result = CURLE_OK;
697 struct SessionHandle *data = conn->data;
698 struct IMAP *imap = data->req.protop;
699 struct imap_conn *imapc = &conn->proto.imapc;
702 /* Invalidate old information as we are switching mailboxes */
703 Curl_safefree(imapc->mailbox);
704 Curl_safefree(imapc->mailbox_uidvalidity);
706 /* Check we have a mailbox */
708 failf(conn->data, "Cannot SELECT without a mailbox.");
709 return CURLE_URL_MALFORMAT;
712 /* Make sure the mailbox is in the correct atom format */
713 mailbox = imap_atom(imap->mailbox);
715 return CURLE_OUT_OF_MEMORY;
717 /* Send the SELECT command */
718 result = imap_sendf(conn, "SELECT %s", mailbox);
720 Curl_safefree(mailbox);
723 state(conn, IMAP_SELECT);
728 /***********************************************************************
730 * imap_perform_fetch()
732 * Sends a FETCH command to initiate the download of a message.
734 static CURLcode imap_perform_fetch(struct connectdata *conn)
736 CURLcode result = CURLE_OK;
737 struct IMAP *imap = conn->data->req.protop;
739 /* Check we have a UID */
741 failf(conn->data, "Cannot FETCH without a UID.");
742 return CURLE_URL_MALFORMAT;
745 /* Send the FETCH command */
747 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
749 imap->section ? imap->section : "",
752 result = imap_sendf(conn, "FETCH %s BODY[%s]",
754 imap->section ? imap->section : "");
757 state(conn, IMAP_FETCH);
762 /***********************************************************************
764 * imap_perform_append()
766 * Sends an APPEND command to initiate the upload of a message.
768 static CURLcode imap_perform_append(struct connectdata *conn)
770 CURLcode result = CURLE_OK;
771 struct IMAP *imap = conn->data->req.protop;
774 /* Check we have a mailbox */
776 failf(conn->data, "Cannot APPEND without a mailbox.");
777 return CURLE_URL_MALFORMAT;
780 /* Check we know the size of the upload */
781 if(conn->data->state.infilesize < 0) {
782 failf(conn->data, "Cannot APPEND with unknown input file size\n");
783 return CURLE_UPLOAD_FAILED;
786 /* Make sure the mailbox is in the correct atom format */
787 mailbox = imap_atom(imap->mailbox);
789 return CURLE_OUT_OF_MEMORY;
791 /* Send the APPEND command */
792 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
793 mailbox, conn->data->state.infilesize);
795 Curl_safefree(mailbox);
798 state(conn, IMAP_APPEND);
803 /***********************************************************************
805 * imap_perform_search()
807 * Sends a SEARCH command.
809 static CURLcode imap_perform_search(struct connectdata *conn)
811 CURLcode result = CURLE_OK;
812 struct IMAP *imap = conn->data->req.protop;
814 /* Check we have a query string */
816 failf(conn->data, "Cannot SEARCH without a query string.");
817 return CURLE_URL_MALFORMAT;
820 /* Send the SEARCH command */
821 result = imap_sendf(conn, "SEARCH %s", imap->query);
824 state(conn, IMAP_SEARCH);
829 /***********************************************************************
831 * imap_perform_logout()
833 * Performs the logout action prior to sclose() being called.
835 static CURLcode imap_perform_logout(struct connectdata *conn)
837 CURLcode result = CURLE_OK;
839 /* Send the LOGOUT command */
840 result = imap_sendf(conn, "LOGOUT");
843 state(conn, IMAP_LOGOUT);
848 /* For the initial server greeting */
849 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
853 CURLcode result = CURLE_OK;
854 struct SessionHandle *data = conn->data;
856 (void)instate; /* no use for this yet */
858 if(imapcode != 'O') {
859 failf(data, "Got unexpected imap-server response");
860 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
863 result = imap_perform_capability(conn);
868 /* For CAPABILITY responses */
869 static CURLcode imap_state_capability_resp(struct connectdata *conn,
873 CURLcode result = CURLE_OK;
874 struct SessionHandle *data = conn->data;
875 struct imap_conn *imapc = &conn->proto.imapc;
876 const char *line = data->state.buffer;
879 (void)instate; /* no use for this yet */
881 /* Do we have a untagged response? */
882 if(imapcode == '*') {
885 /* Loop through the data line */
888 (*line == ' ' || *line == '\t' ||
889 *line == '\r' || *line == '\n')) {
897 /* Extract the word */
898 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
899 line[wordlen] != '\t' && line[wordlen] != '\r' &&
900 line[wordlen] != '\n';)
903 /* Does the server support the STARTTLS capability? */
904 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
905 imapc->tls_supported = TRUE;
907 /* Has the server explicitly disabled clear text authentication? */
908 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
909 imapc->login_disabled = TRUE;
911 /* Does the server support the SASL-IR capability? */
912 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
913 imapc->ir_supported = TRUE;
915 /* Do we have a SASL based authentication mechanism? */
916 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
920 /* Test the word for a matching authentication mechanism */
921 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
922 imapc->authmechs |= SASL_MECH_LOGIN;
923 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
924 imapc->authmechs |= SASL_MECH_PLAIN;
925 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
926 imapc->authmechs |= SASL_MECH_CRAM_MD5;
927 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
928 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
929 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
930 imapc->authmechs |= SASL_MECH_GSSAPI;
931 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
932 imapc->authmechs |= SASL_MECH_EXTERNAL;
933 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
934 imapc->authmechs |= SASL_MECH_NTLM;
935 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
936 imapc->authmechs |= SASL_MECH_XOAUTH2;
942 else if(imapcode == 'O') {
943 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
944 /* We don't have a SSL/TLS connection yet, but SSL is requested */
945 if(imapc->tls_supported)
946 /* Switch to TLS connection now */
947 result = imap_perform_starttls(conn);
948 else if(data->set.use_ssl == CURLUSESSL_TRY)
949 /* Fallback and carry on with authentication */
950 result = imap_perform_authentication(conn);
952 failf(data, "STARTTLS not supported.");
953 result = CURLE_USE_SSL_FAILED;
957 result = imap_perform_authentication(conn);
960 result = imap_perform_authentication(conn);
965 /* For STARTTLS responses */
966 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
970 CURLcode result = CURLE_OK;
971 struct SessionHandle *data = conn->data;
973 (void)instate; /* no use for this yet */
975 if(imapcode != 'O') {
976 if(data->set.use_ssl != CURLUSESSL_TRY) {
977 failf(data, "STARTTLS denied. %c", imapcode);
978 result = CURLE_USE_SSL_FAILED;
981 result = imap_perform_authentication(conn);
984 result = imap_perform_upgrade_tls(conn);
989 /* For AUTHENTICATE PLAIN (without initial response) responses */
990 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
994 CURLcode result = CURLE_OK;
995 struct SessionHandle *data = conn->data;
997 char *plainauth = NULL;
999 (void)instate; /* no use for this yet */
1001 if(imapcode != '+') {
1002 failf(data, "Access denied. %c", imapcode);
1003 result = CURLE_LOGIN_DENIED;
1006 /* Create the authorisation message */
1007 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
1009 if(!result && plainauth) {
1010 /* Send the message */
1011 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
1014 state(conn, IMAP_AUTHENTICATE_FINAL);
1018 Curl_safefree(plainauth);
1023 /* For AUTHENTICATE LOGIN (without initial response) responses */
1024 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1028 CURLcode result = CURLE_OK;
1029 struct SessionHandle *data = conn->data;
1031 char *authuser = NULL;
1033 (void)instate; /* no use for this yet */
1035 if(imapcode != '+') {
1036 failf(data, "Access denied: %d", imapcode);
1037 result = CURLE_LOGIN_DENIED;
1040 /* Create the user message */
1041 result = Curl_sasl_create_login_message(data, conn->user,
1043 if(!result && authuser) {
1045 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1048 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1052 Curl_safefree(authuser);
1057 /* For AUTHENTICATE LOGIN user entry responses */
1058 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1062 CURLcode result = CURLE_OK;
1063 struct SessionHandle *data = conn->data;
1065 char *authpasswd = NULL;
1067 (void)instate; /* no use for this yet */
1069 if(imapcode != '+') {
1070 failf(data, "Access denied: %d", imapcode);
1071 result = CURLE_LOGIN_DENIED;
1074 /* Create the password message */
1075 result = Curl_sasl_create_login_message(data, conn->passwd,
1077 if(!result && authpasswd) {
1078 /* Send the password */
1079 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1082 state(conn, IMAP_AUTHENTICATE_FINAL);
1086 Curl_safefree(authpasswd);
1091 #ifndef CURL_DISABLE_CRYPTO_AUTH
1092 /* For AUTHENTICATE CRAM-MD5 responses */
1093 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1097 CURLcode result = CURLE_OK;
1098 struct SessionHandle *data = conn->data;
1100 char *chlg64 = NULL;
1101 char *rplyb64 = NULL;
1104 (void)instate; /* no use for this yet */
1106 if(imapcode != '+') {
1107 failf(data, "Access denied: %d", imapcode);
1108 return CURLE_LOGIN_DENIED;
1111 /* Get the challenge message */
1112 imap_get_message(data->state.buffer, &chlg64);
1114 /* Decode the challenge message */
1115 result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
1117 /* Send the cancellation */
1118 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1121 state(conn, IMAP_AUTHENTICATE_CANCEL);
1124 /* Create the response message */
1125 result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
1126 conn->passwd, &rplyb64, &len);
1127 if(!result && rplyb64) {
1128 /* Send the response */
1129 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1132 state(conn, IMAP_AUTHENTICATE_FINAL);
1136 Curl_safefree(chlg);
1137 Curl_safefree(rplyb64);
1142 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1143 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1147 CURLcode result = CURLE_OK;
1148 struct SessionHandle *data = conn->data;
1149 char *chlg64 = NULL;
1150 char *rplyb64 = NULL;
1153 (void)instate; /* no use for this yet */
1155 if(imapcode != '+') {
1156 failf(data, "Access denied: %d", imapcode);
1157 return CURLE_LOGIN_DENIED;
1160 /* Get the challenge message */
1161 imap_get_message(data->state.buffer, &chlg64);
1163 /* Create the response message */
1164 result = Curl_sasl_create_digest_md5_message(data, chlg64,
1165 conn->user, conn->passwd,
1166 "imap", &rplyb64, &len);
1168 if(result == CURLE_BAD_CONTENT_ENCODING) {
1169 /* Send the cancellation */
1170 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1173 state(conn, IMAP_AUTHENTICATE_CANCEL);
1177 /* Send the response */
1178 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1181 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1184 Curl_safefree(rplyb64);
1189 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1190 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1194 CURLcode result = CURLE_OK;
1195 struct SessionHandle *data = conn->data;
1197 (void)instate; /* no use for this yet */
1199 if(imapcode != '+') {
1200 failf(data, "Authentication failed: %d", imapcode);
1201 result = CURLE_LOGIN_DENIED;
1204 /* Send an empty response */
1205 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1208 state(conn, IMAP_AUTHENTICATE_FINAL);
1216 /* For AUTHENTICATE NTLM (without initial response) responses */
1217 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1221 CURLcode result = CURLE_OK;
1222 struct SessionHandle *data = conn->data;
1224 char *type1msg = NULL;
1226 (void)instate; /* no use for this yet */
1228 if(imapcode != '+') {
1229 failf(data, "Access denied: %d", imapcode);
1230 result = CURLE_LOGIN_DENIED;
1233 /* Create the type-1 message */
1234 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1237 if(!result && type1msg) {
1238 /* Send the message */
1239 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1242 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1246 Curl_safefree(type1msg);
1251 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1252 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1256 CURLcode result = CURLE_OK;
1257 struct SessionHandle *data = conn->data;
1258 char *type2msg = NULL;
1259 char *type3msg = NULL;
1262 (void)instate; /* no use for this yet */
1264 if(imapcode != '+') {
1265 failf(data, "Access denied: %d", imapcode);
1266 result = CURLE_LOGIN_DENIED;
1269 /* Get the challenge message */
1270 imap_get_message(data->state.buffer, &type2msg);
1272 /* Decode the type-2 message */
1273 result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1275 /* Send the cancellation */
1276 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1279 state(conn, IMAP_AUTHENTICATE_CANCEL);
1282 /* Create the type-3 message */
1283 result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1284 conn->passwd, &conn->ntlm,
1286 if(!result && type3msg) {
1287 /* Send the message */
1288 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1291 state(conn, IMAP_AUTHENTICATE_FINAL);
1296 Curl_safefree(type3msg);
1302 #if defined(USE_KERBEROS5)
1303 /* For AUTHENTICATE GSSAPI (without initial response) responses */
1304 static CURLcode imap_state_auth_gssapi_resp(struct connectdata *conn,
1308 CURLcode result = CURLE_OK;
1309 struct SessionHandle *data = conn->data;
1310 struct imap_conn *imapc = &conn->proto.imapc;
1312 char *respmsg = NULL;
1314 (void)instate; /* no use for this yet */
1316 if(imapcode != '+') {
1317 failf(data, "Access denied: %d", imapcode);
1318 result = CURLE_LOGIN_DENIED;
1321 /* Create the initial response message */
1322 result = Curl_sasl_create_gssapi_user_message(data, conn->user,
1323 conn->passwd, "imap",
1327 if(!result && respmsg) {
1328 /* Send the message */
1329 result = Curl_pp_sendf(&imapc->pp, "%s", respmsg);
1332 state(conn, IMAP_AUTHENTICATE_GSSAPI_TOKEN);
1336 Curl_safefree(respmsg);
1341 /* For AUTHENTICATE GSSAPI user token responses */
1342 static CURLcode imap_state_auth_gssapi_token_resp(struct connectdata *conn,
1346 CURLcode result = CURLE_OK;
1347 struct SessionHandle *data = conn->data;
1348 struct imap_conn *imapc = &conn->proto.imapc;
1349 char *chlgmsg = NULL;
1350 char *respmsg = NULL;
1353 (void)instate; /* no use for this yet */
1355 if(imapcode != '+') {
1356 failf(data, "Access denied: %d", imapcode);
1357 result = CURLE_LOGIN_DENIED;
1360 /* Get the challenge message */
1361 imap_get_message(data->state.buffer, &chlgmsg);
1363 if(imapc->mutual_auth)
1364 /* Decode the user token challenge and create the optional response
1366 result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
1368 chlgmsg, &conn->krb5,
1371 /* Decode the security challenge and create the response message */
1372 result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
1377 if(result == CURLE_BAD_CONTENT_ENCODING) {
1378 /* Send the cancellation */
1379 result = Curl_pp_sendf(&imapc->pp, "%s", "*");
1382 state(conn, IMAP_AUTHENTICATE_CANCEL);
1386 /* Send the response */
1388 result = Curl_pp_sendf(&imapc->pp, "%s", respmsg);
1390 result = Curl_pp_sendf(&imapc->pp, "%s", "");
1393 state(conn, (imapc->mutual_auth ? IMAP_AUTHENTICATE_GSSAPI_NO_DATA :
1394 IMAP_AUTHENTICATE_FINAL));
1398 Curl_safefree(respmsg);
1403 /* For AUTHENTICATE GSSAPI no data responses */
1404 static CURLcode imap_state_auth_gssapi_no_data_resp(struct connectdata *conn,
1408 CURLcode result = CURLE_OK;
1409 struct SessionHandle *data = conn->data;
1410 char *chlgmsg = NULL;
1411 char *respmsg = NULL;
1414 (void)instate; /* no use for this yet */
1416 if(imapcode != '+') {
1417 failf(data, "Access denied: %d", imapcode);
1418 result = CURLE_LOGIN_DENIED;
1421 /* Get the challenge message */
1422 imap_get_message(data->state.buffer, &chlgmsg);
1424 /* Decode the security challenge and create the response message */
1425 result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
1429 if(result == CURLE_BAD_CONTENT_ENCODING) {
1430 /* Send the cancellation */
1431 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1434 state(conn, IMAP_AUTHENTICATE_CANCEL);
1438 /* Send the response */
1440 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", respmsg);
1443 state(conn, IMAP_AUTHENTICATE_FINAL);
1448 Curl_safefree(respmsg);
1454 /* For AUTHENTICATE XOAUTH2 (without initial response) responses */
1455 static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn,
1459 CURLcode result = CURLE_OK;
1460 struct SessionHandle *data = conn->data;
1462 char *xoauth = NULL;
1464 (void)instate; /* no use for this yet */
1466 if(imapcode != '+') {
1467 failf(data, "Access denied: %d", imapcode);
1468 result = CURLE_LOGIN_DENIED;
1471 /* Create the authorisation message */
1472 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1473 conn->xoauth2_bearer,
1475 if(!result && xoauth) {
1476 /* Send the message */
1477 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth);
1480 state(conn, IMAP_AUTHENTICATE_FINAL);
1484 Curl_safefree(xoauth);
1489 /* For AUTHENTICATE cancellation responses */
1490 static CURLcode imap_state_auth_cancel_resp(struct connectdata *conn,
1494 CURLcode result = CURLE_OK;
1495 struct SessionHandle *data = conn->data;
1496 struct imap_conn *imapc = &conn->proto.imapc;
1497 const char *mech = NULL;
1498 char *initresp = NULL;
1500 imapstate state1 = IMAP_STOP;
1501 imapstate state2 = IMAP_STOP;
1504 (void)instate; /* no use for this yet */
1506 /* Remove the offending mechanism from the supported list */
1507 imapc->authmechs ^= imapc->authused;
1509 /* Calculate alternative SASL login details */
1510 result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1514 /* Do we have any mechanisms left or can we fallback to clear text? */
1516 /* Retry SASL based authentication */
1517 result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
1519 Curl_safefree(initresp);
1521 else if((!imapc->login_disabled) &&
1522 (imapc->preftype & IMAP_TYPE_CLEARTEXT))
1523 /* Perform clear text authentication */
1524 result = imap_perform_login(conn);
1526 failf(data, "Authentication cancelled");
1528 result = CURLE_LOGIN_DENIED;
1535 /* For final responses in the AUTHENTICATE sequence */
1536 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1540 CURLcode result = CURLE_OK;
1541 struct SessionHandle *data = conn->data;
1543 (void)instate; /* no use for this yet */
1545 if(imapcode != 'O') {
1546 failf(data, "Authentication failed: %d", imapcode);
1547 result = CURLE_LOGIN_DENIED;
1550 /* End of connect phase */
1551 state(conn, IMAP_STOP);
1556 /* For LOGIN responses */
1557 static CURLcode imap_state_login_resp(struct connectdata *conn,
1561 CURLcode result = CURLE_OK;
1562 struct SessionHandle *data = conn->data;
1564 (void)instate; /* no use for this yet */
1566 if(imapcode != 'O') {
1567 failf(data, "Access denied. %c", imapcode);
1568 result = CURLE_LOGIN_DENIED;
1571 /* End of connect phase */
1572 state(conn, IMAP_STOP);
1577 /* For LIST responses */
1578 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1581 CURLcode result = CURLE_OK;
1582 char *line = conn->data->state.buffer;
1583 size_t len = strlen(line);
1585 (void)instate; /* No use for this yet */
1587 if(imapcode == '*') {
1588 /* Temporarily add the LF character back and send as body to the client */
1590 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1593 else if(imapcode != 'O')
1594 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1596 /* End of DO phase */
1597 state(conn, IMAP_STOP);
1602 /* For SELECT responses */
1603 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1606 CURLcode result = CURLE_OK;
1607 struct SessionHandle *data = conn->data;
1608 struct IMAP *imap = conn->data->req.protop;
1609 struct imap_conn *imapc = &conn->proto.imapc;
1610 const char *line = data->state.buffer;
1613 (void)instate; /* no use for this yet */
1615 if(imapcode == '*') {
1616 /* See if this is an UIDVALIDITY response */
1617 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1618 Curl_safefree(imapc->mailbox_uidvalidity);
1619 imapc->mailbox_uidvalidity = strdup(tmp);
1622 else if(imapcode == 'O') {
1623 /* Check if the UIDVALIDITY has been specified and matches */
1624 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1625 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1626 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1627 result = CURLE_REMOTE_FILE_NOT_FOUND;
1630 /* Note the currently opened mailbox on this connection */
1631 imapc->mailbox = strdup(imap->mailbox);
1634 result = imap_perform_list(conn);
1635 else if(imap->query)
1636 result = imap_perform_search(conn);
1638 result = imap_perform_fetch(conn);
1642 failf(data, "Select failed");
1643 result = CURLE_LOGIN_DENIED;
1649 /* For the (first line of the) FETCH responses */
1650 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1653 CURLcode result = CURLE_OK;
1654 struct SessionHandle *data = conn->data;
1655 struct imap_conn *imapc = &conn->proto.imapc;
1656 struct pingpong *pp = &imapc->pp;
1657 const char *ptr = data->state.buffer;
1658 bool parsed = FALSE;
1661 (void)instate; /* no use for this yet */
1663 if(imapcode != '*') {
1664 Curl_pgrsSetDownloadSize(data, -1);
1665 state(conn, IMAP_STOP);
1666 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1669 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1670 the continuation data contained within the curly brackets */
1671 while(*ptr && (*ptr != '{'))
1676 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1677 if(endptr - ptr > 1 && endptr[0] == '}' &&
1678 endptr[1] == '\r' && endptr[2] == '\0')
1683 infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1685 Curl_pgrsSetDownloadSize(data, size);
1688 /* At this point there is a bunch of data in the header "cache" that is
1689 actually body content, send it as body and then skip it. Do note
1690 that there may even be additional "headers" after the body. */
1691 size_t chunk = pp->cache_size;
1693 if(chunk > (size_t)size)
1694 /* The conversion from curl_off_t to size_t is always fine here */
1695 chunk = (size_t)size;
1697 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1701 data->req.bytecount += chunk;
1703 infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1704 " bytes, %" CURL_FORMAT_CURL_OFF_TU
1705 " bytes are left for transfer\n", (curl_off_t)chunk,
1708 /* Have we used the entire cache or just part of it?*/
1709 if(pp->cache_size > chunk) {
1710 /* Only part of it so shrink the cache to fit the trailing data */
1711 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1712 pp->cache_size -= chunk;
1715 /* Free the cache */
1716 Curl_safefree(pp->cache);
1718 /* Reset the cache size */
1723 if(data->req.bytecount == size)
1724 /* The entire data is already transferred! */
1725 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1728 data->req.maxdownload = size;
1729 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1733 /* We don't know how to parse this line */
1734 failf(pp->conn->data, "Failed to parse FETCH response.");
1735 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1738 /* End of DO phase */
1739 state(conn, IMAP_STOP);
1744 /* For final FETCH responses performed after the download */
1745 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1749 CURLcode result = CURLE_OK;
1751 (void)instate; /* No use for this yet */
1754 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1756 /* End of DONE phase */
1757 state(conn, IMAP_STOP);
1762 /* For APPEND responses */
1763 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1766 CURLcode result = CURLE_OK;
1767 struct SessionHandle *data = conn->data;
1769 (void)instate; /* No use for this yet */
1771 if(imapcode != '+') {
1772 result = CURLE_UPLOAD_FAILED;
1775 /* Set the progress upload size */
1776 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1779 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1781 /* End of DO phase */
1782 state(conn, IMAP_STOP);
1788 /* For final APPEND responses performed after the upload */
1789 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1793 CURLcode result = CURLE_OK;
1795 (void)instate; /* No use for this yet */
1798 result = CURLE_UPLOAD_FAILED;
1800 /* End of DONE phase */
1801 state(conn, IMAP_STOP);
1806 /* For SEARCH responses */
1807 static CURLcode imap_state_search_resp(struct connectdata *conn, int imapcode,
1810 CURLcode result = CURLE_OK;
1811 char *line = conn->data->state.buffer;
1812 size_t len = strlen(line);
1814 (void)instate; /* No use for this yet */
1816 if(imapcode == '*') {
1817 /* Temporarily add the LF character back and send as body to the client */
1819 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1822 else if(imapcode != 'O')
1823 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1825 /* End of DO phase */
1826 state(conn, IMAP_STOP);
1831 static CURLcode imap_statemach_act(struct connectdata *conn)
1833 CURLcode result = CURLE_OK;
1834 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1836 struct imap_conn *imapc = &conn->proto.imapc;
1837 struct pingpong *pp = &imapc->pp;
1840 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1841 if(imapc->state == IMAP_UPGRADETLS)
1842 return imap_perform_upgrade_tls(conn);
1844 /* Flush any data that needs to be sent */
1846 return Curl_pp_flushsend(pp);
1849 /* Read the response from the server */
1850 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1854 /* Was there an error parsing the response line? */
1856 return CURLE_FTP_WEIRD_SERVER_REPLY;
1861 /* We have now received a full IMAP server response */
1862 switch(imapc->state) {
1863 case IMAP_SERVERGREET:
1864 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1867 case IMAP_CAPABILITY:
1868 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1872 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1875 case IMAP_AUTHENTICATE_PLAIN:
1876 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1879 case IMAP_AUTHENTICATE_LOGIN:
1880 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1883 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1884 result = imap_state_auth_login_password_resp(conn, imapcode,
1888 #ifndef CURL_DISABLE_CRYPTO_AUTH
1889 case IMAP_AUTHENTICATE_CRAMMD5:
1890 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1893 case IMAP_AUTHENTICATE_DIGESTMD5:
1894 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1897 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1898 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1903 case IMAP_AUTHENTICATE_NTLM:
1904 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1907 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1908 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1913 #if defined(USE_KERBEROS5)
1914 case IMAP_AUTHENTICATE_GSSAPI:
1915 result = imap_state_auth_gssapi_resp(conn, imapcode, imapc->state);
1918 case IMAP_AUTHENTICATE_GSSAPI_TOKEN:
1919 result = imap_state_auth_gssapi_token_resp(conn, imapcode, imapc->state);
1922 case IMAP_AUTHENTICATE_GSSAPI_NO_DATA:
1923 result = imap_state_auth_gssapi_no_data_resp(conn, imapcode,
1928 case IMAP_AUTHENTICATE_XOAUTH2:
1929 result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state);
1932 case IMAP_AUTHENTICATE_CANCEL:
1933 result = imap_state_auth_cancel_resp(conn, imapcode, imapc->state);
1936 case IMAP_AUTHENTICATE_FINAL:
1937 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1941 result = imap_state_login_resp(conn, imapcode, imapc->state);
1945 result = imap_state_list_resp(conn, imapcode, imapc->state);
1949 result = imap_state_select_resp(conn, imapcode, imapc->state);
1953 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1956 case IMAP_FETCH_FINAL:
1957 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1961 result = imap_state_append_resp(conn, imapcode, imapc->state);
1964 case IMAP_APPEND_FINAL:
1965 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1969 result = imap_state_search_resp(conn, imapcode, imapc->state);
1973 /* fallthrough, just stop! */
1975 /* internal error */
1976 state(conn, IMAP_STOP);
1979 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1984 /* Called repeatedly until done from multi.c */
1985 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1987 CURLcode result = CURLE_OK;
1988 struct imap_conn *imapc = &conn->proto.imapc;
1990 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1991 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1992 if(result || !imapc->ssldone)
1996 result = Curl_pp_statemach(&imapc->pp, FALSE);
1997 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
2002 static CURLcode imap_block_statemach(struct connectdata *conn)
2004 CURLcode result = CURLE_OK;
2005 struct imap_conn *imapc = &conn->proto.imapc;
2007 while(imapc->state != IMAP_STOP && !result)
2008 result = Curl_pp_statemach(&imapc->pp, TRUE);
2013 /* Allocate and initialize the struct IMAP for the current SessionHandle if
2015 static CURLcode imap_init(struct connectdata *conn)
2017 CURLcode result = CURLE_OK;
2018 struct SessionHandle *data = conn->data;
2021 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
2023 result = CURLE_OUT_OF_MEMORY;
2028 /* For the IMAP "protocol connect" and "doing" phases only */
2029 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
2032 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
2035 /***********************************************************************
2039 * This function should do everything that is to be considered a part of the
2042 * The variable 'done' points to will be TRUE if the protocol-layer connect
2043 * phase is done when this function returns, or FALSE if not.
2045 static CURLcode imap_connect(struct connectdata *conn, bool *done)
2047 CURLcode result = CURLE_OK;
2048 struct imap_conn *imapc = &conn->proto.imapc;
2049 struct pingpong *pp = &imapc->pp;
2051 *done = FALSE; /* default to not done yet */
2053 /* We always support persistent connections in IMAP */
2054 connkeep(conn, "IMAP default");
2056 /* Set the default response time-out */
2057 pp->response_time = RESP_TIMEOUT;
2058 pp->statemach_act = imap_statemach_act;
2059 pp->endofresp = imap_endofresp;
2062 /* Set the default preferred authentication type and mechanism */
2063 imapc->preftype = IMAP_TYPE_ANY;
2064 imapc->prefmech = SASL_AUTH_ANY;
2066 /* Initialise the pingpong layer */
2069 /* Parse the URL options */
2070 result = imap_parse_url_options(conn);
2074 /* Start off waiting for the server greeting response */
2075 state(conn, IMAP_SERVERGREET);
2077 /* Start off with an response id of '*' */
2078 strcpy(imapc->resptag, "*");
2080 result = imap_multi_statemach(conn, done);
2085 /***********************************************************************
2089 * The DONE function. This does what needs to be done after a single DO has
2092 * Input argument is already checked for validity.
2094 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
2097 CURLcode result = CURLE_OK;
2098 struct SessionHandle *data = conn->data;
2099 struct IMAP *imap = data->req.protop;
2104 /* When the easy handle is removed from the multi interface while libcurl
2105 is still trying to resolve the host name, the IMAP struct is not yet
2106 initialized. However, the removal action calls Curl_done() which in
2107 turn calls this function, so we simply return success. */
2111 connclose(conn, "IMAP done with bad status"); /* marked for closure */
2112 result = status; /* use the already set error code */
2114 else if(!data->set.connect_only && !imap->custom &&
2115 (imap->uid || data->set.upload)) {
2116 /* Handle responses after FETCH or APPEND transfer has finished */
2117 if(!data->set.upload)
2118 state(conn, IMAP_FETCH_FINAL);
2120 /* End the APPEND command first by sending an empty line */
2121 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
2123 state(conn, IMAP_APPEND_FINAL);
2126 /* Run the state-machine
2128 TODO: when the multi interface is used, this _really_ should be using
2129 the imap_multi_statemach function but we have no general support for
2130 non-blocking DONE operations, not in the multi state machine and with
2131 Curl_done() invokes on several places in the code!
2134 result = imap_block_statemach(conn);
2137 /* Cleanup our per-request based variables */
2138 Curl_safefree(imap->mailbox);
2139 Curl_safefree(imap->uidvalidity);
2140 Curl_safefree(imap->uid);
2141 Curl_safefree(imap->section);
2142 Curl_safefree(imap->partial);
2143 Curl_safefree(imap->query);
2144 Curl_safefree(imap->custom);
2145 Curl_safefree(imap->custom_params);
2147 /* Clear the transfer mode for the next request */
2148 imap->transfer = FTPTRANSFER_BODY;
2153 /***********************************************************************
2157 * This is the actual DO function for IMAP. Fetch or append a message, or do
2158 * other things according to the options previously setup.
2160 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
2163 /* This is IMAP and no proxy */
2164 CURLcode result = CURLE_OK;
2165 struct SessionHandle *data = conn->data;
2166 struct IMAP *imap = data->req.protop;
2167 struct imap_conn *imapc = &conn->proto.imapc;
2168 bool selected = FALSE;
2170 DEBUGF(infof(conn->data, "DO phase starts\n"));
2172 if(conn->data->set.opt_no_body) {
2173 /* Requested no body means no transfer */
2174 imap->transfer = FTPTRANSFER_INFO;
2177 *dophase_done = FALSE; /* not done yet */
2179 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
2180 has already been selected on this connection */
2181 if(imap->mailbox && imapc->mailbox &&
2182 !strcmp(imap->mailbox, imapc->mailbox) &&
2183 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
2184 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
2187 /* Start the first command in the DO phase */
2188 if(conn->data->set.upload)
2189 /* APPEND can be executed directly */
2190 result = imap_perform_append(conn);
2191 else if(imap->custom && (selected || !imap->mailbox))
2192 /* Custom command using the same mailbox or no mailbox */
2193 result = imap_perform_list(conn);
2194 else if(!imap->custom && selected && imap->uid)
2195 /* FETCH from the same mailbox */
2196 result = imap_perform_fetch(conn);
2197 else if(!imap->custom && selected && imap->query)
2198 /* SEARCH the current mailbox */
2199 result = imap_perform_search(conn);
2200 else if(imap->mailbox && !selected &&
2201 (imap->custom || imap->uid || imap->query))
2202 /* SELECT the mailbox */
2203 result = imap_perform_select(conn);
2206 result = imap_perform_list(conn);
2211 /* Run the state-machine */
2212 result = imap_multi_statemach(conn, dophase_done);
2214 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
2217 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2222 /***********************************************************************
2226 * This function is registered as 'curl_do' function. It decodes the path
2227 * parts etc as a wrapper to the actual DO function (imap_perform).
2229 * The input argument is already checked for validity.
2231 static CURLcode imap_do(struct connectdata *conn, bool *done)
2233 CURLcode result = CURLE_OK;
2235 *done = FALSE; /* default to false */
2237 /* Parse the URL path */
2238 result = imap_parse_url_path(conn);
2242 /* Parse the custom request */
2243 result = imap_parse_custom_request(conn);
2247 result = imap_regular_transfer(conn, done);
2252 /***********************************************************************
2256 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
2257 * resources. BLOCKING.
2259 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2261 struct imap_conn *imapc = &conn->proto.imapc;
2263 /* We cannot send quit unconditionally. If this connection is stale or
2264 bad in any way, sending quit and waiting around here will make the
2265 disconnect wait in vain and cause more problems than we need to. */
2267 /* The IMAP session may or may not have been allocated/setup at this
2269 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
2270 if(!imap_perform_logout(conn))
2271 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
2273 /* Disconnect from the server */
2274 Curl_pp_disconnect(&imapc->pp);
2276 /* Cleanup the SASL module */
2277 Curl_sasl_cleanup(conn, imapc->authused);
2279 /* Cleanup our connection based variables */
2280 Curl_safefree(imapc->mailbox);
2281 Curl_safefree(imapc->mailbox_uidvalidity);
2286 /* Call this when the DO phase has completed */
2287 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2289 struct IMAP *imap = conn->data->req.protop;
2293 if(imap->transfer != FTPTRANSFER_BODY)
2294 /* no data to transfer */
2295 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2300 /* Called from multi.c while DOing */
2301 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2303 CURLcode result = imap_multi_statemach(conn, dophase_done);
2306 DEBUGF(infof(conn->data, "DO phase failed\n"));
2307 else if(*dophase_done) {
2308 result = imap_dophase_done(conn, FALSE /* not connected */);
2310 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2316 /***********************************************************************
2318 * imap_regular_transfer()
2320 * The input argument is already checked for validity.
2322 * Performs all commands done before a regular transfer between a local and a
2325 static CURLcode imap_regular_transfer(struct connectdata *conn,
2328 CURLcode result = CURLE_OK;
2329 bool connected = FALSE;
2330 struct SessionHandle *data = conn->data;
2332 /* Make sure size is unknown at this point */
2333 data->req.size = -1;
2335 /* Set the progress data */
2336 Curl_pgrsSetUploadCounter(data, 0);
2337 Curl_pgrsSetDownloadCounter(data, 0);
2338 Curl_pgrsSetUploadSize(data, -1);
2339 Curl_pgrsSetDownloadSize(data, -1);
2341 /* Carry out the perform */
2342 result = imap_perform(conn, &connected, dophase_done);
2344 /* Perform post DO phase operations if necessary */
2345 if(!result && *dophase_done)
2346 result = imap_dophase_done(conn, connected);
2351 static CURLcode imap_setup_connection(struct connectdata *conn)
2353 struct SessionHandle *data = conn->data;
2355 /* Initialise the IMAP layer */
2356 CURLcode result = imap_init(conn);
2360 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2361 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2362 switch and use HTTP operations only */
2363 #ifndef CURL_DISABLE_HTTP
2364 if(conn->handler == &Curl_handler_imap)
2365 conn->handler = &Curl_handler_imap_proxy;
2368 conn->handler = &Curl_handler_imaps_proxy;
2370 failf(data, "IMAPS not supported!");
2371 return CURLE_UNSUPPORTED_PROTOCOL;
2375 /* set it up as an HTTP connection instead */
2376 return conn->handler->setup_connection(conn);
2378 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2379 return CURLE_UNSUPPORTED_PROTOCOL;
2383 data->state.path++; /* don't include the initial slash */
2388 /***********************************************************************
2392 * Sends the formated string as an IMAP command to the server.
2394 * Designed to never block.
2396 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
2398 CURLcode result = CURLE_OK;
2399 struct imap_conn *imapc = &conn->proto.imapc;
2405 /* Calculate the next command ID wrapping at 3 digits */
2406 imapc->cmdid = (imapc->cmdid + 1) % 1000;
2408 /* Calculate the tag based on the connection ID and command ID */
2409 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
2410 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
2412 /* Prefix the format with the tag */
2413 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
2415 return CURLE_OUT_OF_MEMORY;
2417 /* Send the data with the tag */
2419 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
2422 Curl_safefree(taggedfmt);
2427 /***********************************************************************
2431 * Checks the input string for characters that need escaping and returns an
2432 * atom ready for sending to the server.
2434 * The returned string needs to be freed.
2437 static char *imap_atom(const char *str)
2441 size_t backsp_count = 0;
2442 size_t quote_count = 0;
2443 bool space_exists = FALSE;
2445 char *newstr = NULL;
2450 /* Count any unescaped characters */
2458 space_exists = TRUE;
2463 /* Does the input contain any unescaped characters? */
2464 if(!backsp_count && !quote_count && !space_exists)
2467 /* Calculate the new string length */
2468 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
2470 /* Allocate the new string */
2471 newstr = (char *) malloc((newlen + 1) * sizeof(char));
2475 /* Surround the string in quotes if necessary */
2479 newstr[newlen - 1] = '"';
2483 /* Copy the string, escaping backslash and quote characters along the way */
2486 if(*p1 == '\\' || *p1 == '"') {
2497 /* Terminate the string */
2498 newstr[newlen] = '\0';
2503 /***********************************************************************
2507 * Portable test of whether the specified char is a "bchar" as defined in the
2508 * grammar of RFC-5092.
2510 static bool imap_is_bchar(char ch)
2514 case ':': case '@': case '/':
2515 /* bchar -> achar */
2517 /* bchar -> achar -> uchar -> unreserved */
2518 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2519 case '7': case '8': case '9':
2520 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2521 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2522 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2523 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2524 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2525 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2526 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2527 case 'v': case 'w': case 'x': case 'y': case 'z':
2528 case '-': case '.': case '_': case '~':
2529 /* bchar -> achar -> uchar -> sub-delims-sh */
2530 case '!': case '$': case '\'': case '(': case ')': case '*':
2532 /* bchar -> achar -> uchar -> pct-encoded */
2533 case '%': /* HEXDIG chars are already included above */
2541 /***********************************************************************
2543 * imap_parse_url_options()
2545 * Parse the URL login options.
2547 static CURLcode imap_parse_url_options(struct connectdata *conn)
2549 CURLcode result = CURLE_OK;
2550 struct imap_conn *imapc = &conn->proto.imapc;
2551 const char *options = conn->options;
2552 const char *ptr = options;
2555 while(ptr && *ptr) {
2556 const char *key = ptr;
2558 while(*ptr && *ptr != '=')
2561 if(strnequal(key, "AUTH", 4)) {
2563 const char *value = ++ptr;
2567 imapc->preftype = IMAP_TYPE_NONE;
2568 imapc->prefmech = SASL_AUTH_NONE;
2571 while(*ptr && *ptr != ';') {
2576 if(strnequal(value, "*", len)) {
2577 imapc->preftype = IMAP_TYPE_ANY;
2578 imapc->prefmech = SASL_AUTH_ANY;
2580 else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
2581 imapc->preftype = IMAP_TYPE_SASL;
2582 imapc->prefmech |= SASL_MECH_LOGIN;
2584 else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
2585 imapc->preftype = IMAP_TYPE_SASL;
2586 imapc->prefmech |= SASL_MECH_PLAIN;
2588 else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
2589 imapc->preftype = IMAP_TYPE_SASL;
2590 imapc->prefmech |= SASL_MECH_CRAM_MD5;
2592 else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
2593 imapc->preftype = IMAP_TYPE_SASL;
2594 imapc->prefmech |= SASL_MECH_DIGEST_MD5;
2596 else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
2597 imapc->preftype = IMAP_TYPE_SASL;
2598 imapc->prefmech |= SASL_MECH_GSSAPI;
2600 else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
2601 imapc->preftype = IMAP_TYPE_SASL;
2602 imapc->prefmech |= SASL_MECH_NTLM;
2604 else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
2605 imapc->preftype = IMAP_TYPE_SASL;
2606 imapc->prefmech |= SASL_MECH_XOAUTH2;
2613 result = CURLE_URL_MALFORMAT;
2619 /***********************************************************************
2621 * imap_parse_url_path()
2623 * Parse the URL path into separate path components.
2626 static CURLcode imap_parse_url_path(struct connectdata *conn)
2628 /* The imap struct is already initialised in imap_connect() */
2629 CURLcode result = CURLE_OK;
2630 struct SessionHandle *data = conn->data;
2631 struct IMAP *imap = data->req.protop;
2632 const char *begin = data->state.path;
2633 const char *ptr = begin;
2635 /* See how much of the URL is a valid path and decode it */
2636 while(imap_is_bchar(*ptr))
2640 /* Remove the trailing slash if present */
2641 const char *end = ptr;
2642 if(end > begin && end[-1] == '/')
2645 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2651 imap->mailbox = NULL;
2653 /* There can be any number of parameters in the form ";NAME=VALUE" */
2654 while(*ptr == ';') {
2659 /* Find the length of the name parameter */
2661 while(*ptr && *ptr != '=')
2665 return CURLE_URL_MALFORMAT;
2667 /* Decode the name parameter */
2668 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2672 /* Find the length of the value parameter */
2674 while(imap_is_bchar(*ptr))
2677 /* Decode the value parameter */
2678 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2680 Curl_safefree(name);
2684 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2686 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2687 PARTIAL) stripping of the trailing slash character if it is present.
2689 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2690 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2691 if(valuelen > 0 && value[valuelen - 1] == '/')
2692 value[valuelen - 1] = '\0';
2694 imap->uidvalidity = value;
2697 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2698 if(valuelen > 0 && value[valuelen - 1] == '/')
2699 value[valuelen - 1] = '\0';
2704 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2705 if(valuelen > 0 && value[valuelen - 1] == '/')
2706 value[valuelen - 1] = '\0';
2708 imap->section = value;
2711 else if(Curl_raw_equal(name, "PARTIAL") && !imap->partial) {
2712 if(valuelen > 0 && value[valuelen - 1] == '/')
2713 value[valuelen - 1] = '\0';
2715 imap->partial = value;
2719 Curl_safefree(name);
2720 Curl_safefree(value);
2722 return CURLE_URL_MALFORMAT;
2725 Curl_safefree(name);
2726 Curl_safefree(value);
2729 /* Does the URL contain a query parameter? Only valid when we have a mailbox
2730 and no UID as per RFC-5092 */
2731 if(imap->mailbox && !imap->uid && *ptr == '?') {
2732 /* Find the length of the query parameter */
2734 while(imap_is_bchar(*ptr))
2737 /* Decode the query parameter */
2738 result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2744 /* Any extra stuff at the end of the URL is an error */
2746 return CURLE_URL_MALFORMAT;
2751 /***********************************************************************
2753 * imap_parse_custom_request()
2755 * Parse the custom request.
2757 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2759 CURLcode result = CURLE_OK;
2760 struct SessionHandle *data = conn->data;
2761 struct IMAP *imap = data->req.protop;
2762 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2765 /* URL decode the custom request */
2766 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2768 /* Extract the parameters if specified */
2770 const char *params = imap->custom;
2772 while(*params && *params != ' ')
2776 imap->custom_params = strdup(params);
2777 imap->custom[params - imap->custom] = '\0';
2779 if(!imap->custom_params)
2780 result = CURLE_OUT_OF_MEMORY;
2788 /***********************************************************************
2790 * imap_calc_sasl_details()
2792 * Calculate the required login details for SASL authentication.
2794 static CURLcode imap_calc_sasl_details(struct connectdata *conn,
2796 char **initresp, size_t *len,
2797 imapstate *state1, imapstate *state2)
2799 CURLcode result = CURLE_OK;
2800 struct SessionHandle *data = conn->data;
2801 struct imap_conn *imapc = &conn->proto.imapc;
2803 /* Calculate the supported authentication mechanism, by decreasing order of
2804 security, as well as the initial response where appropriate */
2805 #if defined(USE_KERBEROS5)
2806 if((imapc->authmechs & SASL_MECH_GSSAPI) &&
2807 (imapc->prefmech & SASL_MECH_GSSAPI)) {
2808 imapc->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
2810 *mech = SASL_MECH_STRING_GSSAPI;
2811 *state1 = IMAP_AUTHENTICATE_GSSAPI;
2812 *state2 = IMAP_AUTHENTICATE_GSSAPI_TOKEN;
2813 imapc->authused = SASL_MECH_GSSAPI;
2815 if(imapc->ir_supported || data->set.sasl_ir)
2816 result = Curl_sasl_create_gssapi_user_message(data, conn->user,
2817 conn->passwd, "imap",
2824 #ifndef CURL_DISABLE_CRYPTO_AUTH
2825 if((imapc->authmechs & SASL_MECH_DIGEST_MD5) &&
2826 (imapc->prefmech & SASL_MECH_DIGEST_MD5)) {
2827 *mech = SASL_MECH_STRING_DIGEST_MD5;
2828 *state1 = IMAP_AUTHENTICATE_DIGESTMD5;
2829 imapc->authused = SASL_MECH_DIGEST_MD5;
2831 else if((imapc->authmechs & SASL_MECH_CRAM_MD5) &&
2832 (imapc->prefmech & SASL_MECH_CRAM_MD5)) {
2833 *mech = SASL_MECH_STRING_CRAM_MD5;
2834 *state1 = IMAP_AUTHENTICATE_CRAMMD5;
2835 imapc->authused = SASL_MECH_CRAM_MD5;
2840 if((imapc->authmechs & SASL_MECH_NTLM) &&
2841 (imapc->prefmech & SASL_MECH_NTLM)) {
2842 *mech = SASL_MECH_STRING_NTLM;
2843 *state1 = IMAP_AUTHENTICATE_NTLM;
2844 *state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
2845 imapc->authused = SASL_MECH_NTLM;
2847 if(imapc->ir_supported || data->set.sasl_ir)
2848 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2854 if(((imapc->authmechs & SASL_MECH_XOAUTH2) &&
2855 (imapc->prefmech & SASL_MECH_XOAUTH2) &&
2856 (imapc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2857 *mech = SASL_MECH_STRING_XOAUTH2;
2858 *state1 = IMAP_AUTHENTICATE_XOAUTH2;
2859 *state2 = IMAP_AUTHENTICATE_FINAL;
2860 imapc->authused = SASL_MECH_XOAUTH2;
2862 if(imapc->ir_supported || data->set.sasl_ir)
2863 result = Curl_sasl_create_xoauth2_message(data, conn->user,
2864 conn->xoauth2_bearer,
2867 else if((imapc->authmechs & SASL_MECH_LOGIN) &&
2868 (imapc->prefmech & SASL_MECH_LOGIN)) {
2869 *mech = SASL_MECH_STRING_LOGIN;
2870 *state1 = IMAP_AUTHENTICATE_LOGIN;
2871 *state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
2872 imapc->authused = SASL_MECH_LOGIN;
2874 if(imapc->ir_supported || data->set.sasl_ir)
2875 result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2877 else if((imapc->authmechs & SASL_MECH_PLAIN) &&
2878 (imapc->prefmech & SASL_MECH_PLAIN)) {
2879 *mech = SASL_MECH_STRING_PLAIN;
2880 *state1 = IMAP_AUTHENTICATE_PLAIN;
2881 *state2 = IMAP_AUTHENTICATE_FINAL;
2882 imapc->authused = SASL_MECH_PLAIN;
2884 if(imapc->ir_supported || data->set.sasl_ir)
2885 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2892 #endif /* CURL_DISABLE_IMAP */