1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2020, 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 * RFC8314 Use of TLS for Email Submission and Access
32 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
34 ***************************************************************************/
36 #include "curl_setup.h"
38 #ifndef CURL_DISABLE_IMAP
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
47 #include <sys/utsname.h>
57 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59 #define in_addr_t unsigned long
62 #include <curl/curl.h>
69 #include "http.h" /* for HTTP proxy tunnel stuff */
73 #include "strtoofft.h"
75 #include "vtls/vtls.h"
82 #include "curl_sasl.h"
85 /* The last 3 #include files should be in this order */
86 #include "curl_printf.h"
87 #include "curl_memory.h"
90 /* Local API functions */
91 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
92 static CURLcode imap_do(struct connectdata *conn, bool *done);
93 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
95 static CURLcode imap_connect(struct connectdata *conn, bool *done);
96 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
97 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
98 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 CURLPROTO_IMAP, /* family */
136 PROTOPT_CLOSEACTION| /* flags */
142 * IMAPS protocol handler.
145 const struct Curl_handler Curl_handler_imaps = {
146 "IMAPS", /* scheme */
147 imap_setup_connection, /* setup_connection */
149 imap_done, /* done */
150 ZERO_NULL, /* do_more */
151 imap_connect, /* connect_it */
152 imap_multi_statemach, /* connecting */
153 imap_doing, /* doing */
154 imap_getsock, /* proto_getsock */
155 imap_getsock, /* doing_getsock */
156 ZERO_NULL, /* domore_getsock */
157 ZERO_NULL, /* perform_getsock */
158 imap_disconnect, /* disconnect */
159 ZERO_NULL, /* readwrite */
160 ZERO_NULL, /* connection_check */
161 PORT_IMAPS, /* defport */
162 CURLPROTO_IMAPS, /* protocol */
163 CURLPROTO_IMAP, /* family */
164 PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
169 #define IMAP_RESP_OK 1
170 #define IMAP_RESP_NOT_OK 2
171 #define IMAP_RESP_PREAUTH 3
173 /* SASL parameters for the imap protocol */
174 static const struct SASLproto saslimap = {
175 "imap", /* The service name */
176 '+', /* Code received when continuation is expected */
177 IMAP_RESP_OK, /* Code to receive upon authentication success */
178 0, /* Maximum initial response length (no max) */
179 imap_perform_authenticate, /* Send authentication command */
180 imap_continue_authenticate, /* Send authentication continuation */
181 imap_get_message /* Get SASL response message */
186 static void imap_to_imaps(struct connectdata *conn)
188 /* Change the connection handler */
189 conn->handler = &Curl_handler_imaps;
191 /* Set the connection's upgraded to TLS flag */
192 conn->bits.tls_upgraded = TRUE;
195 #define imap_to_imaps(x) Curl_nop_stmt
198 /***********************************************************************
202 * Determines whether the untagged response is related to the specified
203 * command by checking if it is in format "* <command-name> ..." or
204 * "* <number> <command-name> ...".
206 * The "* " marker is assumed to have already been checked by the caller.
208 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
210 const char *end = line + len;
211 size_t cmd_len = strlen(cmd);
213 /* Skip the untagged response marker */
216 /* Do we have a number after the marker? */
217 if(line < end && ISDIGIT(*line)) {
218 /* Skip the number */
221 while(line < end && ISDIGIT(*line));
223 /* Do we have the space character? */
224 if(line == end || *line != ' ')
230 /* Does the command name match and is it followed by a space character or at
232 if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
233 (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
239 /***********************************************************************
243 * Checks whether the given string is a valid tagged, untagged or continuation
244 * response which can be processed by the response handler.
246 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
249 struct IMAP *imap = conn->data->req.protop;
250 struct imap_conn *imapc = &conn->proto.imapc;
251 const char *id = imapc->resptag;
252 size_t id_len = strlen(id);
254 /* Do we have a tagged command response? */
255 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
259 if(len >= 2 && !memcmp(line, "OK", 2))
260 *resp = IMAP_RESP_OK;
261 else if(len >= 7 && !memcmp(line, "PREAUTH", 7))
262 *resp = IMAP_RESP_PREAUTH;
264 *resp = IMAP_RESP_NOT_OK;
269 /* Do we have an untagged command response? */
270 if(len >= 2 && !memcmp("* ", line, 2)) {
271 switch(imapc->state) {
272 /* States which are interested in untagged responses */
273 case IMAP_CAPABILITY:
274 if(!imap_matchresp(line, len, "CAPABILITY"))
279 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
280 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
281 (!strcasecompare(imap->custom, "STORE") ||
282 !imap_matchresp(line, len, "FETCH")) &&
283 !strcasecompare(imap->custom, "SELECT") &&
284 !strcasecompare(imap->custom, "EXAMINE") &&
285 !strcasecompare(imap->custom, "SEARCH") &&
286 !strcasecompare(imap->custom, "EXPUNGE") &&
287 !strcasecompare(imap->custom, "LSUB") &&
288 !strcasecompare(imap->custom, "UID") &&
289 !strcasecompare(imap->custom, "NOOP")))
294 /* SELECT is special in that its untagged responses do not have a
295 common prefix so accept anything! */
299 if(!imap_matchresp(line, len, "FETCH"))
304 if(!imap_matchresp(line, len, "SEARCH"))
308 /* Ignore other untagged responses */
317 /* Do we have a continuation response? This should be a + symbol followed by
318 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
319 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
320 some e-mail servers ignore this and only send a single + instead. */
321 if(imap && !imap->custom && ((len == 3 && line[0] == '+') ||
322 (len >= 2 && !memcmp("+ ", line, 2)))) {
323 switch(imapc->state) {
324 /* States which are interested in continuation responses */
325 case IMAP_AUTHENTICATE:
331 failf(conn->data, "Unexpected continuation response");
339 return FALSE; /* Nothing for us */
342 /***********************************************************************
346 * Gets the authentication message from the response buffer.
348 static void imap_get_message(char *buffer, char **outptr)
350 size_t len = strlen(buffer);
351 char *message = NULL;
354 /* Find the start of the message */
356 for(message = buffer + 2; *message == ' ' || *message == '\t';
360 /* Find the end of the message */
362 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
363 message[len] != '\t')
366 /* Terminate the message */
372 /* junk input => zero length output */
373 message = &buffer[len];
378 /***********************************************************************
382 * This is the ONLY way to change IMAP state!
384 static void state(struct connectdata *conn, imapstate newstate)
386 struct imap_conn *imapc = &conn->proto.imapc;
387 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
388 /* for debug purposes */
389 static const char * const names[]={
408 if(imapc->state != newstate)
409 infof(conn->data, "IMAP %p state change from %s to %s\n",
410 (void *)imapc, names[imapc->state], names[newstate]);
413 imapc->state = newstate;
416 /***********************************************************************
418 * imap_perform_capability()
420 * Sends the CAPABILITY command in order to obtain a list of server side
421 * supported capabilities.
423 static CURLcode imap_perform_capability(struct connectdata *conn)
425 CURLcode result = CURLE_OK;
426 struct imap_conn *imapc = &conn->proto.imapc;
427 imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
428 imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
429 imapc->tls_supported = FALSE; /* Clear the TLS capability */
431 /* Send the CAPABILITY command */
432 result = imap_sendf(conn, "CAPABILITY");
435 state(conn, IMAP_CAPABILITY);
440 /***********************************************************************
442 * imap_perform_starttls()
444 * Sends the STARTTLS command to start the upgrade to TLS.
446 static CURLcode imap_perform_starttls(struct connectdata *conn)
448 /* Send the STARTTLS command */
449 CURLcode 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 /* Start the SSL connection */
466 struct imap_conn *imapc = &conn->proto.imapc;
467 CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
471 if(imapc->state != IMAP_UPGRADETLS)
472 state(conn, IMAP_UPGRADETLS);
476 result = imap_perform_capability(conn);
483 /***********************************************************************
485 * imap_perform_login()
487 * Sends a clear text LOGIN command to authenticate with.
489 static CURLcode imap_perform_login(struct connectdata *conn)
491 CURLcode result = CURLE_OK;
495 /* Check we have a username and password to authenticate with and end the
496 connect phase if we don't */
497 if(!conn->bits.user_passwd) {
498 state(conn, IMAP_STOP);
503 /* Make sure the username and password are in the correct atom format */
504 user = imap_atom(conn->user, false);
505 passwd = imap_atom(conn->passwd, false);
507 /* Send the LOGIN command */
508 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
509 passwd ? passwd : "");
515 state(conn, IMAP_LOGIN);
520 /***********************************************************************
522 * imap_perform_authenticate()
524 * Sends an AUTHENTICATE command allowing the client to login with the given
525 * SASL authentication mechanism.
527 static CURLcode imap_perform_authenticate(struct connectdata *conn,
529 const char *initresp)
531 CURLcode result = CURLE_OK;
534 /* Send the AUTHENTICATE command with the initial response */
535 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
538 /* Send the AUTHENTICATE command */
539 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
545 /***********************************************************************
547 * imap_continue_authenticate()
549 * Sends SASL continuation data or cancellation.
551 static CURLcode imap_continue_authenticate(struct connectdata *conn,
554 struct imap_conn *imapc = &conn->proto.imapc;
556 return Curl_pp_sendf(&imapc->pp, "%s", resp);
559 /***********************************************************************
561 * imap_perform_authentication()
563 * Initiates the authentication sequence, with the appropriate SASL
564 * authentication mechanism, falling back to clear text should a common
565 * mechanism not be available between the client and server.
567 static CURLcode imap_perform_authentication(struct connectdata *conn)
569 CURLcode result = CURLE_OK;
570 struct imap_conn *imapc = &conn->proto.imapc;
571 saslprogress progress;
573 /* Check if already authenticated OR if there is enough data to authenticate
574 with and end the connect phase if we don't */
576 !Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
577 state(conn, IMAP_STOP);
581 /* Calculate the SASL login details */
582 result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
585 if(progress == SASL_INPROGRESS)
586 state(conn, IMAP_AUTHENTICATE);
587 else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
588 /* Perform clear text authentication */
589 result = imap_perform_login(conn);
591 /* Other mechanisms not supported */
592 infof(conn->data, "No known authentication mechanisms supported!\n");
593 result = CURLE_LOGIN_DENIED;
600 /***********************************************************************
602 * imap_perform_list()
604 * Sends a LIST command or an alternative custom request.
606 static CURLcode imap_perform_list(struct connectdata *conn)
608 CURLcode result = CURLE_OK;
609 struct Curl_easy *data = conn->data;
610 struct IMAP *imap = data->req.protop;
613 /* Send the custom request */
614 result = imap_sendf(conn, "%s%s", imap->custom,
615 imap->custom_params ? imap->custom_params : "");
617 /* Make sure the mailbox is in the correct atom format if necessary */
618 char *mailbox = imap->mailbox ? imap_atom(imap->mailbox, true)
621 return CURLE_OUT_OF_MEMORY;
623 /* Send the LIST command */
624 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
630 state(conn, IMAP_LIST);
635 /***********************************************************************
637 * imap_perform_select()
639 * Sends a SELECT command to ask the server to change the selected mailbox.
641 static CURLcode imap_perform_select(struct connectdata *conn)
643 CURLcode result = CURLE_OK;
644 struct Curl_easy *data = conn->data;
645 struct IMAP *imap = data->req.protop;
646 struct imap_conn *imapc = &conn->proto.imapc;
649 /* Invalidate old information as we are switching mailboxes */
650 Curl_safefree(imapc->mailbox);
651 Curl_safefree(imapc->mailbox_uidvalidity);
653 /* Check we have a mailbox */
655 failf(conn->data, "Cannot SELECT without a mailbox.");
656 return CURLE_URL_MALFORMAT;
659 /* Make sure the mailbox is in the correct atom format */
660 mailbox = imap_atom(imap->mailbox, false);
662 return CURLE_OUT_OF_MEMORY;
664 /* Send the SELECT command */
665 result = imap_sendf(conn, "SELECT %s", mailbox);
670 state(conn, IMAP_SELECT);
675 /***********************************************************************
677 * imap_perform_fetch()
679 * Sends a FETCH command to initiate the download of a message.
681 static CURLcode imap_perform_fetch(struct connectdata *conn)
683 CURLcode result = CURLE_OK;
684 struct IMAP *imap = conn->data->req.protop;
685 /* Check we have a UID */
688 /* Send the FETCH command */
690 result = imap_sendf(conn, "UID FETCH %s BODY[%s]<%s>",
692 imap->section ? imap->section : "",
695 result = imap_sendf(conn, "UID FETCH %s BODY[%s]",
697 imap->section ? imap->section : "");
699 else if(imap->mindex) {
701 /* Send the FETCH command */
703 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
705 imap->section ? imap->section : "",
708 result = imap_sendf(conn, "FETCH %s BODY[%s]",
710 imap->section ? imap->section : "");
713 failf(conn->data, "Cannot FETCH without a UID.");
714 return CURLE_URL_MALFORMAT;
717 state(conn, IMAP_FETCH);
722 /***********************************************************************
724 * imap_perform_append()
726 * Sends an APPEND command to initiate the upload of a message.
728 static CURLcode imap_perform_append(struct connectdata *conn)
730 CURLcode result = CURLE_OK;
731 struct Curl_easy *data = conn->data;
732 struct IMAP *imap = data->req.protop;
735 /* Check we have a mailbox */
737 failf(data, "Cannot APPEND without a mailbox.");
738 return CURLE_URL_MALFORMAT;
741 /* Prepare the mime data if some. */
742 if(data->set.mimepost.kind != MIMEKIND_NONE) {
743 /* Use the whole structure as data. */
744 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
746 /* Add external headers and mime version. */
747 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
748 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
749 NULL, MIMESTRATEGY_MAIL);
752 if(!Curl_checkheaders(conn, "Mime-Version"))
753 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
754 "Mime-Version: 1.0");
756 /* Make sure we will read the entire mime structure. */
758 result = Curl_mime_rewind(&data->set.mimepost);
763 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
765 /* Read from mime structure. */
766 data->state.fread_func = (curl_read_callback) Curl_mime_read;
767 data->state.in = (void *) &data->set.mimepost;
770 /* Check we know the size of the upload */
771 if(data->state.infilesize < 0) {
772 failf(data, "Cannot APPEND with unknown input file size\n");
773 return CURLE_UPLOAD_FAILED;
776 /* Make sure the mailbox is in the correct atom format */
777 mailbox = imap_atom(imap->mailbox, false);
779 return CURLE_OUT_OF_MEMORY;
781 /* Send the APPEND command */
782 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
783 mailbox, data->state.infilesize);
788 state(conn, IMAP_APPEND);
793 /***********************************************************************
795 * imap_perform_search()
797 * Sends a SEARCH command.
799 static CURLcode imap_perform_search(struct connectdata *conn)
801 CURLcode result = CURLE_OK;
802 struct IMAP *imap = conn->data->req.protop;
804 /* Check we have a query string */
806 failf(conn->data, "Cannot SEARCH without a query string.");
807 return CURLE_URL_MALFORMAT;
810 /* Send the SEARCH command */
811 result = imap_sendf(conn, "SEARCH %s", imap->query);
814 state(conn, IMAP_SEARCH);
819 /***********************************************************************
821 * imap_perform_logout()
823 * Performs the logout action prior to sclose() being called.
825 static CURLcode imap_perform_logout(struct connectdata *conn)
827 /* Send the LOGOUT command */
828 CURLcode result = imap_sendf(conn, "LOGOUT");
831 state(conn, IMAP_LOGOUT);
836 /* For the initial server greeting */
837 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
841 struct Curl_easy *data = conn->data;
842 (void)instate; /* no use for this yet */
844 if(imapcode == IMAP_RESP_PREAUTH) {
846 struct imap_conn *imapc = &conn->proto.imapc;
847 imapc->preauth = TRUE;
848 infof(data, "PREAUTH connection, already authenticated!\n");
850 else if(imapcode != IMAP_RESP_OK) {
851 failf(data, "Got unexpected imap-server response");
852 return CURLE_WEIRD_SERVER_REPLY;
855 return imap_perform_capability(conn);
858 /* For CAPABILITY responses */
859 static CURLcode imap_state_capability_resp(struct connectdata *conn,
863 CURLcode result = CURLE_OK;
864 struct Curl_easy *data = conn->data;
865 struct imap_conn *imapc = &conn->proto.imapc;
866 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 */
878 (*line == ' ' || *line == '\t' ||
879 *line == '\r' || *line == '\n')) {
887 /* Extract the word */
888 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
889 line[wordlen] != '\t' && line[wordlen] != '\r' &&
890 line[wordlen] != '\n';)
893 /* Does the server support the STARTTLS capability? */
894 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
895 imapc->tls_supported = TRUE;
897 /* Has the server explicitly disabled clear text authentication? */
898 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
899 imapc->login_disabled = TRUE;
901 /* Does the server support the SASL-IR capability? */
902 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
903 imapc->ir_supported = TRUE;
905 /* Do we have a SASL based authentication mechanism? */
906 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
908 unsigned int mechbit;
913 /* Test the word for a matching authentication mechanism */
914 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
915 if(mechbit && llen == wordlen)
916 imapc->sasl.authmechs |= mechbit;
922 else if(imapcode == IMAP_RESP_OK) {
923 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
924 /* We don't have a SSL/TLS connection yet, but SSL is requested */
925 if(imapc->tls_supported)
926 /* Switch to TLS connection now */
927 result = imap_perform_starttls(conn);
928 else if(data->set.use_ssl == CURLUSESSL_TRY)
929 /* Fallback and carry on with authentication */
930 result = imap_perform_authentication(conn);
932 failf(data, "STARTTLS not supported.");
933 result = CURLE_USE_SSL_FAILED;
937 result = imap_perform_authentication(conn);
940 result = imap_perform_authentication(conn);
945 /* For STARTTLS responses */
946 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
950 CURLcode result = CURLE_OK;
951 struct Curl_easy *data = conn->data;
953 (void)instate; /* no use for this yet */
955 if(imapcode != IMAP_RESP_OK) {
956 if(data->set.use_ssl != CURLUSESSL_TRY) {
957 failf(data, "STARTTLS denied");
958 result = CURLE_USE_SSL_FAILED;
961 result = imap_perform_authentication(conn);
964 result = imap_perform_upgrade_tls(conn);
969 /* For SASL authentication responses */
970 static CURLcode imap_state_auth_resp(struct connectdata *conn,
974 CURLcode result = CURLE_OK;
975 struct Curl_easy *data = conn->data;
976 struct imap_conn *imapc = &conn->proto.imapc;
977 saslprogress progress;
979 (void)instate; /* no use for this yet */
981 result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
985 state(conn, IMAP_STOP); /* Authenticated */
987 case SASL_IDLE: /* No mechanism left after cancellation */
988 if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
989 /* Perform clear text authentication */
990 result = imap_perform_login(conn);
992 failf(data, "Authentication cancelled");
993 result = CURLE_LOGIN_DENIED;
1003 /* For LOGIN responses */
1004 static CURLcode imap_state_login_resp(struct connectdata *conn,
1008 CURLcode result = CURLE_OK;
1009 struct Curl_easy *data = conn->data;
1011 (void)instate; /* no use for this yet */
1013 if(imapcode != IMAP_RESP_OK) {
1014 failf(data, "Access denied. %c", imapcode);
1015 result = CURLE_LOGIN_DENIED;
1018 /* End of connect phase */
1019 state(conn, IMAP_STOP);
1024 /* For LIST and SEARCH responses */
1025 static CURLcode imap_state_listsearch_resp(struct connectdata *conn,
1029 CURLcode result = CURLE_OK;
1030 char *line = conn->data->state.buffer;
1031 size_t len = strlen(line);
1033 (void)instate; /* No use for this yet */
1035 if(imapcode == '*') {
1036 /* Temporarily add the LF character back and send as body to the client */
1038 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1041 else if(imapcode != IMAP_RESP_OK)
1042 result = CURLE_QUOTE_ERROR;
1044 /* End of DO phase */
1045 state(conn, IMAP_STOP);
1050 /* For SELECT responses */
1051 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1054 CURLcode result = CURLE_OK;
1055 struct Curl_easy *data = conn->data;
1056 struct IMAP *imap = conn->data->req.protop;
1057 struct imap_conn *imapc = &conn->proto.imapc;
1058 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 */
1065 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1066 Curl_safefree(imapc->mailbox_uidvalidity);
1067 imapc->mailbox_uidvalidity = strdup(tmp);
1070 else if(imapcode == IMAP_RESP_OK) {
1071 /* Check if the UIDVALIDITY has been specified and matches */
1072 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1073 !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1074 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1075 result = CURLE_REMOTE_FILE_NOT_FOUND;
1078 /* Note the currently opened mailbox on this connection */
1079 imapc->mailbox = strdup(imap->mailbox);
1082 result = imap_perform_list(conn);
1083 else if(imap->query)
1084 result = imap_perform_search(conn);
1086 result = imap_perform_fetch(conn);
1090 failf(data, "Select failed");
1091 result = CURLE_LOGIN_DENIED;
1097 /* For the (first line of the) FETCH responses */
1098 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1101 CURLcode result = CURLE_OK;
1102 struct Curl_easy *data = conn->data;
1103 struct imap_conn *imapc = &conn->proto.imapc;
1104 struct pingpong *pp = &imapc->pp;
1105 const char *ptr = data->state.buffer;
1106 bool parsed = FALSE;
1107 curl_off_t size = 0;
1109 (void)instate; /* no use for this yet */
1111 if(imapcode != '*') {
1112 Curl_pgrsSetDownloadSize(data, -1);
1113 state(conn, IMAP_STOP);
1114 return CURLE_REMOTE_FILE_NOT_FOUND;
1117 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1118 the continuation data contained within the curly brackets */
1119 while(*ptr && (*ptr != '{'))
1124 if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
1125 if(endptr - ptr > 1 && endptr[0] == '}' &&
1126 endptr[1] == '\r' && endptr[2] == '\0')
1132 infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download\n",
1134 Curl_pgrsSetDownloadSize(data, size);
1137 /* At this point there is a bunch of data in the header "cache" that is
1138 actually body content, send it as body and then skip it. Do note
1139 that there may even be additional "headers" after the body. */
1140 size_t chunk = pp->cache_size;
1142 if(chunk > (size_t)size)
1143 /* The conversion from curl_off_t to size_t is always fine here */
1144 chunk = (size_t)size;
1147 /* no size, we're done with the data */
1148 state(conn, IMAP_STOP);
1151 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1155 data->req.bytecount += chunk;
1157 infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
1158 " bytes are left for transfer\n", chunk, size - chunk);
1160 /* Have we used the entire cache or just part of it?*/
1161 if(pp->cache_size > chunk) {
1162 /* Only part of it so shrink the cache to fit the trailing data */
1163 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1164 pp->cache_size -= chunk;
1167 /* Free the cache */
1168 Curl_safefree(pp->cache);
1170 /* Reset the cache size */
1175 if(data->req.bytecount == size)
1176 /* The entire data is already transferred! */
1177 Curl_setup_transfer(data, -1, -1, FALSE, -1);
1180 data->req.maxdownload = size;
1181 /* force a recv/send check of this connection, as the data might've been
1182 read off the socket already */
1183 data->conn->cselect_bits = CURL_CSELECT_IN;
1184 Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1);
1188 /* We don't know how to parse this line */
1189 failf(pp->conn->data, "Failed to parse FETCH response.");
1190 result = CURLE_WEIRD_SERVER_REPLY;
1193 /* End of DO phase */
1194 state(conn, IMAP_STOP);
1199 /* For final FETCH responses performed after the download */
1200 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1204 CURLcode result = CURLE_OK;
1206 (void)instate; /* No use for this yet */
1208 if(imapcode != IMAP_RESP_OK)
1209 result = CURLE_WEIRD_SERVER_REPLY;
1211 /* End of DONE phase */
1212 state(conn, IMAP_STOP);
1217 /* For APPEND responses */
1218 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1221 CURLcode result = CURLE_OK;
1222 struct Curl_easy *data = conn->data;
1224 (void)instate; /* No use for this yet */
1226 if(imapcode != '+') {
1227 result = CURLE_UPLOAD_FAILED;
1230 /* Set the progress upload size */
1231 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1234 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
1236 /* End of DO phase */
1237 state(conn, IMAP_STOP);
1243 /* For final APPEND responses performed after the upload */
1244 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1248 CURLcode result = CURLE_OK;
1250 (void)instate; /* No use for this yet */
1252 if(imapcode != IMAP_RESP_OK)
1253 result = CURLE_UPLOAD_FAILED;
1255 /* End of DONE phase */
1256 state(conn, IMAP_STOP);
1261 static CURLcode imap_statemach_act(struct connectdata *conn)
1263 CURLcode result = CURLE_OK;
1264 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1266 struct imap_conn *imapc = &conn->proto.imapc;
1267 struct pingpong *pp = &imapc->pp;
1270 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1271 if(imapc->state == IMAP_UPGRADETLS)
1272 return imap_perform_upgrade_tls(conn);
1274 /* Flush any data that needs to be sent */
1276 return Curl_pp_flushsend(pp);
1279 /* Read the response from the server */
1280 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1284 /* Was there an error parsing the response line? */
1286 return CURLE_WEIRD_SERVER_REPLY;
1291 /* We have now received a full IMAP server response */
1292 switch(imapc->state) {
1293 case IMAP_SERVERGREET:
1294 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1297 case IMAP_CAPABILITY:
1298 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1302 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1305 case IMAP_AUTHENTICATE:
1306 result = imap_state_auth_resp(conn, imapcode, imapc->state);
1310 result = imap_state_login_resp(conn, imapcode, imapc->state);
1315 result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
1319 result = imap_state_select_resp(conn, imapcode, imapc->state);
1323 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1326 case IMAP_FETCH_FINAL:
1327 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1331 result = imap_state_append_resp(conn, imapcode, imapc->state);
1334 case IMAP_APPEND_FINAL:
1335 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1339 /* fallthrough, just stop! */
1341 /* internal error */
1342 state(conn, IMAP_STOP);
1345 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1350 /* Called repeatedly until done from multi.c */
1351 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1353 CURLcode result = CURLE_OK;
1354 struct imap_conn *imapc = &conn->proto.imapc;
1356 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1357 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1358 if(result || !imapc->ssldone)
1362 result = Curl_pp_statemach(&imapc->pp, FALSE, FALSE);
1363 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1368 static CURLcode imap_block_statemach(struct connectdata *conn,
1371 CURLcode result = CURLE_OK;
1372 struct imap_conn *imapc = &conn->proto.imapc;
1374 while(imapc->state != IMAP_STOP && !result)
1375 result = Curl_pp_statemach(&imapc->pp, TRUE, disconnecting);
1380 /* Allocate and initialize the struct IMAP for the current Curl_easy if
1382 static CURLcode imap_init(struct connectdata *conn)
1384 CURLcode result = CURLE_OK;
1385 struct Curl_easy *data = conn->data;
1388 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1390 result = CURLE_OUT_OF_MEMORY;
1395 /* For the IMAP "protocol connect" and "doing" phases only */
1396 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks)
1398 return Curl_pp_getsock(&conn->proto.imapc.pp, socks);
1401 /***********************************************************************
1405 * This function should do everything that is to be considered a part of the
1408 * The variable 'done' points to will be TRUE if the protocol-layer connect
1409 * phase is done when this function returns, or FALSE if not.
1411 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1413 CURLcode result = CURLE_OK;
1414 struct imap_conn *imapc = &conn->proto.imapc;
1415 struct pingpong *pp = &imapc->pp;
1417 *done = FALSE; /* default to not done yet */
1419 /* We always support persistent connections in IMAP */
1420 connkeep(conn, "IMAP default");
1422 /* Set the default response time-out */
1423 pp->response_time = RESP_TIMEOUT;
1424 pp->statemach_act = imap_statemach_act;
1425 pp->endofresp = imap_endofresp;
1428 /* Set the default preferred authentication type and mechanism */
1429 imapc->preftype = IMAP_TYPE_ANY;
1430 Curl_sasl_init(&imapc->sasl, &saslimap);
1432 Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
1433 /* Initialise the pingpong layer */
1437 /* Parse the URL options */
1438 result = imap_parse_url_options(conn);
1442 /* Start off waiting for the server greeting response */
1443 state(conn, IMAP_SERVERGREET);
1445 /* Start off with an response id of '*' */
1446 strcpy(imapc->resptag, "*");
1448 result = imap_multi_statemach(conn, done);
1453 /***********************************************************************
1457 * The DONE function. This does what needs to be done after a single DO has
1460 * Input argument is already checked for validity.
1462 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1465 CURLcode result = CURLE_OK;
1466 struct Curl_easy *data = conn->data;
1467 struct IMAP *imap = data->req.protop;
1475 connclose(conn, "IMAP done with bad status"); /* marked for closure */
1476 result = status; /* use the already set error code */
1478 else if(!data->set.connect_only && !imap->custom &&
1479 (imap->uid || imap->mindex || data->set.upload ||
1480 data->set.mimepost.kind != MIMEKIND_NONE)) {
1481 /* Handle responses after FETCH or APPEND transfer has finished */
1483 if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
1484 state(conn, IMAP_FETCH_FINAL);
1486 /* End the APPEND command first by sending an empty line */
1487 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1489 state(conn, IMAP_APPEND_FINAL);
1492 /* Run the state-machine */
1494 result = imap_block_statemach(conn, FALSE);
1497 /* Cleanup our per-request based variables */
1498 Curl_safefree(imap->mailbox);
1499 Curl_safefree(imap->uidvalidity);
1500 Curl_safefree(imap->uid);
1501 Curl_safefree(imap->mindex);
1502 Curl_safefree(imap->section);
1503 Curl_safefree(imap->partial);
1504 Curl_safefree(imap->query);
1505 Curl_safefree(imap->custom);
1506 Curl_safefree(imap->custom_params);
1508 /* Clear the transfer mode for the next request */
1509 imap->transfer = FTPTRANSFER_BODY;
1514 /***********************************************************************
1518 * This is the actual DO function for IMAP. Fetch or append a message, or do
1519 * other things according to the options previously setup.
1521 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1524 /* This is IMAP and no proxy */
1525 CURLcode result = CURLE_OK;
1526 struct Curl_easy *data = conn->data;
1527 struct IMAP *imap = data->req.protop;
1528 struct imap_conn *imapc = &conn->proto.imapc;
1529 bool selected = FALSE;
1531 DEBUGF(infof(conn->data, "DO phase starts\n"));
1533 if(conn->data->set.opt_no_body) {
1534 /* Requested no body means no transfer */
1535 imap->transfer = FTPTRANSFER_INFO;
1538 *dophase_done = FALSE; /* not done yet */
1540 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1541 has already been selected on this connection */
1542 if(imap->mailbox && imapc->mailbox &&
1543 strcasecompare(imap->mailbox, imapc->mailbox) &&
1544 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1545 strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1548 /* Start the first command in the DO phase */
1549 if(conn->data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
1550 /* APPEND can be executed directly */
1551 result = imap_perform_append(conn);
1552 else if(imap->custom && (selected || !imap->mailbox))
1553 /* Custom command using the same mailbox or no mailbox */
1554 result = imap_perform_list(conn);
1555 else if(!imap->custom && selected && (imap->uid || imap->mindex))
1556 /* FETCH from the same mailbox */
1557 result = imap_perform_fetch(conn);
1558 else if(!imap->custom && selected && imap->query)
1559 /* SEARCH the current mailbox */
1560 result = imap_perform_search(conn);
1561 else if(imap->mailbox && !selected &&
1562 (imap->custom || imap->uid || imap->mindex || imap->query))
1563 /* SELECT the mailbox */
1564 result = imap_perform_select(conn);
1567 result = imap_perform_list(conn);
1572 /* Run the state-machine */
1573 result = imap_multi_statemach(conn, dophase_done);
1575 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1578 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1583 /***********************************************************************
1587 * This function is registered as 'curl_do' function. It decodes the path
1588 * parts etc as a wrapper to the actual DO function (imap_perform).
1590 * The input argument is already checked for validity.
1592 static CURLcode imap_do(struct connectdata *conn, bool *done)
1594 CURLcode result = CURLE_OK;
1596 *done = FALSE; /* default to false */
1598 /* Parse the URL path */
1599 result = imap_parse_url_path(conn);
1603 /* Parse the custom request */
1604 result = imap_parse_custom_request(conn);
1608 result = imap_regular_transfer(conn, done);
1613 /***********************************************************************
1617 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1618 * resources. BLOCKING.
1620 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1622 struct imap_conn *imapc = &conn->proto.imapc;
1624 /* We cannot send quit unconditionally. If this connection is stale or
1625 bad in any way, sending quit and waiting around here will make the
1626 disconnect wait in vain and cause more problems than we need to. */
1628 /* The IMAP session may or may not have been allocated/setup at this
1630 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
1631 if(!imap_perform_logout(conn))
1632 (void)imap_block_statemach(conn, TRUE); /* ignore errors on LOGOUT */
1634 /* Disconnect from the server */
1635 Curl_pp_disconnect(&imapc->pp);
1636 Curl_dyn_free(&imapc->dyn);
1638 /* Cleanup the SASL module */
1639 Curl_sasl_cleanup(conn, imapc->sasl.authused);
1641 /* Cleanup our connection based variables */
1642 Curl_safefree(imapc->mailbox);
1643 Curl_safefree(imapc->mailbox_uidvalidity);
1648 /* Call this when the DO phase has completed */
1649 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1651 struct IMAP *imap = conn->data->req.protop;
1655 if(imap->transfer != FTPTRANSFER_BODY)
1656 /* no data to transfer */
1657 Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
1662 /* Called from multi.c while DOing */
1663 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1665 CURLcode result = imap_multi_statemach(conn, dophase_done);
1668 DEBUGF(infof(conn->data, "DO phase failed\n"));
1669 else if(*dophase_done) {
1670 result = imap_dophase_done(conn, FALSE /* not connected */);
1672 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1678 /***********************************************************************
1680 * imap_regular_transfer()
1682 * The input argument is already checked for validity.
1684 * Performs all commands done before a regular transfer between a local and a
1687 static CURLcode imap_regular_transfer(struct connectdata *conn,
1690 CURLcode result = CURLE_OK;
1691 bool connected = FALSE;
1692 struct Curl_easy *data = conn->data;
1694 /* Make sure size is unknown at this point */
1695 data->req.size = -1;
1697 /* Set the progress data */
1698 Curl_pgrsSetUploadCounter(data, 0);
1699 Curl_pgrsSetDownloadCounter(data, 0);
1700 Curl_pgrsSetUploadSize(data, -1);
1701 Curl_pgrsSetDownloadSize(data, -1);
1703 /* Carry out the perform */
1704 result = imap_perform(conn, &connected, dophase_done);
1706 /* Perform post DO phase operations if necessary */
1707 if(!result && *dophase_done)
1708 result = imap_dophase_done(conn, connected);
1713 static CURLcode imap_setup_connection(struct connectdata *conn)
1715 /* Initialise the IMAP layer */
1716 CURLcode result = imap_init(conn);
1720 /* Clear the TLS upgraded flag */
1721 conn->bits.tls_upgraded = FALSE;
1726 /***********************************************************************
1730 * Sends the formatted string as an IMAP command to the server.
1732 * Designed to never block.
1734 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
1736 CURLcode result = CURLE_OK;
1737 struct imap_conn *imapc = &conn->proto.imapc;
1741 /* Calculate the tag based on the connection ID and command ID */
1742 msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1743 'A' + curlx_sltosi(conn->connection_id % 26),
1744 (++imapc->cmdid)%1000);
1746 /* start with a blank buffer */
1747 Curl_dyn_reset(&imapc->dyn);
1749 /* append tag + space + fmt */
1750 result = Curl_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt);
1754 result = Curl_pp_vsendf(&imapc->pp, Curl_dyn_ptr(&imapc->dyn), 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 escaping */
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.up.path[1]; /* skip leading slash */
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,
1990 /* Find the length of the value parameter */
1992 while(imap_is_bchar(*ptr))
1995 /* Decode the value parameter */
1996 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen,
2003 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2005 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2006 PARTIAL) stripping of the trailing slash character if it is present.
2008 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2009 if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
2010 if(valuelen > 0 && value[valuelen - 1] == '/')
2011 value[valuelen - 1] = '\0';
2013 imap->uidvalidity = value;
2016 else if(strcasecompare(name, "UID") && !imap->uid) {
2017 if(valuelen > 0 && value[valuelen - 1] == '/')
2018 value[valuelen - 1] = '\0';
2023 else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) {
2024 if(valuelen > 0 && value[valuelen - 1] == '/')
2025 value[valuelen - 1] = '\0';
2027 imap->mindex = value;
2030 else if(strcasecompare(name, "SECTION") && !imap->section) {
2031 if(valuelen > 0 && value[valuelen - 1] == '/')
2032 value[valuelen - 1] = '\0';
2034 imap->section = value;
2037 else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
2038 if(valuelen > 0 && value[valuelen - 1] == '/')
2039 value[valuelen - 1] = '\0';
2041 imap->partial = value;
2048 return CURLE_URL_MALFORMAT;
2055 /* Does the URL contain a query parameter? Only valid when we have a mailbox
2056 and no UID as per RFC-5092 */
2057 if(imap->mailbox && !imap->uid && !imap->mindex) {
2058 /* Get the query parameter, URL decoded */
2059 (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query,
2063 /* Any extra stuff at the end of the URL is an error */
2065 return CURLE_URL_MALFORMAT;
2070 /***********************************************************************
2072 * imap_parse_custom_request()
2074 * Parse the custom request.
2076 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2078 CURLcode result = CURLE_OK;
2079 struct Curl_easy *data = conn->data;
2080 struct IMAP *imap = data->req.protop;
2081 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2084 /* URL decode the custom request */
2085 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, REJECT_CTRL);
2087 /* Extract the parameters if specified */
2089 const char *params = imap->custom;
2091 while(*params && *params != ' ')
2095 imap->custom_params = strdup(params);
2096 imap->custom[params - imap->custom] = '\0';
2098 if(!imap->custom_params)
2099 result = CURLE_OUT_OF_MEMORY;
2107 #endif /* CURL_DISABLE_IMAP */