1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC2195 CRAM-MD5 authentication
22 * RFC2595 Using TLS with IMAP, POP3 and ACAP
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3501 IMAPv4 protocol
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28 * RFC4959 IMAP Extension for SASL Initial Client Response
29 * RFC5092 IMAP URL Scheme
30 * RFC6749 OAuth 2.0 Authorization Framework
31 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
33 ***************************************************************************/
35 #include "curl_setup.h"
37 #ifndef CURL_DISABLE_IMAP
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
46 #include <sys/utsname.h>
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
58 #define in_addr_t unsigned long
61 #include <curl/curl.h>
68 #include "http.h" /* for HTTP proxy tunnel stuff */
72 #include "strtoofft.h"
74 #include "vtls/vtls.h"
81 #include "curl_sasl.h"
83 #include "curl_printf.h"
85 #include "curl_memory.h"
86 /* The last #include file should be: */
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);
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 PORT_IMAP, /* defport */
133 CURLPROTO_IMAP, /* protocol */
134 PROTOPT_CLOSEACTION /* flags */
139 * IMAPS protocol handler.
142 const struct Curl_handler Curl_handler_imaps = {
143 "IMAPS", /* scheme */
144 imap_setup_connection, /* setup_connection */
146 imap_done, /* done */
147 ZERO_NULL, /* do_more */
148 imap_connect, /* connect_it */
149 imap_multi_statemach, /* connecting */
150 imap_doing, /* doing */
151 imap_getsock, /* proto_getsock */
152 imap_getsock, /* doing_getsock */
153 ZERO_NULL, /* domore_getsock */
154 ZERO_NULL, /* perform_getsock */
155 imap_disconnect, /* disconnect */
156 ZERO_NULL, /* readwrite */
157 PORT_IMAPS, /* defport */
158 CURLPROTO_IMAPS, /* protocol */
159 PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */
163 #ifndef CURL_DISABLE_HTTP
165 * HTTP-proxyed IMAP protocol handler.
168 static const struct Curl_handler Curl_handler_imap_proxy = {
170 Curl_http_setup_conn, /* setup_connection */
171 Curl_http, /* do_it */
172 Curl_http_done, /* done */
173 ZERO_NULL, /* do_more */
174 ZERO_NULL, /* connect_it */
175 ZERO_NULL, /* connecting */
176 ZERO_NULL, /* doing */
177 ZERO_NULL, /* proto_getsock */
178 ZERO_NULL, /* doing_getsock */
179 ZERO_NULL, /* domore_getsock */
180 ZERO_NULL, /* perform_getsock */
181 ZERO_NULL, /* disconnect */
182 ZERO_NULL, /* readwrite */
183 PORT_IMAP, /* defport */
184 CURLPROTO_HTTP, /* protocol */
185 PROTOPT_NONE /* flags */
190 * HTTP-proxyed IMAPS protocol handler.
193 static const struct Curl_handler Curl_handler_imaps_proxy = {
194 "IMAPS", /* scheme */
195 Curl_http_setup_conn, /* setup_connection */
196 Curl_http, /* do_it */
197 Curl_http_done, /* done */
198 ZERO_NULL, /* do_more */
199 ZERO_NULL, /* connect_it */
200 ZERO_NULL, /* connecting */
201 ZERO_NULL, /* doing */
202 ZERO_NULL, /* proto_getsock */
203 ZERO_NULL, /* doing_getsock */
204 ZERO_NULL, /* domore_getsock */
205 ZERO_NULL, /* perform_getsock */
206 ZERO_NULL, /* disconnect */
207 ZERO_NULL, /* readwrite */
208 PORT_IMAPS, /* defport */
209 CURLPROTO_HTTP, /* protocol */
210 PROTOPT_NONE /* flags */
215 /* SASL parameters for the imap protocol */
216 static const struct SASLproto saslimap = {
217 "imap", /* The service name */
218 '+', /* Code received when continuation is expected */
219 'O', /* Code to receive upon authentication success */
220 0, /* Maximum initial response length (no max) */
221 imap_perform_authenticate, /* Send authentication command */
222 imap_continue_authenticate, /* Send authentication continuation */
223 imap_get_message /* Get SASL response message */
228 static void imap_to_imaps(struct connectdata *conn)
230 conn->handler = &Curl_handler_imaps;
233 #define imap_to_imaps(x) Curl_nop_stmt
236 /***********************************************************************
240 * Determines whether the untagged response is related to the specified
241 * command by checking if it is in format "* <command-name> ..." or
242 * "* <number> <command-name> ...".
244 * The "* " marker is assumed to have already been checked by the caller.
246 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
248 const char *end = line + len;
249 size_t cmd_len = strlen(cmd);
251 /* Skip the untagged response marker */
254 /* Do we have a number after the marker? */
255 if(line < end && ISDIGIT(*line)) {
256 /* Skip the number */
259 while(line < end && ISDIGIT(*line));
261 /* Do we have the space character? */
262 if(line == end || *line != ' ')
268 /* Does the command name match and is it followed by a space character or at
270 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
271 (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
277 /***********************************************************************
281 * Checks whether the given string is a valid tagged, untagged or continuation
282 * response which can be processed by the response handler.
284 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
287 struct IMAP *imap = conn->data->req.protop;
288 struct imap_conn *imapc = &conn->proto.imapc;
289 const char *id = imapc->resptag;
290 size_t id_len = strlen(id);
292 /* Do we have a tagged command response? */
293 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
297 if(len >= 2 && !memcmp(line, "OK", 2))
299 else if(len >= 2 && !memcmp(line, "NO", 2))
301 else if(len >= 3 && !memcmp(line, "BAD", 3))
304 failf(conn->data, "Bad tagged response");
311 /* Do we have an untagged command response? */
312 if(len >= 2 && !memcmp("* ", line, 2)) {
313 switch(imapc->state) {
314 /* States which are interested in untagged responses */
315 case IMAP_CAPABILITY:
316 if(!imap_matchresp(line, len, "CAPABILITY"))
321 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
322 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
323 (strcmp(imap->custom, "STORE") ||
324 !imap_matchresp(line, len, "FETCH")) &&
325 strcmp(imap->custom, "SELECT") &&
326 strcmp(imap->custom, "EXAMINE") &&
327 strcmp(imap->custom, "SEARCH") &&
328 strcmp(imap->custom, "EXPUNGE") &&
329 strcmp(imap->custom, "LSUB") &&
330 strcmp(imap->custom, "UID") &&
331 strcmp(imap->custom, "NOOP")))
336 /* SELECT is special in that its untagged responses do not have a
337 common prefix so accept anything! */
341 if(!imap_matchresp(line, len, "FETCH"))
346 if(!imap_matchresp(line, len, "SEARCH"))
350 /* Ignore other untagged responses */
359 /* Do we have a continuation response? This should be a + symbol followed by
360 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
361 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
362 some e-mail servers ignore this and only send a single + instead. */
363 if((len == 3 && !memcmp("+", line, 1)) ||
364 (len >= 2 && !memcmp("+ ", line, 2))) {
365 switch(imapc->state) {
366 /* States which are interested in continuation responses */
367 case IMAP_AUTHENTICATE:
373 failf(conn->data, "Unexpected continuation response");
381 return FALSE; /* Nothing for us */
384 /***********************************************************************
388 * Gets the authentication message from the response buffer.
390 static void imap_get_message(char *buffer, char** outptr)
393 char* message = NULL;
395 /* Find the start of the message */
396 for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
399 /* Find the end of the message */
400 for(len = strlen(message); len--;)
401 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
402 message[len] != '\t')
405 /* Terminate the message */
413 /***********************************************************************
417 * This is the ONLY way to change IMAP state!
419 static void state(struct connectdata *conn, imapstate newstate)
421 struct imap_conn *imapc = &conn->proto.imapc;
422 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
423 /* for debug purposes */
424 static const char * const names[]={
443 if(imapc->state != newstate)
444 infof(conn->data, "IMAP %p state change from %s to %s\n",
445 (void *)imapc, names[imapc->state], names[newstate]);
448 imapc->state = newstate;
451 /***********************************************************************
453 * imap_perform_capability()
455 * Sends the CAPABILITY command in order to obtain a list of server side
456 * supported capabilities.
458 static CURLcode imap_perform_capability(struct connectdata *conn)
460 CURLcode result = CURLE_OK;
461 struct imap_conn *imapc = &conn->proto.imapc;
463 imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
464 imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
465 imapc->tls_supported = FALSE; /* Clear the TLS capability */
467 /* Send the CAPABILITY command */
468 result = imap_sendf(conn, "CAPABILITY");
471 state(conn, IMAP_CAPABILITY);
476 /***********************************************************************
478 * imap_perform_starttls()
480 * Sends the STARTTLS command to start the upgrade to TLS.
482 static CURLcode imap_perform_starttls(struct connectdata *conn)
484 CURLcode result = CURLE_OK;
486 /* Send the STARTTLS command */
487 result = imap_sendf(conn, "STARTTLS");
490 state(conn, IMAP_STARTTLS);
495 /***********************************************************************
497 * imap_perform_upgrade_tls()
499 * Performs the upgrade to TLS.
501 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
503 CURLcode result = CURLE_OK;
504 struct imap_conn *imapc = &conn->proto.imapc;
506 /* Start the SSL connection */
507 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
510 if(imapc->state != IMAP_UPGRADETLS)
511 state(conn, IMAP_UPGRADETLS);
515 result = imap_perform_capability(conn);
522 /***********************************************************************
524 * imap_perform_login()
526 * Sends a clear text LOGIN command to authenticate with.
528 static CURLcode imap_perform_login(struct connectdata *conn)
530 CURLcode result = CURLE_OK;
534 /* Check we have a username and password to authenticate with and end the
535 connect phase if we don't */
536 if(!conn->bits.user_passwd) {
537 state(conn, IMAP_STOP);
542 /* Make sure the username and password are in the correct atom format */
543 user = imap_atom(conn->user);
544 passwd = imap_atom(conn->passwd);
546 /* Send the LOGIN command */
547 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
548 passwd ? passwd : "");
554 state(conn, IMAP_LOGIN);
559 /***********************************************************************
561 * imap_perform_authenticate()
563 * Sends an AUTHENTICATE command allowing the client to login with the given
564 * SASL authentication mechanism.
566 static CURLcode imap_perform_authenticate(struct connectdata *conn,
568 const char *initresp)
570 CURLcode result = CURLE_OK;
573 /* Send the AUTHENTICATE command with the initial response */
574 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
577 /* Send the AUTHENTICATE command */
578 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
584 /***********************************************************************
586 * imap_continue_authenticate()
588 * Sends SASL continuation data or cancellation.
590 static CURLcode imap_continue_authenticate(struct connectdata *conn,
593 struct imap_conn *imapc = &conn->proto.imapc;
595 return Curl_pp_sendf(&imapc->pp, "%s", resp);
598 /***********************************************************************
600 * imap_perform_authentication()
602 * Initiates the authentication sequence, with the appropriate SASL
603 * authentication mechanism, falling back to clear text should a common
604 * mechanism not be available between the client and server.
606 static CURLcode imap_perform_authentication(struct connectdata *conn)
608 CURLcode result = CURLE_OK;
609 struct imap_conn *imapc = &conn->proto.imapc;
610 saslprogress progress;
612 /* Check we have enough data to authenticate with and end the
613 connect phase if we don't */
614 if(!Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
615 state(conn, IMAP_STOP);
619 /* Calculate the SASL login details */
620 result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
623 if(progress == SASL_INPROGRESS)
624 state(conn, IMAP_AUTHENTICATE);
625 else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
626 /* Perform clear text authentication */
627 result = imap_perform_login(conn);
629 /* Other mechanisms not supported */
630 infof(conn->data, "No known authentication mechanisms supported!\n");
631 result = CURLE_LOGIN_DENIED;
638 /***********************************************************************
640 * imap_perform_list()
642 * Sends a LIST command or an alternative custom request.
644 static CURLcode imap_perform_list(struct connectdata *conn)
646 CURLcode result = CURLE_OK;
647 struct SessionHandle *data = conn->data;
648 struct IMAP *imap = data->req.protop;
652 /* Send the custom request */
653 result = imap_sendf(conn, "%s%s", imap->custom,
654 imap->custom_params ? imap->custom_params : "");
656 /* Make sure the mailbox is in the correct atom format */
657 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
659 return CURLE_OUT_OF_MEMORY;
661 /* Send the LIST command */
662 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
668 state(conn, IMAP_LIST);
673 /***********************************************************************
675 * imap_perform_select()
677 * Sends a SELECT command to ask the server to change the selected mailbox.
679 static CURLcode imap_perform_select(struct connectdata *conn)
681 CURLcode result = CURLE_OK;
682 struct SessionHandle *data = conn->data;
683 struct IMAP *imap = data->req.protop;
684 struct imap_conn *imapc = &conn->proto.imapc;
687 /* Invalidate old information as we are switching mailboxes */
688 Curl_safefree(imapc->mailbox);
689 Curl_safefree(imapc->mailbox_uidvalidity);
691 /* Check we have a mailbox */
693 failf(conn->data, "Cannot SELECT without a mailbox.");
694 return CURLE_URL_MALFORMAT;
697 /* Make sure the mailbox is in the correct atom format */
698 mailbox = imap_atom(imap->mailbox);
700 return CURLE_OUT_OF_MEMORY;
702 /* Send the SELECT command */
703 result = imap_sendf(conn, "SELECT %s", mailbox);
708 state(conn, IMAP_SELECT);
713 /***********************************************************************
715 * imap_perform_fetch()
717 * Sends a FETCH command to initiate the download of a message.
719 static CURLcode imap_perform_fetch(struct connectdata *conn)
721 CURLcode result = CURLE_OK;
722 struct IMAP *imap = conn->data->req.protop;
724 /* Check we have a UID */
726 failf(conn->data, "Cannot FETCH without a UID.");
727 return CURLE_URL_MALFORMAT;
730 /* Send the FETCH command */
732 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
734 imap->section ? imap->section : "",
737 result = imap_sendf(conn, "FETCH %s BODY[%s]",
739 imap->section ? imap->section : "");
742 state(conn, IMAP_FETCH);
747 /***********************************************************************
749 * imap_perform_append()
751 * Sends an APPEND command to initiate the upload of a message.
753 static CURLcode imap_perform_append(struct connectdata *conn)
755 CURLcode result = CURLE_OK;
756 struct IMAP *imap = conn->data->req.protop;
759 /* Check we have a mailbox */
761 failf(conn->data, "Cannot APPEND without a mailbox.");
762 return CURLE_URL_MALFORMAT;
765 /* Check we know the size of the upload */
766 if(conn->data->state.infilesize < 0) {
767 failf(conn->data, "Cannot APPEND with unknown input file size\n");
768 return CURLE_UPLOAD_FAILED;
771 /* Make sure the mailbox is in the correct atom format */
772 mailbox = imap_atom(imap->mailbox);
774 return CURLE_OUT_OF_MEMORY;
776 /* Send the APPEND command */
777 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
778 mailbox, conn->data->state.infilesize);
783 state(conn, IMAP_APPEND);
788 /***********************************************************************
790 * imap_perform_search()
792 * Sends a SEARCH command.
794 static CURLcode imap_perform_search(struct connectdata *conn)
796 CURLcode result = CURLE_OK;
797 struct IMAP *imap = conn->data->req.protop;
799 /* Check we have a query string */
801 failf(conn->data, "Cannot SEARCH without a query string.");
802 return CURLE_URL_MALFORMAT;
805 /* Send the SEARCH command */
806 result = imap_sendf(conn, "SEARCH %s", imap->query);
809 state(conn, IMAP_SEARCH);
814 /***********************************************************************
816 * imap_perform_logout()
818 * Performs the logout action prior to sclose() being called.
820 static CURLcode imap_perform_logout(struct connectdata *conn)
822 CURLcode result = CURLE_OK;
824 /* Send the LOGOUT command */
825 result = imap_sendf(conn, "LOGOUT");
828 state(conn, IMAP_LOGOUT);
833 /* For the initial server greeting */
834 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
838 CURLcode result = CURLE_OK;
839 struct SessionHandle *data = conn->data;
841 (void)instate; /* no use for this yet */
843 if(imapcode != 'O') {
844 failf(data, "Got unexpected imap-server response");
845 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
848 result = imap_perform_capability(conn);
853 /* For CAPABILITY responses */
854 static CURLcode imap_state_capability_resp(struct connectdata *conn,
858 CURLcode result = CURLE_OK;
859 struct SessionHandle *data = conn->data;
860 struct imap_conn *imapc = &conn->proto.imapc;
861 const char *line = data->state.buffer;
864 (void)instate; /* no use for this yet */
866 /* Do we have a untagged response? */
867 if(imapcode == '*') {
870 /* Loop through the data line */
873 (*line == ' ' || *line == '\t' ||
874 *line == '\r' || *line == '\n')) {
882 /* Extract the word */
883 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
884 line[wordlen] != '\t' && line[wordlen] != '\r' &&
885 line[wordlen] != '\n';)
888 /* Does the server support the STARTTLS capability? */
889 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
890 imapc->tls_supported = TRUE;
892 /* Has the server explicitly disabled clear text authentication? */
893 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
894 imapc->login_disabled = TRUE;
896 /* Does the server support the SASL-IR capability? */
897 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
898 imapc->ir_supported = TRUE;
900 /* Do we have a SASL based authentication mechanism? */
901 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
903 unsigned int mechbit;
908 /* Test the word for a matching authentication mechanism */
909 if((mechbit = Curl_sasl_decode_mech(line, wordlen, &llen)) &&
911 imapc->sasl.authmechs |= mechbit;
917 else if(imapcode == 'O') {
918 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
919 /* We don't have a SSL/TLS connection yet, but SSL is requested */
920 if(imapc->tls_supported)
921 /* Switch to TLS connection now */
922 result = imap_perform_starttls(conn);
923 else if(data->set.use_ssl == CURLUSESSL_TRY)
924 /* Fallback and carry on with authentication */
925 result = imap_perform_authentication(conn);
927 failf(data, "STARTTLS not supported.");
928 result = CURLE_USE_SSL_FAILED;
932 result = imap_perform_authentication(conn);
935 result = imap_perform_authentication(conn);
940 /* For STARTTLS responses */
941 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
945 CURLcode result = CURLE_OK;
946 struct SessionHandle *data = conn->data;
948 (void)instate; /* no use for this yet */
950 if(imapcode != 'O') {
951 if(data->set.use_ssl != CURLUSESSL_TRY) {
952 failf(data, "STARTTLS denied. %c", imapcode);
953 result = CURLE_USE_SSL_FAILED;
956 result = imap_perform_authentication(conn);
959 result = imap_perform_upgrade_tls(conn);
964 /* For SASL authentication responses */
965 static CURLcode imap_state_auth_resp(struct connectdata *conn,
969 CURLcode result = CURLE_OK;
970 struct SessionHandle *data = conn->data;
971 struct imap_conn *imapc = &conn->proto.imapc;
972 saslprogress progress;
974 (void)instate; /* no use for this yet */
976 result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
980 state(conn, IMAP_STOP); /* Authenticated */
982 case SASL_IDLE: /* No mechanism left after cancellation */
983 if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
984 /* Perform clear text authentication */
985 result = imap_perform_login(conn);
987 failf(data, "Authentication cancelled");
988 result = CURLE_LOGIN_DENIED;
998 /* For LOGIN responses */
999 static CURLcode imap_state_login_resp(struct connectdata *conn,
1003 CURLcode result = CURLE_OK;
1004 struct SessionHandle *data = conn->data;
1006 (void)instate; /* no use for this yet */
1008 if(imapcode != 'O') {
1009 failf(data, "Access denied. %c", imapcode);
1010 result = CURLE_LOGIN_DENIED;
1013 /* End of connect phase */
1014 state(conn, IMAP_STOP);
1019 /* For LIST responses */
1020 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1023 CURLcode result = CURLE_OK;
1024 char *line = conn->data->state.buffer;
1025 size_t len = strlen(line);
1027 (void)instate; /* No use for this yet */
1029 if(imapcode == '*') {
1030 /* Temporarily add the LF character back and send as body to the client */
1032 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1035 else if(imapcode != 'O')
1036 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1038 /* End of DO phase */
1039 state(conn, IMAP_STOP);
1044 /* For SELECT responses */
1045 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1048 CURLcode result = CURLE_OK;
1049 struct SessionHandle *data = conn->data;
1050 struct IMAP *imap = conn->data->req.protop;
1051 struct imap_conn *imapc = &conn->proto.imapc;
1052 const char *line = data->state.buffer;
1055 (void)instate; /* no use for this yet */
1057 if(imapcode == '*') {
1058 /* See if this is an UIDVALIDITY response */
1059 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1060 Curl_safefree(imapc->mailbox_uidvalidity);
1061 imapc->mailbox_uidvalidity = strdup(tmp);
1064 else if(imapcode == 'O') {
1065 /* Check if the UIDVALIDITY has been specified and matches */
1066 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1067 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1068 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1069 result = CURLE_REMOTE_FILE_NOT_FOUND;
1072 /* Note the currently opened mailbox on this connection */
1073 imapc->mailbox = strdup(imap->mailbox);
1076 result = imap_perform_list(conn);
1077 else if(imap->query)
1078 result = imap_perform_search(conn);
1080 result = imap_perform_fetch(conn);
1084 failf(data, "Select failed");
1085 result = CURLE_LOGIN_DENIED;
1091 /* For the (first line of the) FETCH responses */
1092 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1095 CURLcode result = CURLE_OK;
1096 struct SessionHandle *data = conn->data;
1097 struct imap_conn *imapc = &conn->proto.imapc;
1098 struct pingpong *pp = &imapc->pp;
1099 const char *ptr = data->state.buffer;
1100 bool parsed = FALSE;
1103 (void)instate; /* no use for this yet */
1105 if(imapcode != '*') {
1106 Curl_pgrsSetDownloadSize(data, -1);
1107 state(conn, IMAP_STOP);
1108 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1111 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1112 the continuation data contained within the curly brackets */
1113 while(*ptr && (*ptr != '{'))
1118 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1119 if(endptr - ptr > 1 && endptr[0] == '}' &&
1120 endptr[1] == '\r' && endptr[2] == '\0')
1125 infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1127 Curl_pgrsSetDownloadSize(data, size);
1130 /* At this point there is a bunch of data in the header "cache" that is
1131 actually body content, send it as body and then skip it. Do note
1132 that there may even be additional "headers" after the body. */
1133 size_t chunk = pp->cache_size;
1135 if(chunk > (size_t)size)
1136 /* The conversion from curl_off_t to size_t is always fine here */
1137 chunk = (size_t)size;
1139 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1143 data->req.bytecount += chunk;
1145 infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1146 " bytes, %" CURL_FORMAT_CURL_OFF_TU
1147 " bytes are left for transfer\n", (curl_off_t)chunk,
1150 /* Have we used the entire cache or just part of it?*/
1151 if(pp->cache_size > chunk) {
1152 /* Only part of it so shrink the cache to fit the trailing data */
1153 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1154 pp->cache_size -= chunk;
1157 /* Free the cache */
1158 Curl_safefree(pp->cache);
1160 /* Reset the cache size */
1165 if(data->req.bytecount == size)
1166 /* The entire data is already transferred! */
1167 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1170 data->req.maxdownload = size;
1171 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1175 /* We don't know how to parse this line */
1176 failf(pp->conn->data, "Failed to parse FETCH response.");
1177 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1180 /* End of DO phase */
1181 state(conn, IMAP_STOP);
1186 /* For final FETCH responses performed after the download */
1187 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1191 CURLcode result = CURLE_OK;
1193 (void)instate; /* No use for this yet */
1196 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1198 /* End of DONE phase */
1199 state(conn, IMAP_STOP);
1204 /* For APPEND responses */
1205 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1208 CURLcode result = CURLE_OK;
1209 struct SessionHandle *data = conn->data;
1211 (void)instate; /* No use for this yet */
1213 if(imapcode != '+') {
1214 result = CURLE_UPLOAD_FAILED;
1217 /* Set the progress upload size */
1218 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1221 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1223 /* End of DO phase */
1224 state(conn, IMAP_STOP);
1230 /* For final APPEND responses performed after the upload */
1231 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1235 CURLcode result = CURLE_OK;
1237 (void)instate; /* No use for this yet */
1240 result = CURLE_UPLOAD_FAILED;
1242 /* End of DONE phase */
1243 state(conn, IMAP_STOP);
1248 /* For SEARCH responses */
1249 static CURLcode imap_state_search_resp(struct connectdata *conn, int imapcode,
1252 CURLcode result = CURLE_OK;
1253 char *line = conn->data->state.buffer;
1254 size_t len = strlen(line);
1256 (void)instate; /* No use for this yet */
1258 if(imapcode == '*') {
1259 /* Temporarily add the LF character back and send as body to the client */
1261 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1264 else if(imapcode != 'O')
1265 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1267 /* End of DO phase */
1268 state(conn, IMAP_STOP);
1273 static CURLcode imap_statemach_act(struct connectdata *conn)
1275 CURLcode result = CURLE_OK;
1276 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1278 struct imap_conn *imapc = &conn->proto.imapc;
1279 struct pingpong *pp = &imapc->pp;
1282 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1283 if(imapc->state == IMAP_UPGRADETLS)
1284 return imap_perform_upgrade_tls(conn);
1286 /* Flush any data that needs to be sent */
1288 return Curl_pp_flushsend(pp);
1291 /* Read the response from the server */
1292 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1296 /* Was there an error parsing the response line? */
1298 return CURLE_FTP_WEIRD_SERVER_REPLY;
1303 /* We have now received a full IMAP server response */
1304 switch(imapc->state) {
1305 case IMAP_SERVERGREET:
1306 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1309 case IMAP_CAPABILITY:
1310 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1314 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1317 case IMAP_AUTHENTICATE:
1318 result = imap_state_auth_resp(conn, imapcode, imapc->state);
1322 result = imap_state_login_resp(conn, imapcode, imapc->state);
1326 result = imap_state_list_resp(conn, imapcode, imapc->state);
1330 result = imap_state_select_resp(conn, imapcode, imapc->state);
1334 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1337 case IMAP_FETCH_FINAL:
1338 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1342 result = imap_state_append_resp(conn, imapcode, imapc->state);
1345 case IMAP_APPEND_FINAL:
1346 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1350 result = imap_state_search_resp(conn, imapcode, imapc->state);
1354 /* fallthrough, just stop! */
1356 /* internal error */
1357 state(conn, IMAP_STOP);
1360 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1365 /* Called repeatedly until done from multi.c */
1366 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1368 CURLcode result = CURLE_OK;
1369 struct imap_conn *imapc = &conn->proto.imapc;
1371 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1372 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1373 if(result || !imapc->ssldone)
1377 result = Curl_pp_statemach(&imapc->pp, FALSE);
1378 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1383 static CURLcode imap_block_statemach(struct connectdata *conn)
1385 CURLcode result = CURLE_OK;
1386 struct imap_conn *imapc = &conn->proto.imapc;
1388 while(imapc->state != IMAP_STOP && !result)
1389 result = Curl_pp_statemach(&imapc->pp, TRUE);
1394 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1396 static CURLcode imap_init(struct connectdata *conn)
1398 CURLcode result = CURLE_OK;
1399 struct SessionHandle *data = conn->data;
1402 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1404 result = CURLE_OUT_OF_MEMORY;
1409 /* For the IMAP "protocol connect" and "doing" phases only */
1410 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1413 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1416 /***********************************************************************
1420 * This function should do everything that is to be considered a part of the
1423 * The variable 'done' points to will be TRUE if the protocol-layer connect
1424 * phase is done when this function returns, or FALSE if not.
1426 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1428 CURLcode result = CURLE_OK;
1429 struct imap_conn *imapc = &conn->proto.imapc;
1430 struct pingpong *pp = &imapc->pp;
1432 *done = FALSE; /* default to not done yet */
1434 /* We always support persistent connections in IMAP */
1435 connkeep(conn, "IMAP default");
1437 /* Set the default response time-out */
1438 pp->response_time = RESP_TIMEOUT;
1439 pp->statemach_act = imap_statemach_act;
1440 pp->endofresp = imap_endofresp;
1443 /* Set the default preferred authentication type and mechanism */
1444 imapc->preftype = IMAP_TYPE_ANY;
1445 Curl_sasl_init(&imapc->sasl, &saslimap);
1447 /* Initialise the pingpong layer */
1450 /* Parse the URL options */
1451 result = imap_parse_url_options(conn);
1455 /* Start off waiting for the server greeting response */
1456 state(conn, IMAP_SERVERGREET);
1458 /* Start off with an response id of '*' */
1459 strcpy(imapc->resptag, "*");
1461 result = imap_multi_statemach(conn, done);
1466 /***********************************************************************
1470 * The DONE function. This does what needs to be done after a single DO has
1473 * Input argument is already checked for validity.
1475 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1478 CURLcode result = CURLE_OK;
1479 struct SessionHandle *data = conn->data;
1480 struct IMAP *imap = data->req.protop;
1485 /* When the easy handle is removed from the multi interface while libcurl
1486 is still trying to resolve the host name, the IMAP struct is not yet
1487 initialized. However, the removal action calls Curl_done() which in
1488 turn calls this function, so we simply return success. */
1492 connclose(conn, "IMAP done with bad status"); /* marked for closure */
1493 result = status; /* use the already set error code */
1495 else if(!data->set.connect_only && !imap->custom &&
1496 (imap->uid || data->set.upload)) {
1497 /* Handle responses after FETCH or APPEND transfer has finished */
1498 if(!data->set.upload)
1499 state(conn, IMAP_FETCH_FINAL);
1501 /* End the APPEND command first by sending an empty line */
1502 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1504 state(conn, IMAP_APPEND_FINAL);
1507 /* Run the state-machine
1509 TODO: when the multi interface is used, this _really_ should be using
1510 the imap_multi_statemach function but we have no general support for
1511 non-blocking DONE operations, not in the multi state machine and with
1512 Curl_done() invokes on several places in the code!
1515 result = imap_block_statemach(conn);
1518 /* Cleanup our per-request based variables */
1519 Curl_safefree(imap->mailbox);
1520 Curl_safefree(imap->uidvalidity);
1521 Curl_safefree(imap->uid);
1522 Curl_safefree(imap->section);
1523 Curl_safefree(imap->partial);
1524 Curl_safefree(imap->query);
1525 Curl_safefree(imap->custom);
1526 Curl_safefree(imap->custom_params);
1528 /* Clear the transfer mode for the next request */
1529 imap->transfer = FTPTRANSFER_BODY;
1534 /***********************************************************************
1538 * This is the actual DO function for IMAP. Fetch or append a message, or do
1539 * other things according to the options previously setup.
1541 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1544 /* This is IMAP and no proxy */
1545 CURLcode result = CURLE_OK;
1546 struct SessionHandle *data = conn->data;
1547 struct IMAP *imap = data->req.protop;
1548 struct imap_conn *imapc = &conn->proto.imapc;
1549 bool selected = FALSE;
1551 DEBUGF(infof(conn->data, "DO phase starts\n"));
1553 if(conn->data->set.opt_no_body) {
1554 /* Requested no body means no transfer */
1555 imap->transfer = FTPTRANSFER_INFO;
1558 *dophase_done = FALSE; /* not done yet */
1560 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1561 has already been selected on this connection */
1562 if(imap->mailbox && imapc->mailbox &&
1563 !strcmp(imap->mailbox, imapc->mailbox) &&
1564 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1565 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1568 /* Start the first command in the DO phase */
1569 if(conn->data->set.upload)
1570 /* APPEND can be executed directly */
1571 result = imap_perform_append(conn);
1572 else if(imap->custom && (selected || !imap->mailbox))
1573 /* Custom command using the same mailbox or no mailbox */
1574 result = imap_perform_list(conn);
1575 else if(!imap->custom && selected && imap->uid)
1576 /* FETCH from the same mailbox */
1577 result = imap_perform_fetch(conn);
1578 else if(!imap->custom && selected && imap->query)
1579 /* SEARCH the current mailbox */
1580 result = imap_perform_search(conn);
1581 else if(imap->mailbox && !selected &&
1582 (imap->custom || imap->uid || imap->query))
1583 /* SELECT the mailbox */
1584 result = imap_perform_select(conn);
1587 result = imap_perform_list(conn);
1592 /* Run the state-machine */
1593 result = imap_multi_statemach(conn, dophase_done);
1595 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1598 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1603 /***********************************************************************
1607 * This function is registered as 'curl_do' function. It decodes the path
1608 * parts etc as a wrapper to the actual DO function (imap_perform).
1610 * The input argument is already checked for validity.
1612 static CURLcode imap_do(struct connectdata *conn, bool *done)
1614 CURLcode result = CURLE_OK;
1616 *done = FALSE; /* default to false */
1618 /* Parse the URL path */
1619 result = imap_parse_url_path(conn);
1623 /* Parse the custom request */
1624 result = imap_parse_custom_request(conn);
1628 result = imap_regular_transfer(conn, done);
1633 /***********************************************************************
1637 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1638 * resources. BLOCKING.
1640 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1642 struct imap_conn *imapc = &conn->proto.imapc;
1644 /* We cannot send quit unconditionally. If this connection is stale or
1645 bad in any way, sending quit and waiting around here will make the
1646 disconnect wait in vain and cause more problems than we need to. */
1648 /* The IMAP session may or may not have been allocated/setup at this
1650 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
1651 if(!imap_perform_logout(conn))
1652 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1654 /* Disconnect from the server */
1655 Curl_pp_disconnect(&imapc->pp);
1657 /* Cleanup the SASL module */
1658 Curl_sasl_cleanup(conn, imapc->sasl.authused);
1660 /* Cleanup our connection based variables */
1661 Curl_safefree(imapc->mailbox);
1662 Curl_safefree(imapc->mailbox_uidvalidity);
1667 /* Call this when the DO phase has completed */
1668 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1670 struct IMAP *imap = conn->data->req.protop;
1674 if(imap->transfer != FTPTRANSFER_BODY)
1675 /* no data to transfer */
1676 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1681 /* Called from multi.c while DOing */
1682 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1684 CURLcode result = imap_multi_statemach(conn, dophase_done);
1687 DEBUGF(infof(conn->data, "DO phase failed\n"));
1688 else if(*dophase_done) {
1689 result = imap_dophase_done(conn, FALSE /* not connected */);
1691 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1697 /***********************************************************************
1699 * imap_regular_transfer()
1701 * The input argument is already checked for validity.
1703 * Performs all commands done before a regular transfer between a local and a
1706 static CURLcode imap_regular_transfer(struct connectdata *conn,
1709 CURLcode result = CURLE_OK;
1710 bool connected = FALSE;
1711 struct SessionHandle *data = conn->data;
1713 /* Make sure size is unknown at this point */
1714 data->req.size = -1;
1716 /* Set the progress data */
1717 Curl_pgrsSetUploadCounter(data, 0);
1718 Curl_pgrsSetDownloadCounter(data, 0);
1719 Curl_pgrsSetUploadSize(data, -1);
1720 Curl_pgrsSetDownloadSize(data, -1);
1722 /* Carry out the perform */
1723 result = imap_perform(conn, &connected, dophase_done);
1725 /* Perform post DO phase operations if necessary */
1726 if(!result && *dophase_done)
1727 result = imap_dophase_done(conn, connected);
1732 static CURLcode imap_setup_connection(struct connectdata *conn)
1734 struct SessionHandle *data = conn->data;
1736 /* Initialise the IMAP layer */
1737 CURLcode result = imap_init(conn);
1741 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1742 /* Unless we have asked to tunnel IMAP operations through the proxy, we
1743 switch and use HTTP operations only */
1744 #ifndef CURL_DISABLE_HTTP
1745 if(conn->handler == &Curl_handler_imap)
1746 conn->handler = &Curl_handler_imap_proxy;
1749 conn->handler = &Curl_handler_imaps_proxy;
1751 failf(data, "IMAPS not supported!");
1752 return CURLE_UNSUPPORTED_PROTOCOL;
1756 /* set it up as an HTTP connection instead */
1757 return conn->handler->setup_connection(conn);
1759 failf(data, "IMAP over http proxy requires HTTP support built-in!");
1760 return CURLE_UNSUPPORTED_PROTOCOL;
1764 data->state.path++; /* don't include the initial slash */
1769 /***********************************************************************
1773 * Sends the formated string as an IMAP command to the server.
1775 * Designed to never block.
1777 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
1779 CURLcode result = CURLE_OK;
1780 struct imap_conn *imapc = &conn->proto.imapc;
1786 /* Calculate the next command ID wrapping at 3 digits */
1787 imapc->cmdid = (imapc->cmdid + 1) % 1000;
1789 /* Calculate the tag based on the connection ID and command ID */
1790 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1791 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
1793 /* Prefix the format with the tag */
1794 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
1796 return CURLE_OUT_OF_MEMORY;
1798 /* Send the data with the tag */
1800 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
1808 /***********************************************************************
1812 * Checks the input string for characters that need escaping and returns an
1813 * atom ready for sending to the server.
1815 * The returned string needs to be freed.
1818 static char *imap_atom(const char *str)
1822 size_t backsp_count = 0;
1823 size_t quote_count = 0;
1824 bool space_exists = FALSE;
1826 char *newstr = NULL;
1831 /* Count any unescaped characters */
1839 space_exists = TRUE;
1844 /* Does the input contain any unescaped characters? */
1845 if(!backsp_count && !quote_count && !space_exists)
1848 /* Calculate the new string length */
1849 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
1851 /* Allocate the new string */
1852 newstr = (char *) malloc((newlen + 1) * sizeof(char));
1856 /* Surround the string in quotes if necessary */
1860 newstr[newlen - 1] = '"';
1864 /* Copy the string, escaping backslash and quote characters along the way */
1867 if(*p1 == '\\' || *p1 == '"') {
1878 /* Terminate the string */
1879 newstr[newlen] = '\0';
1884 /***********************************************************************
1888 * Portable test of whether the specified char is a "bchar" as defined in the
1889 * grammar of RFC-5092.
1891 static bool imap_is_bchar(char ch)
1895 case ':': case '@': case '/':
1896 /* bchar -> achar */
1898 /* bchar -> achar -> uchar -> unreserved */
1899 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
1900 case '7': case '8': case '9':
1901 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
1902 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
1903 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
1904 case 'V': case 'W': case 'X': case 'Y': case 'Z':
1905 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
1906 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
1907 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
1908 case 'v': case 'w': case 'x': case 'y': case 'z':
1909 case '-': case '.': case '_': case '~':
1910 /* bchar -> achar -> uchar -> sub-delims-sh */
1911 case '!': case '$': case '\'': case '(': case ')': case '*':
1913 /* bchar -> achar -> uchar -> pct-encoded */
1914 case '%': /* HEXDIG chars are already included above */
1922 /***********************************************************************
1924 * imap_parse_url_options()
1926 * Parse the URL login options.
1928 static CURLcode imap_parse_url_options(struct connectdata *conn)
1930 CURLcode result = CURLE_OK;
1931 struct imap_conn *imapc = &conn->proto.imapc;
1932 const char *ptr = conn->options;
1934 imapc->sasl.resetprefs = TRUE;
1936 while(!result && ptr && *ptr) {
1937 const char *key = ptr;
1940 while(*ptr && *ptr != '=')
1945 while(*ptr && *ptr != ';')
1948 if(strnequal(key, "AUTH=", 5))
1949 result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
1950 value, ptr - value);
1952 result = CURLE_URL_MALFORMAT;
1958 switch(imapc->sasl.prefmech) {
1959 case SASL_AUTH_NONE:
1960 imapc->preftype = IMAP_TYPE_NONE;
1962 case SASL_AUTH_DEFAULT:
1963 imapc->preftype = IMAP_TYPE_ANY;
1966 imapc->preftype = IMAP_TYPE_SASL;
1973 /***********************************************************************
1975 * imap_parse_url_path()
1977 * Parse the URL path into separate path components.
1980 static CURLcode imap_parse_url_path(struct connectdata *conn)
1982 /* The imap struct is already initialised in imap_connect() */
1983 CURLcode result = CURLE_OK;
1984 struct SessionHandle *data = conn->data;
1985 struct IMAP *imap = data->req.protop;
1986 const char *begin = data->state.path;
1987 const char *ptr = begin;
1989 /* See how much of the URL is a valid path and decode it */
1990 while(imap_is_bchar(*ptr))
1994 /* Remove the trailing slash if present */
1995 const char *end = ptr;
1996 if(end > begin && end[-1] == '/')
1999 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2005 imap->mailbox = NULL;
2007 /* There can be any number of parameters in the form ";NAME=VALUE" */
2008 while(*ptr == ';') {
2013 /* Find the length of the name parameter */
2015 while(*ptr && *ptr != '=')
2019 return CURLE_URL_MALFORMAT;
2021 /* Decode the name parameter */
2022 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2026 /* Find the length of the value parameter */
2028 while(imap_is_bchar(*ptr))
2031 /* Decode the value parameter */
2032 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2038 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2040 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2041 PARTIAL) stripping of the trailing slash character if it is present.
2043 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2044 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2045 if(valuelen > 0 && value[valuelen - 1] == '/')
2046 value[valuelen - 1] = '\0';
2048 imap->uidvalidity = value;
2051 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2052 if(valuelen > 0 && value[valuelen - 1] == '/')
2053 value[valuelen - 1] = '\0';
2058 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2059 if(valuelen > 0 && value[valuelen - 1] == '/')
2060 value[valuelen - 1] = '\0';
2062 imap->section = value;
2065 else if(Curl_raw_equal(name, "PARTIAL") && !imap->partial) {
2066 if(valuelen > 0 && value[valuelen - 1] == '/')
2067 value[valuelen - 1] = '\0';
2069 imap->partial = value;
2076 return CURLE_URL_MALFORMAT;
2083 /* Does the URL contain a query parameter? Only valid when we have a mailbox
2084 and no UID as per RFC-5092 */
2085 if(imap->mailbox && !imap->uid && *ptr == '?') {
2086 /* Find the length of the query parameter */
2088 while(imap_is_bchar(*ptr))
2091 /* Decode the query parameter */
2092 result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2098 /* Any extra stuff at the end of the URL is an error */
2100 return CURLE_URL_MALFORMAT;
2105 /***********************************************************************
2107 * imap_parse_custom_request()
2109 * Parse the custom request.
2111 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2113 CURLcode result = CURLE_OK;
2114 struct SessionHandle *data = conn->data;
2115 struct IMAP *imap = data->req.protop;
2116 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2119 /* URL decode the custom request */
2120 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2122 /* Extract the parameters if specified */
2124 const char *params = imap->custom;
2126 while(*params && *params != ' ')
2130 imap->custom_params = strdup(params);
2131 imap->custom[params - imap->custom] = '\0';
2133 if(!imap->custom_params)
2134 result = CURLE_OUT_OF_MEMORY;
2142 #endif /* CURL_DISABLE_IMAP */