1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, 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 https://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 */
71 #include "strtoofft.h"
73 #include "vtls/vtls.h"
80 #include "curl_sasl.h"
83 /* The last 3 #include files should be in this order */
84 #include "curl_printf.h"
85 #include "curl_memory.h"
88 /* Local API functions */
89 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
90 static CURLcode imap_do(struct connectdata *conn, bool *done);
91 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
93 static CURLcode imap_connect(struct connectdata *conn, bool *done);
94 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
95 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
96 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
98 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
99 static CURLcode imap_setup_connection(struct connectdata *conn);
100 static char *imap_atom(const char *str, bool escape_only);
101 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
102 static CURLcode imap_parse_url_options(struct connectdata *conn);
103 static CURLcode imap_parse_url_path(struct connectdata *conn);
104 static CURLcode imap_parse_custom_request(struct connectdata *conn);
105 static CURLcode imap_perform_authenticate(struct connectdata *conn,
107 const char *initresp);
108 static CURLcode imap_continue_authenticate(struct connectdata *conn,
110 static void imap_get_message(char *buffer, char **outptr);
113 * IMAP protocol handler.
116 const struct Curl_handler Curl_handler_imap = {
118 imap_setup_connection, /* setup_connection */
120 imap_done, /* done */
121 ZERO_NULL, /* do_more */
122 imap_connect, /* connect_it */
123 imap_multi_statemach, /* connecting */
124 imap_doing, /* doing */
125 imap_getsock, /* proto_getsock */
126 imap_getsock, /* doing_getsock */
127 ZERO_NULL, /* domore_getsock */
128 ZERO_NULL, /* perform_getsock */
129 imap_disconnect, /* disconnect */
130 ZERO_NULL, /* readwrite */
131 PORT_IMAP, /* defport */
132 CURLPROTO_IMAP, /* protocol */
133 PROTOPT_CLOSEACTION| /* 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 /* flags */
163 #ifndef CURL_DISABLE_HTTP
165 * HTTP-proxyed IMAP protocol handler.
168 static const struct Curl_handler Curl_handler_imap_proxy = {
170 Curl_http_setup_conn, /* setup_connection */
171 Curl_http, /* do_it */
172 Curl_http_done, /* done */
173 ZERO_NULL, /* do_more */
174 ZERO_NULL, /* connect_it */
175 ZERO_NULL, /* connecting */
176 ZERO_NULL, /* doing */
177 ZERO_NULL, /* proto_getsock */
178 ZERO_NULL, /* doing_getsock */
179 ZERO_NULL, /* domore_getsock */
180 ZERO_NULL, /* perform_getsock */
181 ZERO_NULL, /* disconnect */
182 ZERO_NULL, /* readwrite */
183 PORT_IMAP, /* defport */
184 CURLPROTO_HTTP, /* protocol */
185 PROTOPT_NONE /* flags */
190 * HTTP-proxyed IMAPS protocol handler.
193 static const struct Curl_handler Curl_handler_imaps_proxy = {
194 "IMAPS", /* scheme */
195 Curl_http_setup_conn, /* setup_connection */
196 Curl_http, /* do_it */
197 Curl_http_done, /* done */
198 ZERO_NULL, /* do_more */
199 ZERO_NULL, /* connect_it */
200 ZERO_NULL, /* connecting */
201 ZERO_NULL, /* doing */
202 ZERO_NULL, /* proto_getsock */
203 ZERO_NULL, /* doing_getsock */
204 ZERO_NULL, /* domore_getsock */
205 ZERO_NULL, /* perform_getsock */
206 ZERO_NULL, /* disconnect */
207 ZERO_NULL, /* readwrite */
208 PORT_IMAPS, /* defport */
209 CURLPROTO_HTTP, /* protocol */
210 PROTOPT_NONE /* flags */
215 /* SASL parameters for the imap protocol */
216 static const struct SASLproto saslimap = {
217 "imap", /* The service name */
218 '+', /* Code received when continuation is expected */
219 'O', /* Code to receive upon authentication success */
220 0, /* Maximum initial response length (no max) */
221 imap_perform_authenticate, /* Send authentication command */
222 imap_continue_authenticate, /* Send authentication continuation */
223 imap_get_message /* Get SASL response message */
228 static void imap_to_imaps(struct connectdata *conn)
230 /* Change the connection handler */
231 conn->handler = &Curl_handler_imaps;
233 /* Set the connection's upgraded to TLS flag */
234 conn->tls_upgraded = TRUE;
237 #define imap_to_imaps(x) Curl_nop_stmt
240 /***********************************************************************
244 * Determines whether the untagged response is related to the specified
245 * command by checking if it is in format "* <command-name> ..." or
246 * "* <number> <command-name> ...".
248 * The "* " marker is assumed to have already been checked by the caller.
250 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
252 const char *end = line + len;
253 size_t cmd_len = strlen(cmd);
255 /* Skip the untagged response marker */
258 /* Do we have a number after the marker? */
259 if(line < end && ISDIGIT(*line)) {
260 /* Skip the number */
263 while(line < end && ISDIGIT(*line));
265 /* Do we have the space character? */
266 if(line == end || *line != ' ')
272 /* Does the command name match and is it followed by a space character or at
274 if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
275 (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
281 /***********************************************************************
285 * Checks whether the given string is a valid tagged, untagged or continuation
286 * response which can be processed by the response handler.
288 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
291 struct IMAP *imap = conn->data->req.protop;
292 struct imap_conn *imapc = &conn->proto.imapc;
293 const char *id = imapc->resptag;
294 size_t id_len = strlen(id);
296 /* Do we have a tagged command response? */
297 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
301 if(len >= 2 && !memcmp(line, "OK", 2))
303 else if(len >= 2 && !memcmp(line, "NO", 2))
305 else if(len >= 3 && !memcmp(line, "BAD", 3))
308 failf(conn->data, "Bad tagged response");
315 /* Do we have an untagged command response? */
316 if(len >= 2 && !memcmp("* ", line, 2)) {
317 switch(imapc->state) {
318 /* States which are interested in untagged responses */
319 case IMAP_CAPABILITY:
320 if(!imap_matchresp(line, len, "CAPABILITY"))
325 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
326 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
327 (strcmp(imap->custom, "STORE") ||
328 !imap_matchresp(line, len, "FETCH")) &&
329 strcmp(imap->custom, "SELECT") &&
330 strcmp(imap->custom, "EXAMINE") &&
331 strcmp(imap->custom, "SEARCH") &&
332 strcmp(imap->custom, "EXPUNGE") &&
333 strcmp(imap->custom, "LSUB") &&
334 strcmp(imap->custom, "UID") &&
335 strcmp(imap->custom, "NOOP")))
340 /* SELECT is special in that its untagged responses do not have a
341 common prefix so accept anything! */
345 if(!imap_matchresp(line, len, "FETCH"))
350 if(!imap_matchresp(line, len, "SEARCH"))
354 /* Ignore other untagged responses */
363 /* Do we have a continuation response? This should be a + symbol followed by
364 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
365 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
366 some e-mail servers ignore this and only send a single + instead. */
367 if(imap && !imap->custom && ((len == 3 && !memcmp("+", line, 1)) ||
368 (len >= 2 && !memcmp("+ ", line, 2)))) {
369 switch(imapc->state) {
370 /* States which are interested in continuation responses */
371 case IMAP_AUTHENTICATE:
377 failf(conn->data, "Unexpected continuation response");
385 return FALSE; /* Nothing for us */
388 /***********************************************************************
392 * Gets the authentication message from the response buffer.
394 static void imap_get_message(char *buffer, char **outptr)
397 char *message = NULL;
399 /* Find the start of the message */
400 for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
403 /* Find the end of the message */
404 for(len = strlen(message); len--;)
405 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
406 message[len] != '\t')
409 /* Terminate the message */
417 /***********************************************************************
421 * This is the ONLY way to change IMAP state!
423 static void state(struct connectdata *conn, imapstate newstate)
425 struct imap_conn *imapc = &conn->proto.imapc;
426 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
427 /* for debug purposes */
428 static const char * const names[]={
447 if(imapc->state != newstate)
448 infof(conn->data, "IMAP %p state change from %s to %s\n",
449 (void *)imapc, names[imapc->state], names[newstate]);
452 imapc->state = newstate;
455 /***********************************************************************
457 * imap_perform_capability()
459 * Sends the CAPABILITY command in order to obtain a list of server side
460 * supported capabilities.
462 static CURLcode imap_perform_capability(struct connectdata *conn)
464 CURLcode result = CURLE_OK;
465 struct imap_conn *imapc = &conn->proto.imapc;
467 imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
468 imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
469 imapc->tls_supported = FALSE; /* Clear the TLS capability */
471 /* Send the CAPABILITY command */
472 result = imap_sendf(conn, "CAPABILITY");
475 state(conn, IMAP_CAPABILITY);
480 /***********************************************************************
482 * imap_perform_starttls()
484 * Sends the STARTTLS command to start the upgrade to TLS.
486 static CURLcode imap_perform_starttls(struct connectdata *conn)
488 CURLcode result = CURLE_OK;
490 /* Send the STARTTLS command */
491 result = imap_sendf(conn, "STARTTLS");
494 state(conn, IMAP_STARTTLS);
499 /***********************************************************************
501 * imap_perform_upgrade_tls()
503 * Performs the upgrade to TLS.
505 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
507 CURLcode result = CURLE_OK;
508 struct imap_conn *imapc = &conn->proto.imapc;
510 /* Start the SSL connection */
511 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
514 if(imapc->state != IMAP_UPGRADETLS)
515 state(conn, IMAP_UPGRADETLS);
519 result = imap_perform_capability(conn);
526 /***********************************************************************
528 * imap_perform_login()
530 * Sends a clear text LOGIN command to authenticate with.
532 static CURLcode imap_perform_login(struct connectdata *conn)
534 CURLcode result = CURLE_OK;
538 /* Check we have a username and password to authenticate with and end the
539 connect phase if we don't */
540 if(!conn->bits.user_passwd) {
541 state(conn, IMAP_STOP);
546 /* Make sure the username and password are in the correct atom format */
547 user = imap_atom(conn->user, false);
548 passwd = imap_atom(conn->passwd, false);
550 /* Send the LOGIN command */
551 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
552 passwd ? passwd : "");
558 state(conn, IMAP_LOGIN);
563 /***********************************************************************
565 * imap_perform_authenticate()
567 * Sends an AUTHENTICATE command allowing the client to login with the given
568 * SASL authentication mechanism.
570 static CURLcode imap_perform_authenticate(struct connectdata *conn,
572 const char *initresp)
574 CURLcode result = CURLE_OK;
577 /* Send the AUTHENTICATE command with the initial response */
578 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
581 /* Send the AUTHENTICATE command */
582 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
588 /***********************************************************************
590 * imap_continue_authenticate()
592 * Sends SASL continuation data or cancellation.
594 static CURLcode imap_continue_authenticate(struct connectdata *conn,
597 struct imap_conn *imapc = &conn->proto.imapc;
599 return Curl_pp_sendf(&imapc->pp, "%s", resp);
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 saslprogress progress;
616 /* Check we have enough data to authenticate with and end the
617 connect phase if we don't */
618 if(!Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
619 state(conn, IMAP_STOP);
623 /* Calculate the SASL login details */
624 result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
627 if(progress == SASL_INPROGRESS)
628 state(conn, IMAP_AUTHENTICATE);
629 else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
630 /* Perform clear text authentication */
631 result = imap_perform_login(conn);
633 /* Other mechanisms not supported */
634 infof(conn->data, "No known authentication mechanisms supported!\n");
635 result = CURLE_LOGIN_DENIED;
642 /***********************************************************************
644 * imap_perform_list()
646 * Sends a LIST command or an alternative custom request.
648 static CURLcode imap_perform_list(struct connectdata *conn)
650 CURLcode result = CURLE_OK;
651 struct Curl_easy *data = conn->data;
652 struct IMAP *imap = data->req.protop;
656 /* Send the custom request */
657 result = imap_sendf(conn, "%s%s", imap->custom,
658 imap->custom_params ? imap->custom_params : "");
660 /* Make sure the mailbox is in the correct atom format if necessary */
661 mailbox = imap->mailbox ? imap_atom(imap->mailbox, true) : strdup("");
663 return CURLE_OUT_OF_MEMORY;
665 /* Send the LIST command */
666 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
672 state(conn, IMAP_LIST);
677 /***********************************************************************
679 * imap_perform_select()
681 * Sends a SELECT command to ask the server to change the selected mailbox.
683 static CURLcode imap_perform_select(struct connectdata *conn)
685 CURLcode result = CURLE_OK;
686 struct Curl_easy *data = conn->data;
687 struct IMAP *imap = data->req.protop;
688 struct imap_conn *imapc = &conn->proto.imapc;
691 /* Invalidate old information as we are switching mailboxes */
692 Curl_safefree(imapc->mailbox);
693 Curl_safefree(imapc->mailbox_uidvalidity);
695 /* Check we have a mailbox */
697 failf(conn->data, "Cannot SELECT without a mailbox.");
698 return CURLE_URL_MALFORMAT;
701 /* Make sure the mailbox is in the correct atom format */
702 mailbox = imap_atom(imap->mailbox, false);
704 return CURLE_OUT_OF_MEMORY;
706 /* Send the SELECT command */
707 result = imap_sendf(conn, "SELECT %s", mailbox);
712 state(conn, IMAP_SELECT);
717 /***********************************************************************
719 * imap_perform_fetch()
721 * Sends a FETCH command to initiate the download of a message.
723 static CURLcode imap_perform_fetch(struct connectdata *conn)
725 CURLcode result = CURLE_OK;
726 struct IMAP *imap = conn->data->req.protop;
728 /* Check we have a UID */
730 failf(conn->data, "Cannot FETCH without a UID.");
731 return CURLE_URL_MALFORMAT;
734 /* Send the FETCH command */
736 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
738 imap->section ? imap->section : "",
741 result = imap_sendf(conn, "FETCH %s BODY[%s]",
743 imap->section ? imap->section : "");
746 state(conn, IMAP_FETCH);
751 /***********************************************************************
753 * imap_perform_append()
755 * Sends an APPEND command to initiate the upload of a message.
757 static CURLcode imap_perform_append(struct connectdata *conn)
759 CURLcode result = CURLE_OK;
760 struct IMAP *imap = conn->data->req.protop;
763 /* Check we have a mailbox */
765 failf(conn->data, "Cannot APPEND without a mailbox.");
766 return CURLE_URL_MALFORMAT;
769 /* Check we know the size of the upload */
770 if(conn->data->state.infilesize < 0) {
771 failf(conn->data, "Cannot APPEND with unknown input file size\n");
772 return CURLE_UPLOAD_FAILED;
775 /* Make sure the mailbox is in the correct atom format */
776 mailbox = imap_atom(imap->mailbox, false);
778 return CURLE_OUT_OF_MEMORY;
780 /* Send the APPEND command */
781 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
782 mailbox, conn->data->state.infilesize);
787 state(conn, IMAP_APPEND);
792 /***********************************************************************
794 * imap_perform_search()
796 * Sends a SEARCH command.
798 static CURLcode imap_perform_search(struct connectdata *conn)
800 CURLcode result = CURLE_OK;
801 struct IMAP *imap = conn->data->req.protop;
803 /* Check we have a query string */
805 failf(conn->data, "Cannot SEARCH without a query string.");
806 return CURLE_URL_MALFORMAT;
809 /* Send the SEARCH command */
810 result = imap_sendf(conn, "SEARCH %s", imap->query);
813 state(conn, IMAP_SEARCH);
818 /***********************************************************************
820 * imap_perform_logout()
822 * Performs the logout action prior to sclose() being called.
824 static CURLcode imap_perform_logout(struct connectdata *conn)
826 CURLcode result = CURLE_OK;
828 /* Send the LOGOUT command */
829 result = imap_sendf(conn, "LOGOUT");
832 state(conn, IMAP_LOGOUT);
837 /* For the initial server greeting */
838 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
842 CURLcode result = CURLE_OK;
843 struct Curl_easy *data = conn->data;
845 (void)instate; /* no use for this yet */
847 if(imapcode != 'O') {
848 failf(data, "Got unexpected imap-server response");
849 result = CURLE_WEIRD_SERVER_REPLY;
852 result = imap_perform_capability(conn);
857 /* For CAPABILITY responses */
858 static CURLcode imap_state_capability_resp(struct connectdata *conn,
862 CURLcode result = CURLE_OK;
863 struct Curl_easy *data = conn->data;
864 struct imap_conn *imapc = &conn->proto.imapc;
865 const char *line = data->state.buffer;
868 (void)instate; /* no use for this yet */
870 /* Do we have a untagged response? */
871 if(imapcode == '*') {
874 /* Loop through the data line */
877 (*line == ' ' || *line == '\t' ||
878 *line == '\r' || *line == '\n')) {
886 /* Extract the word */
887 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
888 line[wordlen] != '\t' && line[wordlen] != '\r' &&
889 line[wordlen] != '\n';)
892 /* Does the server support the STARTTLS capability? */
893 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
894 imapc->tls_supported = TRUE;
896 /* Has the server explicitly disabled clear text authentication? */
897 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
898 imapc->login_disabled = TRUE;
900 /* Does the server support the SASL-IR capability? */
901 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
902 imapc->ir_supported = TRUE;
904 /* Do we have a SASL based authentication mechanism? */
905 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
907 unsigned int mechbit;
912 /* Test the word for a matching authentication mechanism */
913 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
914 if(mechbit && llen == wordlen)
915 imapc->sasl.authmechs |= mechbit;
921 else if(imapcode == 'O') {
922 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
923 /* We don't have a SSL/TLS connection yet, but SSL is requested */
924 if(imapc->tls_supported)
925 /* Switch to TLS connection now */
926 result = imap_perform_starttls(conn);
927 else if(data->set.use_ssl == CURLUSESSL_TRY)
928 /* Fallback and carry on with authentication */
929 result = imap_perform_authentication(conn);
931 failf(data, "STARTTLS not supported.");
932 result = CURLE_USE_SSL_FAILED;
936 result = imap_perform_authentication(conn);
939 result = imap_perform_authentication(conn);
944 /* For STARTTLS responses */
945 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
949 CURLcode result = CURLE_OK;
950 struct Curl_easy *data = conn->data;
952 (void)instate; /* no use for this yet */
954 if(imapcode != 'O') {
955 if(data->set.use_ssl != CURLUSESSL_TRY) {
956 failf(data, "STARTTLS denied");
957 result = CURLE_USE_SSL_FAILED;
960 result = imap_perform_authentication(conn);
963 result = imap_perform_upgrade_tls(conn);
968 /* For SASL authentication responses */
969 static CURLcode imap_state_auth_resp(struct connectdata *conn,
973 CURLcode result = CURLE_OK;
974 struct Curl_easy *data = conn->data;
975 struct imap_conn *imapc = &conn->proto.imapc;
976 saslprogress progress;
978 (void)instate; /* no use for this yet */
980 result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
984 state(conn, IMAP_STOP); /* Authenticated */
986 case SASL_IDLE: /* No mechanism left after cancellation */
987 if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
988 /* Perform clear text authentication */
989 result = imap_perform_login(conn);
991 failf(data, "Authentication cancelled");
992 result = CURLE_LOGIN_DENIED;
1002 /* For LOGIN responses */
1003 static CURLcode imap_state_login_resp(struct connectdata *conn,
1007 CURLcode result = CURLE_OK;
1008 struct Curl_easy *data = conn->data;
1010 (void)instate; /* no use for this yet */
1012 if(imapcode != 'O') {
1013 failf(data, "Access denied. %c", imapcode);
1014 result = CURLE_LOGIN_DENIED;
1017 /* End of connect phase */
1018 state(conn, IMAP_STOP);
1023 /* For LIST and SEARCH responses */
1024 static CURLcode imap_state_listsearch_resp(struct connectdata *conn,
1028 CURLcode result = CURLE_OK;
1029 char *line = conn->data->state.buffer;
1030 size_t len = strlen(line);
1032 (void)instate; /* No use for this yet */
1034 if(imapcode == '*') {
1035 /* Temporarily add the LF character back and send as body to the client */
1037 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1040 else if(imapcode != 'O')
1041 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1043 /* End of DO phase */
1044 state(conn, IMAP_STOP);
1049 /* For SELECT responses */
1050 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1053 CURLcode result = CURLE_OK;
1054 struct Curl_easy *data = conn->data;
1055 struct IMAP *imap = conn->data->req.protop;
1056 struct imap_conn *imapc = &conn->proto.imapc;
1057 const char *line = data->state.buffer;
1060 (void)instate; /* no use for this yet */
1062 if(imapcode == '*') {
1063 /* See if this is an UIDVALIDITY response */
1064 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1065 Curl_safefree(imapc->mailbox_uidvalidity);
1066 imapc->mailbox_uidvalidity = strdup(tmp);
1069 else if(imapcode == 'O') {
1070 /* Check if the UIDVALIDITY has been specified and matches */
1071 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1072 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1073 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1074 result = CURLE_REMOTE_FILE_NOT_FOUND;
1077 /* Note the currently opened mailbox on this connection */
1078 imapc->mailbox = strdup(imap->mailbox);
1081 result = imap_perform_list(conn);
1082 else if(imap->query)
1083 result = imap_perform_search(conn);
1085 result = imap_perform_fetch(conn);
1089 failf(data, "Select failed");
1090 result = CURLE_LOGIN_DENIED;
1096 /* For the (first line of the) FETCH responses */
1097 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1100 CURLcode result = CURLE_OK;
1101 struct Curl_easy *data = conn->data;
1102 struct imap_conn *imapc = &conn->proto.imapc;
1103 struct pingpong *pp = &imapc->pp;
1104 const char *ptr = data->state.buffer;
1105 bool parsed = FALSE;
1106 curl_off_t size = 0;
1108 (void)instate; /* no use for this yet */
1110 if(imapcode != '*') {
1111 Curl_pgrsSetDownloadSize(data, -1);
1112 state(conn, IMAP_STOP);
1113 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1116 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1117 the continuation data contained within the curly brackets */
1118 while(*ptr && (*ptr != '{'))
1123 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1124 if(endptr - ptr > 1 && endptr[0] == '}' &&
1125 endptr[1] == '\r' && endptr[2] == '\0')
1130 infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1132 Curl_pgrsSetDownloadSize(data, size);
1135 /* At this point there is a bunch of data in the header "cache" that is
1136 actually body content, send it as body and then skip it. Do note
1137 that there may even be additional "headers" after the body. */
1138 size_t chunk = pp->cache_size;
1140 if(chunk > (size_t)size)
1141 /* The conversion from curl_off_t to size_t is always fine here */
1142 chunk = (size_t)size;
1144 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1148 data->req.bytecount += chunk;
1150 infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1151 " bytes, %" CURL_FORMAT_CURL_OFF_TU
1152 " bytes are left for transfer\n", (curl_off_t)chunk,
1155 /* Have we used the entire cache or just part of it?*/
1156 if(pp->cache_size > chunk) {
1157 /* Only part of it so shrink the cache to fit the trailing data */
1158 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1159 pp->cache_size -= chunk;
1162 /* Free the cache */
1163 Curl_safefree(pp->cache);
1165 /* Reset the cache size */
1170 if(data->req.bytecount == size)
1171 /* The entire data is already transferred! */
1172 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1175 data->req.maxdownload = size;
1176 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1180 /* We don't know how to parse this line */
1181 failf(pp->conn->data, "Failed to parse FETCH response.");
1182 result = CURLE_WEIRD_SERVER_REPLY;
1185 /* End of DO phase */
1186 state(conn, IMAP_STOP);
1191 /* For final FETCH responses performed after the download */
1192 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1196 CURLcode result = CURLE_OK;
1198 (void)instate; /* No use for this yet */
1201 result = CURLE_WEIRD_SERVER_REPLY;
1203 /* End of DONE phase */
1204 state(conn, IMAP_STOP);
1209 /* For APPEND responses */
1210 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1213 CURLcode result = CURLE_OK;
1214 struct Curl_easy *data = conn->data;
1216 (void)instate; /* No use for this yet */
1218 if(imapcode != '+') {
1219 result = CURLE_UPLOAD_FAILED;
1222 /* Set the progress upload size */
1223 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1226 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1228 /* End of DO phase */
1229 state(conn, IMAP_STOP);
1235 /* For final APPEND responses performed after the upload */
1236 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1240 CURLcode result = CURLE_OK;
1242 (void)instate; /* No use for this yet */
1245 result = CURLE_UPLOAD_FAILED;
1247 /* End of DONE phase */
1248 state(conn, IMAP_STOP);
1253 static CURLcode imap_statemach_act(struct connectdata *conn)
1255 CURLcode result = CURLE_OK;
1256 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1258 struct imap_conn *imapc = &conn->proto.imapc;
1259 struct pingpong *pp = &imapc->pp;
1262 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1263 if(imapc->state == IMAP_UPGRADETLS)
1264 return imap_perform_upgrade_tls(conn);
1266 /* Flush any data that needs to be sent */
1268 return Curl_pp_flushsend(pp);
1271 /* Read the response from the server */
1272 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1276 /* Was there an error parsing the response line? */
1278 return CURLE_WEIRD_SERVER_REPLY;
1283 /* We have now received a full IMAP server response */
1284 switch(imapc->state) {
1285 case IMAP_SERVERGREET:
1286 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1289 case IMAP_CAPABILITY:
1290 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1294 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1297 case IMAP_AUTHENTICATE:
1298 result = imap_state_auth_resp(conn, imapcode, imapc->state);
1302 result = imap_state_login_resp(conn, imapcode, imapc->state);
1306 result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
1310 result = imap_state_select_resp(conn, imapcode, imapc->state);
1314 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1317 case IMAP_FETCH_FINAL:
1318 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1322 result = imap_state_append_resp(conn, imapcode, imapc->state);
1325 case IMAP_APPEND_FINAL:
1326 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1330 result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
1334 /* fallthrough, just stop! */
1336 /* internal error */
1337 state(conn, IMAP_STOP);
1340 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1345 /* Called repeatedly until done from multi.c */
1346 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1348 CURLcode result = CURLE_OK;
1349 struct imap_conn *imapc = &conn->proto.imapc;
1351 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1352 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1353 if(result || !imapc->ssldone)
1357 result = Curl_pp_statemach(&imapc->pp, FALSE);
1358 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1363 static CURLcode imap_block_statemach(struct connectdata *conn)
1365 CURLcode result = CURLE_OK;
1366 struct imap_conn *imapc = &conn->proto.imapc;
1368 while(imapc->state != IMAP_STOP && !result)
1369 result = Curl_pp_statemach(&imapc->pp, TRUE);
1374 /* Allocate and initialize the struct IMAP for the current Curl_easy if
1376 static CURLcode imap_init(struct connectdata *conn)
1378 CURLcode result = CURLE_OK;
1379 struct Curl_easy *data = conn->data;
1382 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1384 result = CURLE_OUT_OF_MEMORY;
1389 /* For the IMAP "protocol connect" and "doing" phases only */
1390 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1393 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1396 /***********************************************************************
1400 * This function should do everything that is to be considered a part of the
1403 * The variable 'done' points to will be TRUE if the protocol-layer connect
1404 * phase is done when this function returns, or FALSE if not.
1406 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1408 CURLcode result = CURLE_OK;
1409 struct imap_conn *imapc = &conn->proto.imapc;
1410 struct pingpong *pp = &imapc->pp;
1412 *done = FALSE; /* default to not done yet */
1414 /* We always support persistent connections in IMAP */
1415 connkeep(conn, "IMAP default");
1417 /* Set the default response time-out */
1418 pp->response_time = RESP_TIMEOUT;
1419 pp->statemach_act = imap_statemach_act;
1420 pp->endofresp = imap_endofresp;
1423 /* Set the default preferred authentication type and mechanism */
1424 imapc->preftype = IMAP_TYPE_ANY;
1425 Curl_sasl_init(&imapc->sasl, &saslimap);
1427 /* Initialise the pingpong layer */
1430 /* Parse the URL options */
1431 result = imap_parse_url_options(conn);
1435 /* Start off waiting for the server greeting response */
1436 state(conn, IMAP_SERVERGREET);
1438 /* Start off with an response id of '*' */
1439 strcpy(imapc->resptag, "*");
1441 result = imap_multi_statemach(conn, done);
1446 /***********************************************************************
1450 * The DONE function. This does what needs to be done after a single DO has
1453 * Input argument is already checked for validity.
1455 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1458 CURLcode result = CURLE_OK;
1459 struct Curl_easy *data = conn->data;
1460 struct IMAP *imap = data->req.protop;
1468 connclose(conn, "IMAP done with bad status"); /* marked for closure */
1469 result = status; /* use the already set error code */
1471 else if(!data->set.connect_only && !imap->custom &&
1472 (imap->uid || data->set.upload)) {
1473 /* Handle responses after FETCH or APPEND transfer has finished */
1474 if(!data->set.upload)
1475 state(conn, IMAP_FETCH_FINAL);
1477 /* End the APPEND command first by sending an empty line */
1478 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1480 state(conn, IMAP_APPEND_FINAL);
1483 /* Run the state-machine
1485 TODO: when the multi interface is used, this _really_ should be using
1486 the imap_multi_statemach function but we have no general support for
1487 non-blocking DONE operations!
1490 result = imap_block_statemach(conn);
1493 /* Cleanup our per-request based variables */
1494 Curl_safefree(imap->mailbox);
1495 Curl_safefree(imap->uidvalidity);
1496 Curl_safefree(imap->uid);
1497 Curl_safefree(imap->section);
1498 Curl_safefree(imap->partial);
1499 Curl_safefree(imap->query);
1500 Curl_safefree(imap->custom);
1501 Curl_safefree(imap->custom_params);
1503 /* Clear the transfer mode for the next request */
1504 imap->transfer = FTPTRANSFER_BODY;
1509 /***********************************************************************
1513 * This is the actual DO function for IMAP. Fetch or append a message, or do
1514 * other things according to the options previously setup.
1516 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1519 /* This is IMAP and no proxy */
1520 CURLcode result = CURLE_OK;
1521 struct Curl_easy *data = conn->data;
1522 struct IMAP *imap = data->req.protop;
1523 struct imap_conn *imapc = &conn->proto.imapc;
1524 bool selected = FALSE;
1526 DEBUGF(infof(conn->data, "DO phase starts\n"));
1528 if(conn->data->set.opt_no_body) {
1529 /* Requested no body means no transfer */
1530 imap->transfer = FTPTRANSFER_INFO;
1533 *dophase_done = FALSE; /* not done yet */
1535 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1536 has already been selected on this connection */
1537 if(imap->mailbox && imapc->mailbox &&
1538 !strcmp(imap->mailbox, imapc->mailbox) &&
1539 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1540 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1543 /* Start the first command in the DO phase */
1544 if(conn->data->set.upload)
1545 /* APPEND can be executed directly */
1546 result = imap_perform_append(conn);
1547 else if(imap->custom && (selected || !imap->mailbox))
1548 /* Custom command using the same mailbox or no mailbox */
1549 result = imap_perform_list(conn);
1550 else if(!imap->custom && selected && imap->uid)
1551 /* FETCH from the same mailbox */
1552 result = imap_perform_fetch(conn);
1553 else if(!imap->custom && selected && imap->query)
1554 /* SEARCH the current mailbox */
1555 result = imap_perform_search(conn);
1556 else if(imap->mailbox && !selected &&
1557 (imap->custom || imap->uid || imap->query))
1558 /* SELECT the mailbox */
1559 result = imap_perform_select(conn);
1562 result = imap_perform_list(conn);
1567 /* Run the state-machine */
1568 result = imap_multi_statemach(conn, dophase_done);
1570 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1573 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1578 /***********************************************************************
1582 * This function is registered as 'curl_do' function. It decodes the path
1583 * parts etc as a wrapper to the actual DO function (imap_perform).
1585 * The input argument is already checked for validity.
1587 static CURLcode imap_do(struct connectdata *conn, bool *done)
1589 CURLcode result = CURLE_OK;
1591 *done = FALSE; /* default to false */
1593 /* Parse the URL path */
1594 result = imap_parse_url_path(conn);
1598 /* Parse the custom request */
1599 result = imap_parse_custom_request(conn);
1603 result = imap_regular_transfer(conn, done);
1608 /***********************************************************************
1612 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1613 * resources. BLOCKING.
1615 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1617 struct imap_conn *imapc = &conn->proto.imapc;
1619 /* We cannot send quit unconditionally. If this connection is stale or
1620 bad in any way, sending quit and waiting around here will make the
1621 disconnect wait in vain and cause more problems than we need to. */
1623 /* The IMAP session may or may not have been allocated/setup at this
1625 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
1626 if(!imap_perform_logout(conn))
1627 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1629 /* Disconnect from the server */
1630 Curl_pp_disconnect(&imapc->pp);
1632 /* Cleanup the SASL module */
1633 Curl_sasl_cleanup(conn, imapc->sasl.authused);
1635 /* Cleanup our connection based variables */
1636 Curl_safefree(imapc->mailbox);
1637 Curl_safefree(imapc->mailbox_uidvalidity);
1642 /* Call this when the DO phase has completed */
1643 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1645 struct IMAP *imap = conn->data->req.protop;
1649 if(imap->transfer != FTPTRANSFER_BODY)
1650 /* no data to transfer */
1651 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1656 /* Called from multi.c while DOing */
1657 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1659 CURLcode result = imap_multi_statemach(conn, dophase_done);
1662 DEBUGF(infof(conn->data, "DO phase failed\n"));
1663 else if(*dophase_done) {
1664 result = imap_dophase_done(conn, FALSE /* not connected */);
1666 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1672 /***********************************************************************
1674 * imap_regular_transfer()
1676 * The input argument is already checked for validity.
1678 * Performs all commands done before a regular transfer between a local and a
1681 static CURLcode imap_regular_transfer(struct connectdata *conn,
1684 CURLcode result = CURLE_OK;
1685 bool connected = FALSE;
1686 struct Curl_easy *data = conn->data;
1688 /* Make sure size is unknown at this point */
1689 data->req.size = -1;
1691 /* Set the progress data */
1692 Curl_pgrsSetUploadCounter(data, 0);
1693 Curl_pgrsSetDownloadCounter(data, 0);
1694 Curl_pgrsSetUploadSize(data, -1);
1695 Curl_pgrsSetDownloadSize(data, -1);
1697 /* Carry out the perform */
1698 result = imap_perform(conn, &connected, dophase_done);
1700 /* Perform post DO phase operations if necessary */
1701 if(!result && *dophase_done)
1702 result = imap_dophase_done(conn, connected);
1707 static CURLcode imap_setup_connection(struct connectdata *conn)
1709 struct Curl_easy *data = conn->data;
1711 /* Initialise the IMAP layer */
1712 CURLcode result = imap_init(conn);
1716 /* Clear the TLS upgraded flag */
1717 conn->tls_upgraded = FALSE;
1719 /* Set up the proxy if necessary */
1720 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1721 /* Unless we have asked to tunnel IMAP operations through the proxy, we
1722 switch and use HTTP operations only */
1723 #ifndef CURL_DISABLE_HTTP
1724 if(conn->handler == &Curl_handler_imap)
1725 conn->handler = &Curl_handler_imap_proxy;
1728 conn->handler = &Curl_handler_imaps_proxy;
1730 failf(data, "IMAPS not supported!");
1731 return CURLE_UNSUPPORTED_PROTOCOL;
1735 /* set it up as an HTTP connection instead */
1736 return conn->handler->setup_connection(conn);
1738 failf(data, "IMAP over http proxy requires HTTP support built-in!");
1739 return CURLE_UNSUPPORTED_PROTOCOL;
1743 data->state.path++; /* don't include the initial slash */
1748 /***********************************************************************
1752 * Sends the formated string as an IMAP command to the server.
1754 * Designed to never block.
1756 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
1758 CURLcode result = CURLE_OK;
1759 struct imap_conn *imapc = &conn->proto.imapc;
1765 /* Calculate the next command ID wrapping at 3 digits */
1766 imapc->cmdid = (imapc->cmdid + 1) % 1000;
1768 /* Calculate the tag based on the connection ID and command ID */
1769 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1770 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
1772 /* Prefix the format with the tag */
1773 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
1775 return CURLE_OUT_OF_MEMORY;
1777 /* Send the data with the tag */
1779 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
1787 /***********************************************************************
1791 * Checks the input string for characters that need escaping and returns an
1792 * atom ready for sending to the server.
1794 * The returned string needs to be freed.
1797 static char *imap_atom(const char *str, bool escape_only)
1799 /* !checksrc! disable PARENBRACE 1 */
1800 const char atom_specials[] = "(){ %*]";
1803 size_t backsp_count = 0;
1804 size_t quote_count = 0;
1805 bool others_exists = FALSE;
1807 char *newstr = NULL;
1812 /* Look for "atom-specials", counting the backslash and quote characters as
1813 these will need escapping */
1820 else if(!escape_only) {
1821 const char *p3 = atom_specials;
1823 while(*p3 && !others_exists) {
1825 others_exists = TRUE;
1834 /* Does the input contain any "atom-special" characters? */
1835 if(!backsp_count && !quote_count && !others_exists)
1838 /* Calculate the new string length */
1839 newlen = strlen(str) + backsp_count + quote_count + (others_exists ? 2 : 0);
1841 /* Allocate the new string */
1842 newstr = (char *) malloc((newlen + 1) * sizeof(char));
1846 /* Surround the string in quotes if necessary */
1850 newstr[newlen - 1] = '"';
1854 /* Copy the string, escaping backslash and quote characters along the way */
1857 if(*p1 == '\\' || *p1 == '"') {
1868 /* Terminate the string */
1869 newstr[newlen] = '\0';
1874 /***********************************************************************
1878 * Portable test of whether the specified char is a "bchar" as defined in the
1879 * grammar of RFC-5092.
1881 static bool imap_is_bchar(char ch)
1885 case ':': case '@': case '/':
1886 /* bchar -> achar */
1888 /* bchar -> achar -> uchar -> unreserved */
1889 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
1890 case '7': case '8': case '9':
1891 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
1892 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
1893 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
1894 case 'V': case 'W': case 'X': case 'Y': case 'Z':
1895 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
1896 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
1897 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
1898 case 'v': case 'w': case 'x': case 'y': case 'z':
1899 case '-': case '.': case '_': case '~':
1900 /* bchar -> achar -> uchar -> sub-delims-sh */
1901 case '!': case '$': case '\'': case '(': case ')': case '*':
1903 /* bchar -> achar -> uchar -> pct-encoded */
1904 case '%': /* HEXDIG chars are already included above */
1912 /***********************************************************************
1914 * imap_parse_url_options()
1916 * Parse the URL login options.
1918 static CURLcode imap_parse_url_options(struct connectdata *conn)
1920 CURLcode result = CURLE_OK;
1921 struct imap_conn *imapc = &conn->proto.imapc;
1922 const char *ptr = conn->options;
1924 imapc->sasl.resetprefs = TRUE;
1926 while(!result && ptr && *ptr) {
1927 const char *key = ptr;
1930 while(*ptr && *ptr != '=')
1935 while(*ptr && *ptr != ';')
1938 if(strncasecompare(key, "AUTH=", 5))
1939 result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
1940 value, ptr - value);
1942 result = CURLE_URL_MALFORMAT;
1948 switch(imapc->sasl.prefmech) {
1949 case SASL_AUTH_NONE:
1950 imapc->preftype = IMAP_TYPE_NONE;
1952 case SASL_AUTH_DEFAULT:
1953 imapc->preftype = IMAP_TYPE_ANY;
1956 imapc->preftype = IMAP_TYPE_SASL;
1963 /***********************************************************************
1965 * imap_parse_url_path()
1967 * Parse the URL path into separate path components.
1970 static CURLcode imap_parse_url_path(struct connectdata *conn)
1972 /* The imap struct is already initialised in imap_connect() */
1973 CURLcode result = CURLE_OK;
1974 struct Curl_easy *data = conn->data;
1975 struct IMAP *imap = data->req.protop;
1976 const char *begin = data->state.path;
1977 const char *ptr = begin;
1979 /* See how much of the URL is a valid path and decode it */
1980 while(imap_is_bchar(*ptr))
1984 /* Remove the trailing slash if present */
1985 const char *end = ptr;
1986 if(end > begin && end[-1] == '/')
1989 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
1995 imap->mailbox = NULL;
1997 /* There can be any number of parameters in the form ";NAME=VALUE" */
1998 while(*ptr == ';') {
2003 /* Find the length of the name parameter */
2005 while(*ptr && *ptr != '=')
2009 return CURLE_URL_MALFORMAT;
2011 /* Decode the name parameter */
2012 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2016 /* Find the length of the value parameter */
2018 while(imap_is_bchar(*ptr))
2021 /* Decode the value parameter */
2022 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2028 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2030 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2031 PARTIAL) stripping of the trailing slash character if it is present.
2033 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2034 if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
2035 if(valuelen > 0 && value[valuelen - 1] == '/')
2036 value[valuelen - 1] = '\0';
2038 imap->uidvalidity = value;
2041 else if(strcasecompare(name, "UID") && !imap->uid) {
2042 if(valuelen > 0 && value[valuelen - 1] == '/')
2043 value[valuelen - 1] = '\0';
2048 else if(strcasecompare(name, "SECTION") && !imap->section) {
2049 if(valuelen > 0 && value[valuelen - 1] == '/')
2050 value[valuelen - 1] = '\0';
2052 imap->section = value;
2055 else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
2056 if(valuelen > 0 && value[valuelen - 1] == '/')
2057 value[valuelen - 1] = '\0';
2059 imap->partial = value;
2066 return CURLE_URL_MALFORMAT;
2073 /* Does the URL contain a query parameter? Only valid when we have a mailbox
2074 and no UID as per RFC-5092 */
2075 if(imap->mailbox && !imap->uid && *ptr == '?') {
2076 /* Find the length of the query parameter */
2078 while(imap_is_bchar(*ptr))
2081 /* Decode the query parameter */
2082 result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2088 /* Any extra stuff at the end of the URL is an error */
2090 return CURLE_URL_MALFORMAT;
2095 /***********************************************************************
2097 * imap_parse_custom_request()
2099 * Parse the custom request.
2101 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2103 CURLcode result = CURLE_OK;
2104 struct Curl_easy *data = conn->data;
2105 struct IMAP *imap = data->req.protop;
2106 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2109 /* URL decode the custom request */
2110 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2112 /* Extract the parameters if specified */
2114 const char *params = imap->custom;
2116 while(*params && *params != ' ')
2120 imap->custom_params = strdup(params);
2121 imap->custom[params - imap->custom] = '\0';
2123 if(!imap->custom_params)
2124 result = CURLE_OUT_OF_MEMORY;
2132 #endif /* CURL_DISABLE_IMAP */