1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2018, 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 */
72 #include "strtoofft.h"
74 #include "vtls/vtls.h"
81 #include "curl_sasl.h"
84 /* The last 3 #include files should be in this order */
85 #include "curl_printf.h"
86 #include "curl_memory.h"
89 /* Local API functions */
90 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
91 static CURLcode imap_do(struct connectdata *conn, bool *done);
92 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
94 static CURLcode imap_connect(struct connectdata *conn, bool *done);
95 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
96 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
97 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
99 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode imap_setup_connection(struct connectdata *conn);
101 static char *imap_atom(const char *str, bool escape_only);
102 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
103 static CURLcode imap_parse_url_options(struct connectdata *conn);
104 static CURLcode imap_parse_url_path(struct connectdata *conn);
105 static CURLcode imap_parse_custom_request(struct connectdata *conn);
106 static CURLcode imap_perform_authenticate(struct connectdata *conn,
108 const char *initresp);
109 static CURLcode imap_continue_authenticate(struct connectdata *conn,
111 static void imap_get_message(char *buffer, char **outptr);
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 ZERO_NULL, /* connection_check */
133 PORT_IMAP, /* defport */
134 CURLPROTO_IMAP, /* protocol */
135 PROTOPT_CLOSEACTION| /* flags */
141 * IMAPS protocol handler.
144 const struct Curl_handler Curl_handler_imaps = {
145 "IMAPS", /* scheme */
146 imap_setup_connection, /* setup_connection */
148 imap_done, /* done */
149 ZERO_NULL, /* do_more */
150 imap_connect, /* connect_it */
151 imap_multi_statemach, /* connecting */
152 imap_doing, /* doing */
153 imap_getsock, /* proto_getsock */
154 imap_getsock, /* doing_getsock */
155 ZERO_NULL, /* domore_getsock */
156 ZERO_NULL, /* perform_getsock */
157 imap_disconnect, /* disconnect */
158 ZERO_NULL, /* readwrite */
159 ZERO_NULL, /* connection_check */
160 PORT_IMAPS, /* defport */
161 CURLPROTO_IMAPS, /* protocol */
162 PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */
166 #define IMAP_RESP_OK 1
167 #define IMAP_RESP_NOT_OK 2
168 #define IMAP_RESP_PREAUTH 3
170 /* SASL parameters for the imap protocol */
171 static const struct SASLproto saslimap = {
172 "imap", /* The service name */
173 '+', /* Code received when continuation is expected */
174 IMAP_RESP_OK, /* Code to receive upon authentication success */
175 0, /* Maximum initial response length (no max) */
176 imap_perform_authenticate, /* Send authentication command */
177 imap_continue_authenticate, /* Send authentication continuation */
178 imap_get_message /* Get SASL response message */
183 static void imap_to_imaps(struct connectdata *conn)
185 /* Change the connection handler */
186 conn->handler = &Curl_handler_imaps;
188 /* Set the connection's upgraded to TLS flag */
189 conn->tls_upgraded = TRUE;
192 #define imap_to_imaps(x) Curl_nop_stmt
195 /***********************************************************************
199 * Determines whether the untagged response is related to the specified
200 * command by checking if it is in format "* <command-name> ..." or
201 * "* <number> <command-name> ...".
203 * The "* " marker is assumed to have already been checked by the caller.
205 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
207 const char *end = line + len;
208 size_t cmd_len = strlen(cmd);
210 /* Skip the untagged response marker */
213 /* Do we have a number after the marker? */
214 if(line < end && ISDIGIT(*line)) {
215 /* Skip the number */
218 while(line < end && ISDIGIT(*line));
220 /* Do we have the space character? */
221 if(line == end || *line != ' ')
227 /* Does the command name match and is it followed by a space character or at
229 if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
230 (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
236 /***********************************************************************
240 * Checks whether the given string is a valid tagged, untagged or continuation
241 * response which can be processed by the response handler.
243 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
246 struct IMAP *imap = conn->data->req.protop;
247 struct imap_conn *imapc = &conn->proto.imapc;
248 const char *id = imapc->resptag;
249 size_t id_len = strlen(id);
251 /* Do we have a tagged command response? */
252 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
256 if(len >= 2 && !memcmp(line, "OK", 2))
257 *resp = IMAP_RESP_OK;
258 else if(len >= 7 && !memcmp(line, "PREAUTH", 7))
259 *resp = IMAP_RESP_PREAUTH;
261 *resp = IMAP_RESP_NOT_OK;
266 /* Do we have an untagged command response? */
267 if(len >= 2 && !memcmp("* ", line, 2)) {
268 switch(imapc->state) {
269 /* States which are interested in untagged responses */
270 case IMAP_CAPABILITY:
271 if(!imap_matchresp(line, len, "CAPABILITY"))
276 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
277 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
278 (!strcasecompare(imap->custom, "STORE") ||
279 !imap_matchresp(line, len, "FETCH")) &&
280 !strcasecompare(imap->custom, "SELECT") &&
281 !strcasecompare(imap->custom, "EXAMINE") &&
282 !strcasecompare(imap->custom, "SEARCH") &&
283 !strcasecompare(imap->custom, "EXPUNGE") &&
284 !strcasecompare(imap->custom, "LSUB") &&
285 !strcasecompare(imap->custom, "UID") &&
286 !strcasecompare(imap->custom, "NOOP")))
291 /* SELECT is special in that its untagged responses do not have a
292 common prefix so accept anything! */
296 if(!imap_matchresp(line, len, "FETCH"))
301 if(!imap_matchresp(line, len, "SEARCH"))
305 /* Ignore other untagged responses */
314 /* Do we have a continuation response? This should be a + symbol followed by
315 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
316 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
317 some e-mail servers ignore this and only send a single + instead. */
318 if(imap && !imap->custom && ((len == 3 && !memcmp("+", line, 1)) ||
319 (len >= 2 && !memcmp("+ ", line, 2)))) {
320 switch(imapc->state) {
321 /* States which are interested in continuation responses */
322 case IMAP_AUTHENTICATE:
328 failf(conn->data, "Unexpected continuation response");
336 return FALSE; /* Nothing for us */
339 /***********************************************************************
343 * Gets the authentication message from the response buffer.
345 static void imap_get_message(char *buffer, char **outptr)
347 size_t len = strlen(buffer);
348 char *message = NULL;
351 /* Find the start of the message */
353 for(message = buffer + 2; *message == ' ' || *message == '\t';
357 /* Find the end of the message */
359 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
360 message[len] != '\t')
363 /* Terminate the message */
369 /* junk input => zero length output */
370 message = &buffer[len];
375 /***********************************************************************
379 * This is the ONLY way to change IMAP state!
381 static void state(struct connectdata *conn, imapstate newstate)
383 struct imap_conn *imapc = &conn->proto.imapc;
384 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
385 /* for debug purposes */
386 static const char * const names[]={
405 if(imapc->state != newstate)
406 infof(conn->data, "IMAP %p state change from %s to %s\n",
407 (void *)imapc, names[imapc->state], names[newstate]);
410 imapc->state = newstate;
413 /***********************************************************************
415 * imap_perform_capability()
417 * Sends the CAPABILITY command in order to obtain a list of server side
418 * supported capabilities.
420 static CURLcode imap_perform_capability(struct connectdata *conn)
422 CURLcode result = CURLE_OK;
423 struct imap_conn *imapc = &conn->proto.imapc;
425 imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
426 imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
427 imapc->tls_supported = FALSE; /* Clear the TLS capability */
429 /* Send the CAPABILITY command */
430 result = imap_sendf(conn, "CAPABILITY");
433 state(conn, IMAP_CAPABILITY);
438 /***********************************************************************
440 * imap_perform_starttls()
442 * Sends the STARTTLS command to start the upgrade to TLS.
444 static CURLcode imap_perform_starttls(struct connectdata *conn)
446 CURLcode result = CURLE_OK;
448 /* Send the STARTTLS command */
449 result = imap_sendf(conn, "STARTTLS");
452 state(conn, IMAP_STARTTLS);
457 /***********************************************************************
459 * imap_perform_upgrade_tls()
461 * Performs the upgrade to TLS.
463 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
465 CURLcode result = CURLE_OK;
466 struct imap_conn *imapc = &conn->proto.imapc;
468 /* Start the SSL connection */
469 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
472 if(imapc->state != IMAP_UPGRADETLS)
473 state(conn, IMAP_UPGRADETLS);
477 result = imap_perform_capability(conn);
484 /***********************************************************************
486 * imap_perform_login()
488 * Sends a clear text LOGIN command to authenticate with.
490 static CURLcode imap_perform_login(struct connectdata *conn)
492 CURLcode result = CURLE_OK;
496 /* Check we have a username and password to authenticate with and end the
497 connect phase if we don't */
498 if(!conn->bits.user_passwd) {
499 state(conn, IMAP_STOP);
504 /* Make sure the username and password are in the correct atom format */
505 user = imap_atom(conn->user, false);
506 passwd = imap_atom(conn->passwd, false);
508 /* Send the LOGIN command */
509 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
510 passwd ? passwd : "");
516 state(conn, IMAP_LOGIN);
521 /***********************************************************************
523 * imap_perform_authenticate()
525 * Sends an AUTHENTICATE command allowing the client to login with the given
526 * SASL authentication mechanism.
528 static CURLcode imap_perform_authenticate(struct connectdata *conn,
530 const char *initresp)
532 CURLcode result = CURLE_OK;
535 /* Send the AUTHENTICATE command with the initial response */
536 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
539 /* Send the AUTHENTICATE command */
540 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
546 /***********************************************************************
548 * imap_continue_authenticate()
550 * Sends SASL continuation data or cancellation.
552 static CURLcode imap_continue_authenticate(struct connectdata *conn,
555 struct imap_conn *imapc = &conn->proto.imapc;
557 return Curl_pp_sendf(&imapc->pp, "%s", resp);
560 /***********************************************************************
562 * imap_perform_authentication()
564 * Initiates the authentication sequence, with the appropriate SASL
565 * authentication mechanism, falling back to clear text should a common
566 * mechanism not be available between the client and server.
568 static CURLcode imap_perform_authentication(struct connectdata *conn)
570 CURLcode result = CURLE_OK;
571 struct imap_conn *imapc = &conn->proto.imapc;
572 saslprogress progress;
574 /* Check if already authenticated OR if there is enough data to authenticate
575 with and end the connect phase if we don't */
577 !Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
578 state(conn, IMAP_STOP);
582 /* Calculate the SASL login details */
583 result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
586 if(progress == SASL_INPROGRESS)
587 state(conn, IMAP_AUTHENTICATE);
588 else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
589 /* Perform clear text authentication */
590 result = imap_perform_login(conn);
592 /* Other mechanisms not supported */
593 infof(conn->data, "No known authentication mechanisms supported!\n");
594 result = CURLE_LOGIN_DENIED;
601 /***********************************************************************
603 * imap_perform_list()
605 * Sends a LIST command or an alternative custom request.
607 static CURLcode imap_perform_list(struct connectdata *conn)
609 CURLcode result = CURLE_OK;
610 struct Curl_easy *data = conn->data;
611 struct IMAP *imap = data->req.protop;
615 /* Send the custom request */
616 result = imap_sendf(conn, "%s%s", imap->custom,
617 imap->custom_params ? imap->custom_params : "");
619 /* Make sure the mailbox is in the correct atom format if necessary */
620 mailbox = imap->mailbox ? imap_atom(imap->mailbox, true) : strdup("");
622 return CURLE_OUT_OF_MEMORY;
624 /* Send the LIST command */
625 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
631 state(conn, IMAP_LIST);
636 /***********************************************************************
638 * imap_perform_select()
640 * Sends a SELECT command to ask the server to change the selected mailbox.
642 static CURLcode imap_perform_select(struct connectdata *conn)
644 CURLcode result = CURLE_OK;
645 struct Curl_easy *data = conn->data;
646 struct IMAP *imap = data->req.protop;
647 struct imap_conn *imapc = &conn->proto.imapc;
650 /* Invalidate old information as we are switching mailboxes */
651 Curl_safefree(imapc->mailbox);
652 Curl_safefree(imapc->mailbox_uidvalidity);
654 /* Check we have a mailbox */
656 failf(conn->data, "Cannot SELECT without a mailbox.");
657 return CURLE_URL_MALFORMAT;
660 /* Make sure the mailbox is in the correct atom format */
661 mailbox = imap_atom(imap->mailbox, false);
663 return CURLE_OUT_OF_MEMORY;
665 /* Send the SELECT command */
666 result = imap_sendf(conn, "SELECT %s", mailbox);
671 state(conn, IMAP_SELECT);
676 /***********************************************************************
678 * imap_perform_fetch()
680 * Sends a FETCH command to initiate the download of a message.
682 static CURLcode imap_perform_fetch(struct connectdata *conn)
684 CURLcode result = CURLE_OK;
685 struct IMAP *imap = conn->data->req.protop;
687 /* Check we have a UID */
689 failf(conn->data, "Cannot FETCH without a UID.");
690 return CURLE_URL_MALFORMAT;
693 /* Send the FETCH command */
695 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
697 imap->section ? imap->section : "",
700 result = imap_sendf(conn, "FETCH %s BODY[%s]",
702 imap->section ? imap->section : "");
705 state(conn, IMAP_FETCH);
710 /***********************************************************************
712 * imap_perform_append()
714 * Sends an APPEND command to initiate the upload of a message.
716 static CURLcode imap_perform_append(struct connectdata *conn)
718 CURLcode result = CURLE_OK;
719 struct Curl_easy *data = conn->data;
720 struct IMAP *imap = data->req.protop;
723 /* Check we have a mailbox */
725 failf(data, "Cannot APPEND without a mailbox.");
726 return CURLE_URL_MALFORMAT;
729 /* Prepare the mime data if some. */
730 if(data->set.mimepost.kind != MIMEKIND_NONE) {
731 /* Use the whole structure as data. */
732 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
734 /* Add external headers and mime version. */
735 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
736 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
737 NULL, MIMESTRATEGY_MAIL);
740 if(!Curl_checkheaders(conn, "Mime-Version"))
741 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
742 "Mime-Version: 1.0");
744 /* Make sure we will read the entire mime structure. */
746 result = Curl_mime_rewind(&data->set.mimepost);
751 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
753 /* Read from mime structure. */
754 data->state.fread_func = (curl_read_callback) Curl_mime_read;
755 data->state.in = (void *) &data->set.mimepost;
758 /* Check we know the size of the upload */
759 if(data->state.infilesize < 0) {
760 failf(data, "Cannot APPEND with unknown input file size\n");
761 return CURLE_UPLOAD_FAILED;
764 /* Make sure the mailbox is in the correct atom format */
765 mailbox = imap_atom(imap->mailbox, false);
767 return CURLE_OUT_OF_MEMORY;
769 /* Send the APPEND command */
770 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
771 mailbox, data->state.infilesize);
776 state(conn, IMAP_APPEND);
781 /***********************************************************************
783 * imap_perform_search()
785 * Sends a SEARCH command.
787 static CURLcode imap_perform_search(struct connectdata *conn)
789 CURLcode result = CURLE_OK;
790 struct IMAP *imap = conn->data->req.protop;
792 /* Check we have a query string */
794 failf(conn->data, "Cannot SEARCH without a query string.");
795 return CURLE_URL_MALFORMAT;
798 /* Send the SEARCH command */
799 result = imap_sendf(conn, "SEARCH %s", imap->query);
802 state(conn, IMAP_SEARCH);
807 /***********************************************************************
809 * imap_perform_logout()
811 * Performs the logout action prior to sclose() being called.
813 static CURLcode imap_perform_logout(struct connectdata *conn)
815 CURLcode result = CURLE_OK;
817 /* Send the LOGOUT command */
818 result = imap_sendf(conn, "LOGOUT");
821 state(conn, IMAP_LOGOUT);
826 /* For the initial server greeting */
827 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
831 struct Curl_easy *data = conn->data;
832 (void)instate; /* no use for this yet */
834 if(imapcode == IMAP_RESP_PREAUTH) {
836 struct imap_conn *imapc = &conn->proto.imapc;
837 imapc->preauth = TRUE;
838 infof(data, "PREAUTH connection, already authenticated!\n");
840 else if(imapcode != IMAP_RESP_OK) {
841 failf(data, "Got unexpected imap-server response");
842 return CURLE_WEIRD_SERVER_REPLY;
845 return imap_perform_capability(conn);
848 /* For CAPABILITY responses */
849 static CURLcode imap_state_capability_resp(struct connectdata *conn,
853 CURLcode result = CURLE_OK;
854 struct Curl_easy *data = conn->data;
855 struct imap_conn *imapc = &conn->proto.imapc;
856 const char *line = data->state.buffer;
859 (void)instate; /* no use for this yet */
861 /* Do we have a untagged response? */
862 if(imapcode == '*') {
865 /* Loop through the data line */
868 (*line == ' ' || *line == '\t' ||
869 *line == '\r' || *line == '\n')) {
877 /* Extract the word */
878 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
879 line[wordlen] != '\t' && line[wordlen] != '\r' &&
880 line[wordlen] != '\n';)
883 /* Does the server support the STARTTLS capability? */
884 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
885 imapc->tls_supported = TRUE;
887 /* Has the server explicitly disabled clear text authentication? */
888 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
889 imapc->login_disabled = TRUE;
891 /* Does the server support the SASL-IR capability? */
892 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
893 imapc->ir_supported = TRUE;
895 /* Do we have a SASL based authentication mechanism? */
896 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
898 unsigned int mechbit;
903 /* Test the word for a matching authentication mechanism */
904 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
905 if(mechbit && llen == wordlen)
906 imapc->sasl.authmechs |= mechbit;
912 else if(imapcode == IMAP_RESP_OK) {
913 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
914 /* We don't have a SSL/TLS connection yet, but SSL is requested */
915 if(imapc->tls_supported)
916 /* Switch to TLS connection now */
917 result = imap_perform_starttls(conn);
918 else if(data->set.use_ssl == CURLUSESSL_TRY)
919 /* Fallback and carry on with authentication */
920 result = imap_perform_authentication(conn);
922 failf(data, "STARTTLS not supported.");
923 result = CURLE_USE_SSL_FAILED;
927 result = imap_perform_authentication(conn);
930 result = imap_perform_authentication(conn);
935 /* For STARTTLS responses */
936 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
940 CURLcode result = CURLE_OK;
941 struct Curl_easy *data = conn->data;
943 (void)instate; /* no use for this yet */
945 if(imapcode != IMAP_RESP_OK) {
946 if(data->set.use_ssl != CURLUSESSL_TRY) {
947 failf(data, "STARTTLS denied");
948 result = CURLE_USE_SSL_FAILED;
951 result = imap_perform_authentication(conn);
954 result = imap_perform_upgrade_tls(conn);
959 /* For SASL authentication responses */
960 static CURLcode imap_state_auth_resp(struct connectdata *conn,
964 CURLcode result = CURLE_OK;
965 struct Curl_easy *data = conn->data;
966 struct imap_conn *imapc = &conn->proto.imapc;
967 saslprogress progress;
969 (void)instate; /* no use for this yet */
971 result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
975 state(conn, IMAP_STOP); /* Authenticated */
977 case SASL_IDLE: /* No mechanism left after cancellation */
978 if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
979 /* Perform clear text authentication */
980 result = imap_perform_login(conn);
982 failf(data, "Authentication cancelled");
983 result = CURLE_LOGIN_DENIED;
993 /* For LOGIN responses */
994 static CURLcode imap_state_login_resp(struct connectdata *conn,
998 CURLcode result = CURLE_OK;
999 struct Curl_easy *data = conn->data;
1001 (void)instate; /* no use for this yet */
1003 if(imapcode != IMAP_RESP_OK) {
1004 failf(data, "Access denied. %c", imapcode);
1005 result = CURLE_LOGIN_DENIED;
1008 /* End of connect phase */
1009 state(conn, IMAP_STOP);
1014 /* For LIST and SEARCH responses */
1015 static CURLcode imap_state_listsearch_resp(struct connectdata *conn,
1019 CURLcode result = CURLE_OK;
1020 char *line = conn->data->state.buffer;
1021 size_t len = strlen(line);
1023 (void)instate; /* No use for this yet */
1025 if(imapcode == '*') {
1026 /* Temporarily add the LF character back and send as body to the client */
1028 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1031 else if(imapcode != IMAP_RESP_OK)
1032 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1034 /* End of DO phase */
1035 state(conn, IMAP_STOP);
1040 /* For SELECT responses */
1041 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1044 CURLcode result = CURLE_OK;
1045 struct Curl_easy *data = conn->data;
1046 struct IMAP *imap = conn->data->req.protop;
1047 struct imap_conn *imapc = &conn->proto.imapc;
1048 const char *line = data->state.buffer;
1051 (void)instate; /* no use for this yet */
1053 if(imapcode == '*') {
1054 /* See if this is an UIDVALIDITY response */
1055 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1056 Curl_safefree(imapc->mailbox_uidvalidity);
1057 imapc->mailbox_uidvalidity = strdup(tmp);
1060 else if(imapcode == IMAP_RESP_OK) {
1061 /* Check if the UIDVALIDITY has been specified and matches */
1062 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1063 !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1064 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1065 result = CURLE_REMOTE_FILE_NOT_FOUND;
1068 /* Note the currently opened mailbox on this connection */
1069 imapc->mailbox = strdup(imap->mailbox);
1072 result = imap_perform_list(conn);
1073 else if(imap->query)
1074 result = imap_perform_search(conn);
1076 result = imap_perform_fetch(conn);
1080 failf(data, "Select failed");
1081 result = CURLE_LOGIN_DENIED;
1087 /* For the (first line of the) FETCH responses */
1088 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1091 CURLcode result = CURLE_OK;
1092 struct Curl_easy *data = conn->data;
1093 struct imap_conn *imapc = &conn->proto.imapc;
1094 struct pingpong *pp = &imapc->pp;
1095 const char *ptr = data->state.buffer;
1096 bool parsed = FALSE;
1097 curl_off_t size = 0;
1099 (void)instate; /* no use for this yet */
1101 if(imapcode != '*') {
1102 Curl_pgrsSetDownloadSize(data, -1);
1103 state(conn, IMAP_STOP);
1104 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1107 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1108 the continuation data contained within the curly brackets */
1109 while(*ptr && (*ptr != '{'))
1114 if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
1115 if(endptr - ptr > 1 && endptr[0] == '}' &&
1116 endptr[1] == '\r' && endptr[2] == '\0')
1122 infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1124 Curl_pgrsSetDownloadSize(data, size);
1127 /* At this point there is a bunch of data in the header "cache" that is
1128 actually body content, send it as body and then skip it. Do note
1129 that there may even be additional "headers" after the body. */
1130 size_t chunk = pp->cache_size;
1132 if(chunk > (size_t)size)
1133 /* The conversion from curl_off_t to size_t is always fine here */
1134 chunk = (size_t)size;
1137 /* no size, we're done with the data */
1138 state(conn, IMAP_STOP);
1141 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1145 data->req.bytecount += chunk;
1147 infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1148 " bytes, %" CURL_FORMAT_CURL_OFF_TU
1149 " bytes are left for transfer\n", (curl_off_t)chunk,
1152 /* Have we used the entire cache or just part of it?*/
1153 if(pp->cache_size > chunk) {
1154 /* Only part of it so shrink the cache to fit the trailing data */
1155 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1156 pp->cache_size -= chunk;
1159 /* Free the cache */
1160 Curl_safefree(pp->cache);
1162 /* Reset the cache size */
1167 if(data->req.bytecount == size)
1168 /* The entire data is already transferred! */
1169 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1172 data->req.maxdownload = size;
1173 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1177 /* We don't know how to parse this line */
1178 failf(pp->conn->data, "Failed to parse FETCH response.");
1179 result = CURLE_WEIRD_SERVER_REPLY;
1182 /* End of DO phase */
1183 state(conn, IMAP_STOP);
1188 /* For final FETCH responses performed after the download */
1189 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1193 CURLcode result = CURLE_OK;
1195 (void)instate; /* No use for this yet */
1197 if(imapcode != IMAP_RESP_OK)
1198 result = CURLE_WEIRD_SERVER_REPLY;
1200 /* End of DONE phase */
1201 state(conn, IMAP_STOP);
1206 /* For APPEND responses */
1207 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1210 CURLcode result = CURLE_OK;
1211 struct Curl_easy *data = conn->data;
1213 (void)instate; /* No use for this yet */
1215 if(imapcode != '+') {
1216 result = CURLE_UPLOAD_FAILED;
1219 /* Set the progress upload size */
1220 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1223 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1225 /* End of DO phase */
1226 state(conn, IMAP_STOP);
1232 /* For final APPEND responses performed after the upload */
1233 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1237 CURLcode result = CURLE_OK;
1239 (void)instate; /* No use for this yet */
1241 if(imapcode != IMAP_RESP_OK)
1242 result = CURLE_UPLOAD_FAILED;
1244 /* End of DONE phase */
1245 state(conn, IMAP_STOP);
1250 static CURLcode imap_statemach_act(struct connectdata *conn)
1252 CURLcode result = CURLE_OK;
1253 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1255 struct imap_conn *imapc = &conn->proto.imapc;
1256 struct pingpong *pp = &imapc->pp;
1259 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1260 if(imapc->state == IMAP_UPGRADETLS)
1261 return imap_perform_upgrade_tls(conn);
1263 /* Flush any data that needs to be sent */
1265 return Curl_pp_flushsend(pp);
1268 /* Read the response from the server */
1269 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1273 /* Was there an error parsing the response line? */
1275 return CURLE_WEIRD_SERVER_REPLY;
1280 /* We have now received a full IMAP server response */
1281 switch(imapc->state) {
1282 case IMAP_SERVERGREET:
1283 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1286 case IMAP_CAPABILITY:
1287 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1291 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1294 case IMAP_AUTHENTICATE:
1295 result = imap_state_auth_resp(conn, imapcode, imapc->state);
1299 result = imap_state_login_resp(conn, imapcode, imapc->state);
1303 result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
1307 result = imap_state_select_resp(conn, imapcode, imapc->state);
1311 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1314 case IMAP_FETCH_FINAL:
1315 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1319 result = imap_state_append_resp(conn, imapcode, imapc->state);
1322 case IMAP_APPEND_FINAL:
1323 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1327 result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
1331 /* fallthrough, just stop! */
1333 /* internal error */
1334 state(conn, IMAP_STOP);
1337 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1342 /* Called repeatedly until done from multi.c */
1343 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1345 CURLcode result = CURLE_OK;
1346 struct imap_conn *imapc = &conn->proto.imapc;
1348 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1349 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1350 if(result || !imapc->ssldone)
1354 result = Curl_pp_statemach(&imapc->pp, FALSE);
1355 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1360 static CURLcode imap_block_statemach(struct connectdata *conn)
1362 CURLcode result = CURLE_OK;
1363 struct imap_conn *imapc = &conn->proto.imapc;
1365 while(imapc->state != IMAP_STOP && !result)
1366 result = Curl_pp_statemach(&imapc->pp, TRUE);
1371 /* Allocate and initialize the struct IMAP for the current Curl_easy if
1373 static CURLcode imap_init(struct connectdata *conn)
1375 CURLcode result = CURLE_OK;
1376 struct Curl_easy *data = conn->data;
1379 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1381 result = CURLE_OUT_OF_MEMORY;
1386 /* For the IMAP "protocol connect" and "doing" phases only */
1387 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1390 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1393 /***********************************************************************
1397 * This function should do everything that is to be considered a part of the
1400 * The variable 'done' points to will be TRUE if the protocol-layer connect
1401 * phase is done when this function returns, or FALSE if not.
1403 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1405 CURLcode result = CURLE_OK;
1406 struct imap_conn *imapc = &conn->proto.imapc;
1407 struct pingpong *pp = &imapc->pp;
1409 *done = FALSE; /* default to not done yet */
1411 /* We always support persistent connections in IMAP */
1412 connkeep(conn, "IMAP default");
1414 /* Set the default response time-out */
1415 pp->response_time = RESP_TIMEOUT;
1416 pp->statemach_act = imap_statemach_act;
1417 pp->endofresp = imap_endofresp;
1420 /* Set the default preferred authentication type and mechanism */
1421 imapc->preftype = IMAP_TYPE_ANY;
1422 Curl_sasl_init(&imapc->sasl, &saslimap);
1424 /* Initialise the pingpong layer */
1427 /* Parse the URL options */
1428 result = imap_parse_url_options(conn);
1432 /* Start off waiting for the server greeting response */
1433 state(conn, IMAP_SERVERGREET);
1435 /* Start off with an response id of '*' */
1436 strcpy(imapc->resptag, "*");
1438 result = imap_multi_statemach(conn, done);
1443 /***********************************************************************
1447 * The DONE function. This does what needs to be done after a single DO has
1450 * Input argument is already checked for validity.
1452 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1455 CURLcode result = CURLE_OK;
1456 struct Curl_easy *data = conn->data;
1457 struct IMAP *imap = data->req.protop;
1465 connclose(conn, "IMAP done with bad status"); /* marked for closure */
1466 result = status; /* use the already set error code */
1468 else if(!data->set.connect_only && !imap->custom &&
1469 (imap->uid || data->set.upload ||
1470 data->set.mimepost.kind != MIMEKIND_NONE)) {
1471 /* Handle responses after FETCH or APPEND transfer has finished */
1472 if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
1473 state(conn, IMAP_FETCH_FINAL);
1475 /* End the APPEND command first by sending an empty line */
1476 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1478 state(conn, IMAP_APPEND_FINAL);
1481 /* Run the state-machine
1483 TODO: when the multi interface is used, this _really_ should be using
1484 the imap_multi_statemach function but we have no general support for
1485 non-blocking DONE operations!
1488 result = imap_block_statemach(conn);
1491 /* Cleanup our per-request based variables */
1492 Curl_safefree(imap->mailbox);
1493 Curl_safefree(imap->uidvalidity);
1494 Curl_safefree(imap->uid);
1495 Curl_safefree(imap->section);
1496 Curl_safefree(imap->partial);
1497 Curl_safefree(imap->query);
1498 Curl_safefree(imap->custom);
1499 Curl_safefree(imap->custom_params);
1501 /* Clear the transfer mode for the next request */
1502 imap->transfer = FTPTRANSFER_BODY;
1507 /***********************************************************************
1511 * This is the actual DO function for IMAP. Fetch or append a message, or do
1512 * other things according to the options previously setup.
1514 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1517 /* This is IMAP and no proxy */
1518 CURLcode result = CURLE_OK;
1519 struct Curl_easy *data = conn->data;
1520 struct IMAP *imap = data->req.protop;
1521 struct imap_conn *imapc = &conn->proto.imapc;
1522 bool selected = FALSE;
1524 DEBUGF(infof(conn->data, "DO phase starts\n"));
1526 if(conn->data->set.opt_no_body) {
1527 /* Requested no body means no transfer */
1528 imap->transfer = FTPTRANSFER_INFO;
1531 *dophase_done = FALSE; /* not done yet */
1533 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1534 has already been selected on this connection */
1535 if(imap->mailbox && imapc->mailbox &&
1536 strcasecompare(imap->mailbox, imapc->mailbox) &&
1537 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1538 strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1541 /* Start the first command in the DO phase */
1542 if(conn->data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
1543 /* APPEND can be executed directly */
1544 result = imap_perform_append(conn);
1545 else if(imap->custom && (selected || !imap->mailbox))
1546 /* Custom command using the same mailbox or no mailbox */
1547 result = imap_perform_list(conn);
1548 else if(!imap->custom && selected && imap->uid)
1549 /* FETCH from the same mailbox */
1550 result = imap_perform_fetch(conn);
1551 else if(!imap->custom && selected && imap->query)
1552 /* SEARCH the current mailbox */
1553 result = imap_perform_search(conn);
1554 else if(imap->mailbox && !selected &&
1555 (imap->custom || imap->uid || imap->query))
1556 /* SELECT the mailbox */
1557 result = imap_perform_select(conn);
1560 result = imap_perform_list(conn);
1565 /* Run the state-machine */
1566 result = imap_multi_statemach(conn, dophase_done);
1568 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1571 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1576 /***********************************************************************
1580 * This function is registered as 'curl_do' function. It decodes the path
1581 * parts etc as a wrapper to the actual DO function (imap_perform).
1583 * The input argument is already checked for validity.
1585 static CURLcode imap_do(struct connectdata *conn, bool *done)
1587 CURLcode result = CURLE_OK;
1589 *done = FALSE; /* default to false */
1591 /* Parse the URL path */
1592 result = imap_parse_url_path(conn);
1596 /* Parse the custom request */
1597 result = imap_parse_custom_request(conn);
1601 result = imap_regular_transfer(conn, done);
1606 /***********************************************************************
1610 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1611 * resources. BLOCKING.
1613 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1615 struct imap_conn *imapc = &conn->proto.imapc;
1617 /* We cannot send quit unconditionally. If this connection is stale or
1618 bad in any way, sending quit and waiting around here will make the
1619 disconnect wait in vain and cause more problems than we need to. */
1621 /* The IMAP session may or may not have been allocated/setup at this
1623 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
1624 if(!imap_perform_logout(conn))
1625 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1627 /* Disconnect from the server */
1628 Curl_pp_disconnect(&imapc->pp);
1630 /* Cleanup the SASL module */
1631 Curl_sasl_cleanup(conn, imapc->sasl.authused);
1633 /* Cleanup our connection based variables */
1634 Curl_safefree(imapc->mailbox);
1635 Curl_safefree(imapc->mailbox_uidvalidity);
1640 /* Call this when the DO phase has completed */
1641 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1643 struct IMAP *imap = conn->data->req.protop;
1647 if(imap->transfer != FTPTRANSFER_BODY)
1648 /* no data to transfer */
1649 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1654 /* Called from multi.c while DOing */
1655 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1657 CURLcode result = imap_multi_statemach(conn, dophase_done);
1660 DEBUGF(infof(conn->data, "DO phase failed\n"));
1661 else if(*dophase_done) {
1662 result = imap_dophase_done(conn, FALSE /* not connected */);
1664 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1670 /***********************************************************************
1672 * imap_regular_transfer()
1674 * The input argument is already checked for validity.
1676 * Performs all commands done before a regular transfer between a local and a
1679 static CURLcode imap_regular_transfer(struct connectdata *conn,
1682 CURLcode result = CURLE_OK;
1683 bool connected = FALSE;
1684 struct Curl_easy *data = conn->data;
1686 /* Make sure size is unknown at this point */
1687 data->req.size = -1;
1689 /* Set the progress data */
1690 Curl_pgrsSetUploadCounter(data, 0);
1691 Curl_pgrsSetDownloadCounter(data, 0);
1692 Curl_pgrsSetUploadSize(data, -1);
1693 Curl_pgrsSetDownloadSize(data, -1);
1695 /* Carry out the perform */
1696 result = imap_perform(conn, &connected, dophase_done);
1698 /* Perform post DO phase operations if necessary */
1699 if(!result && *dophase_done)
1700 result = imap_dophase_done(conn, connected);
1705 static CURLcode imap_setup_connection(struct connectdata *conn)
1707 struct Curl_easy *data = conn->data;
1709 /* Initialise the IMAP layer */
1710 CURLcode result = imap_init(conn);
1714 /* Clear the TLS upgraded flag */
1715 conn->tls_upgraded = FALSE;
1716 data->state.path++; /* don't include the initial slash */
1721 /***********************************************************************
1725 * Sends the formatted string as an IMAP command to the server.
1727 * Designed to never block.
1729 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
1731 CURLcode result = CURLE_OK;
1732 struct imap_conn *imapc = &conn->proto.imapc;
1738 /* Calculate the next command ID wrapping at 3 digits */
1739 imapc->cmdid = (imapc->cmdid + 1) % 1000;
1741 /* Calculate the tag based on the connection ID and command ID */
1742 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1743 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
1745 /* Prefix the format with the tag */
1746 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
1748 return CURLE_OUT_OF_MEMORY;
1750 /* Send the data with the tag */
1752 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
1760 /***********************************************************************
1764 * Checks the input string for characters that need escaping and returns an
1765 * atom ready for sending to the server.
1767 * The returned string needs to be freed.
1770 static char *imap_atom(const char *str, bool escape_only)
1772 /* !checksrc! disable PARENBRACE 1 */
1773 const char atom_specials[] = "(){ %*]";
1776 size_t backsp_count = 0;
1777 size_t quote_count = 0;
1778 bool others_exists = FALSE;
1780 char *newstr = NULL;
1785 /* Look for "atom-specials", counting the backslash and quote characters as
1786 these will need escapping */
1793 else if(!escape_only) {
1794 const char *p3 = atom_specials;
1796 while(*p3 && !others_exists) {
1798 others_exists = TRUE;
1807 /* Does the input contain any "atom-special" characters? */
1808 if(!backsp_count && !quote_count && !others_exists)
1811 /* Calculate the new string length */
1812 newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2);
1814 /* Allocate the new string */
1815 newstr = (char *) malloc((newlen + 1) * sizeof(char));
1819 /* Surround the string in quotes if necessary */
1823 newstr[newlen - 1] = '"';
1827 /* Copy the string, escaping backslash and quote characters along the way */
1830 if(*p1 == '\\' || *p1 == '"') {
1841 /* Terminate the string */
1842 newstr[newlen] = '\0';
1847 /***********************************************************************
1851 * Portable test of whether the specified char is a "bchar" as defined in the
1852 * grammar of RFC-5092.
1854 static bool imap_is_bchar(char ch)
1858 case ':': case '@': case '/':
1859 /* bchar -> achar */
1861 /* bchar -> achar -> uchar -> unreserved */
1862 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
1863 case '7': case '8': case '9':
1864 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
1865 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
1866 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
1867 case 'V': case 'W': case 'X': case 'Y': case 'Z':
1868 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
1869 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
1870 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
1871 case 'v': case 'w': case 'x': case 'y': case 'z':
1872 case '-': case '.': case '_': case '~':
1873 /* bchar -> achar -> uchar -> sub-delims-sh */
1874 case '!': case '$': case '\'': case '(': case ')': case '*':
1876 /* bchar -> achar -> uchar -> pct-encoded */
1877 case '%': /* HEXDIG chars are already included above */
1885 /***********************************************************************
1887 * imap_parse_url_options()
1889 * Parse the URL login options.
1891 static CURLcode imap_parse_url_options(struct connectdata *conn)
1893 CURLcode result = CURLE_OK;
1894 struct imap_conn *imapc = &conn->proto.imapc;
1895 const char *ptr = conn->options;
1897 imapc->sasl.resetprefs = TRUE;
1899 while(!result && ptr && *ptr) {
1900 const char *key = ptr;
1903 while(*ptr && *ptr != '=')
1908 while(*ptr && *ptr != ';')
1911 if(strncasecompare(key, "AUTH=", 5))
1912 result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
1913 value, ptr - value);
1915 result = CURLE_URL_MALFORMAT;
1921 switch(imapc->sasl.prefmech) {
1922 case SASL_AUTH_NONE:
1923 imapc->preftype = IMAP_TYPE_NONE;
1925 case SASL_AUTH_DEFAULT:
1926 imapc->preftype = IMAP_TYPE_ANY;
1929 imapc->preftype = IMAP_TYPE_SASL;
1936 /***********************************************************************
1938 * imap_parse_url_path()
1940 * Parse the URL path into separate path components.
1943 static CURLcode imap_parse_url_path(struct connectdata *conn)
1945 /* The imap struct is already initialised in imap_connect() */
1946 CURLcode result = CURLE_OK;
1947 struct Curl_easy *data = conn->data;
1948 struct IMAP *imap = data->req.protop;
1949 const char *begin = data->state.path;
1950 const char *ptr = begin;
1952 /* See how much of the URL is a valid path and decode it */
1953 while(imap_is_bchar(*ptr))
1957 /* Remove the trailing slash if present */
1958 const char *end = ptr;
1959 if(end > begin && end[-1] == '/')
1962 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
1968 imap->mailbox = NULL;
1970 /* There can be any number of parameters in the form ";NAME=VALUE" */
1971 while(*ptr == ';') {
1976 /* Find the length of the name parameter */
1978 while(*ptr && *ptr != '=')
1982 return CURLE_URL_MALFORMAT;
1984 /* Decode the name parameter */
1985 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
1989 /* Find the length of the value parameter */
1991 while(imap_is_bchar(*ptr))
1994 /* Decode the value parameter */
1995 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2001 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2003 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2004 PARTIAL) stripping of the trailing slash character if it is present.
2006 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2007 if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
2008 if(valuelen > 0 && value[valuelen - 1] == '/')
2009 value[valuelen - 1] = '\0';
2011 imap->uidvalidity = value;
2014 else if(strcasecompare(name, "UID") && !imap->uid) {
2015 if(valuelen > 0 && value[valuelen - 1] == '/')
2016 value[valuelen - 1] = '\0';
2021 else if(strcasecompare(name, "SECTION") && !imap->section) {
2022 if(valuelen > 0 && value[valuelen - 1] == '/')
2023 value[valuelen - 1] = '\0';
2025 imap->section = value;
2028 else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
2029 if(valuelen > 0 && value[valuelen - 1] == '/')
2030 value[valuelen - 1] = '\0';
2032 imap->partial = value;
2039 return CURLE_URL_MALFORMAT;
2046 /* Does the URL contain a query parameter? Only valid when we have a mailbox
2047 and no UID as per RFC-5092 */
2048 if(imap->mailbox && !imap->uid && *ptr == '?') {
2049 /* Find the length of the query parameter */
2051 while(imap_is_bchar(*ptr))
2054 /* Decode the query parameter */
2055 result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2061 /* Any extra stuff at the end of the URL is an error */
2063 return CURLE_URL_MALFORMAT;
2068 /***********************************************************************
2070 * imap_parse_custom_request()
2072 * Parse the custom request.
2074 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2076 CURLcode result = CURLE_OK;
2077 struct Curl_easy *data = conn->data;
2078 struct IMAP *imap = data->req.protop;
2079 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2082 /* URL decode the custom request */
2083 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2085 /* Extract the parameters if specified */
2087 const char *params = imap->custom;
2089 while(*params && *params != ' ')
2093 imap->custom_params = strdup(params);
2094 imap->custom[params - imap->custom] = '\0';
2096 if(!imap->custom_params)
2097 result = CURLE_OUT_OF_MEMORY;
2105 #endif /* CURL_DISABLE_IMAP */