1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC2195 CRAM-MD5 authentication
22 * RFC2595 Using TLS with IMAP, POP3 and ACAP
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3501 IMAPv4 protocol
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28 * RFC4959 IMAP Extension for SASL Initial Client Response
29 * RFC5092 IMAP URL Scheme
30 * RFC6749 OAuth 2.0 Authorization Framework
31 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
33 ***************************************************************************/
35 #include "curl_setup.h"
37 #ifndef CURL_DISABLE_IMAP
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
46 #include <sys/utsname.h>
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
58 #define in_addr_t unsigned long
61 #include <curl/curl.h>
68 #include "http.h" /* for HTTP proxy tunnel stuff */
72 #include "strtoofft.h"
74 #include "vtls/vtls.h"
81 #include "curl_sasl.h"
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, 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 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 /* Change the connection handler */
231 conn->handler = &Curl_handler_imaps;
233 /* Set the connection's upgraded to TLS flag */
234 conn->tls_upgraded = TRUE;
237 #define imap_to_imaps(x) Curl_nop_stmt
240 /***********************************************************************
244 * Determines whether the untagged response is related to the specified
245 * command by checking if it is in format "* <command-name> ..." or
246 * "* <number> <command-name> ...".
248 * The "* " marker is assumed to have already been checked by the caller.
250 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
252 const char *end = line + len;
253 size_t cmd_len = strlen(cmd);
255 /* Skip the untagged response marker */
258 /* Do we have a number after the marker? */
259 if(line < end && ISDIGIT(*line)) {
260 /* Skip the number */
263 while(line < end && ISDIGIT(*line));
265 /* Do we have the space character? */
266 if(line == end || *line != ' ')
272 /* Does the command name match and is it followed by a space character or at
274 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
275 (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
281 /***********************************************************************
285 * Checks whether the given string is a valid tagged, untagged or continuation
286 * response which can be processed by the response handler.
288 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
291 struct IMAP *imap = conn->data->req.protop;
292 struct imap_conn *imapc = &conn->proto.imapc;
293 const char *id = imapc->resptag;
294 size_t id_len = strlen(id);
296 /* Do we have a tagged command response? */
297 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
301 if(len >= 2 && !memcmp(line, "OK", 2))
303 else if(len >= 2 && !memcmp(line, "NO", 2))
305 else if(len >= 3 && !memcmp(line, "BAD", 3))
308 failf(conn->data, "Bad tagged response");
315 /* Do we have an untagged command response? */
316 if(len >= 2 && !memcmp("* ", line, 2)) {
317 switch(imapc->state) {
318 /* States which are interested in untagged responses */
319 case IMAP_CAPABILITY:
320 if(!imap_matchresp(line, len, "CAPABILITY"))
325 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
326 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
327 (strcmp(imap->custom, "STORE") ||
328 !imap_matchresp(line, len, "FETCH")) &&
329 strcmp(imap->custom, "SELECT") &&
330 strcmp(imap->custom, "EXAMINE") &&
331 strcmp(imap->custom, "SEARCH") &&
332 strcmp(imap->custom, "EXPUNGE") &&
333 strcmp(imap->custom, "LSUB") &&
334 strcmp(imap->custom, "UID") &&
335 strcmp(imap->custom, "NOOP")))
340 /* SELECT is special in that its untagged responses do not have a
341 common prefix so accept anything! */
345 if(!imap_matchresp(line, len, "FETCH"))
350 if(!imap_matchresp(line, len, "SEARCH"))
354 /* Ignore other untagged responses */
363 /* Do we have a continuation response? This should be a + symbol followed by
364 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
365 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
366 some e-mail servers ignore this and only send a single + instead. */
367 if(!imap->custom && ((len == 3 && !memcmp("+", line, 1)) ||
368 (len >= 2 && !memcmp("+ ", line, 2)))) {
369 switch(imapc->state) {
370 /* States which are interested in continuation responses */
371 case IMAP_AUTHENTICATE:
377 failf(conn->data, "Unexpected continuation response");
385 return FALSE; /* Nothing for us */
388 /***********************************************************************
392 * Gets the authentication message from the response buffer.
394 static void imap_get_message(char *buffer, char** outptr)
397 char* message = NULL;
399 /* Find the start of the message */
400 for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
403 /* Find the end of the message */
404 for(len = strlen(message); len--;)
405 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
406 message[len] != '\t')
409 /* Terminate the message */
417 /***********************************************************************
421 * This is the ONLY way to change IMAP state!
423 static void state(struct connectdata *conn, imapstate newstate)
425 struct imap_conn *imapc = &conn->proto.imapc;
426 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
427 /* for debug purposes */
428 static const char * const names[]={
447 if(imapc->state != newstate)
448 infof(conn->data, "IMAP %p state change from %s to %s\n",
449 (void *)imapc, names[imapc->state], names[newstate]);
452 imapc->state = newstate;
455 /***********************************************************************
457 * imap_perform_capability()
459 * Sends the CAPABILITY command in order to obtain a list of server side
460 * supported capabilities.
462 static CURLcode imap_perform_capability(struct connectdata *conn)
464 CURLcode result = CURLE_OK;
465 struct imap_conn *imapc = &conn->proto.imapc;
467 imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
468 imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
469 imapc->tls_supported = FALSE; /* Clear the TLS capability */
471 /* Send the CAPABILITY command */
472 result = imap_sendf(conn, "CAPABILITY");
475 state(conn, IMAP_CAPABILITY);
480 /***********************************************************************
482 * imap_perform_starttls()
484 * Sends the STARTTLS command to start the upgrade to TLS.
486 static CURLcode imap_perform_starttls(struct connectdata *conn)
488 CURLcode result = CURLE_OK;
490 /* Send the STARTTLS command */
491 result = imap_sendf(conn, "STARTTLS");
494 state(conn, IMAP_STARTTLS);
499 /***********************************************************************
501 * imap_perform_upgrade_tls()
503 * Performs the upgrade to TLS.
505 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
507 CURLcode result = CURLE_OK;
508 struct imap_conn *imapc = &conn->proto.imapc;
510 /* Start the SSL connection */
511 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
514 if(imapc->state != IMAP_UPGRADETLS)
515 state(conn, IMAP_UPGRADETLS);
519 result = imap_perform_capability(conn);
526 /***********************************************************************
528 * imap_perform_login()
530 * Sends a clear text LOGIN command to authenticate with.
532 static CURLcode imap_perform_login(struct connectdata *conn)
534 CURLcode result = CURLE_OK;
538 /* Check we have a username and password to authenticate with and end the
539 connect phase if we don't */
540 if(!conn->bits.user_passwd) {
541 state(conn, IMAP_STOP);
546 /* Make sure the username and password are in the correct atom format */
547 user = imap_atom(conn->user, false);
548 passwd = imap_atom(conn->passwd, false);
550 /* Send the LOGIN command */
551 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
552 passwd ? passwd : "");
558 state(conn, IMAP_LOGIN);
563 /***********************************************************************
565 * imap_perform_authenticate()
567 * Sends an AUTHENTICATE command allowing the client to login with the given
568 * SASL authentication mechanism.
570 static CURLcode imap_perform_authenticate(struct connectdata *conn,
572 const char *initresp)
574 CURLcode result = CURLE_OK;
577 /* Send the AUTHENTICATE command with the initial response */
578 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
581 /* Send the AUTHENTICATE command */
582 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
588 /***********************************************************************
590 * imap_continue_authenticate()
592 * Sends SASL continuation data or cancellation.
594 static CURLcode imap_continue_authenticate(struct connectdata *conn,
597 struct imap_conn *imapc = &conn->proto.imapc;
599 return Curl_pp_sendf(&imapc->pp, "%s", resp);
602 /***********************************************************************
604 * imap_perform_authentication()
606 * Initiates the authentication sequence, with the appropriate SASL
607 * authentication mechanism, falling back to clear text should a common
608 * mechanism not be available between the client and server.
610 static CURLcode imap_perform_authentication(struct connectdata *conn)
612 CURLcode result = CURLE_OK;
613 struct imap_conn *imapc = &conn->proto.imapc;
614 saslprogress progress;
616 /* Check we have enough data to authenticate with and end the
617 connect phase if we don't */
618 if(!Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
619 state(conn, IMAP_STOP);
623 /* Calculate the SASL login details */
624 result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
627 if(progress == SASL_INPROGRESS)
628 state(conn, IMAP_AUTHENTICATE);
629 else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
630 /* Perform clear text authentication */
631 result = imap_perform_login(conn);
633 /* Other mechanisms not supported */
634 infof(conn->data, "No known authentication mechanisms supported!\n");
635 result = CURLE_LOGIN_DENIED;
642 /***********************************************************************
644 * imap_perform_list()
646 * Sends a LIST command or an alternative custom request.
648 static CURLcode imap_perform_list(struct connectdata *conn)
650 CURLcode result = CURLE_OK;
651 struct SessionHandle *data = conn->data;
652 struct IMAP *imap = data->req.protop;
656 /* Send the custom request */
657 result = imap_sendf(conn, "%s%s", imap->custom,
658 imap->custom_params ? imap->custom_params : "");
660 /* Make sure the mailbox is in the correct atom format if necessary */
661 mailbox = imap->mailbox ? imap_atom(imap->mailbox, true) : strdup("");
663 return CURLE_OUT_OF_MEMORY;
665 /* Send the LIST command */
666 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
672 state(conn, IMAP_LIST);
677 /***********************************************************************
679 * imap_perform_select()
681 * Sends a SELECT command to ask the server to change the selected mailbox.
683 static CURLcode imap_perform_select(struct connectdata *conn)
685 CURLcode result = CURLE_OK;
686 struct SessionHandle *data = conn->data;
687 struct IMAP *imap = data->req.protop;
688 struct imap_conn *imapc = &conn->proto.imapc;
691 /* Invalidate old information as we are switching mailboxes */
692 Curl_safefree(imapc->mailbox);
693 Curl_safefree(imapc->mailbox_uidvalidity);
695 /* Check we have a mailbox */
697 failf(conn->data, "Cannot SELECT without a mailbox.");
698 return CURLE_URL_MALFORMAT;
701 /* Make sure the mailbox is in the correct atom format */
702 mailbox = imap_atom(imap->mailbox, false);
704 return CURLE_OUT_OF_MEMORY;
706 /* Send the SELECT command */
707 result = imap_sendf(conn, "SELECT %s", mailbox);
712 state(conn, IMAP_SELECT);
717 /***********************************************************************
719 * imap_perform_fetch()
721 * Sends a FETCH command to initiate the download of a message.
723 static CURLcode imap_perform_fetch(struct connectdata *conn)
725 CURLcode result = CURLE_OK;
726 struct IMAP *imap = conn->data->req.protop;
728 /* Check we have a UID */
730 failf(conn->data, "Cannot FETCH without a UID.");
731 return CURLE_URL_MALFORMAT;
734 /* Send the FETCH command */
736 result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
738 imap->section ? imap->section : "",
741 result = imap_sendf(conn, "FETCH %s BODY[%s]",
743 imap->section ? imap->section : "");
746 state(conn, IMAP_FETCH);
751 /***********************************************************************
753 * imap_perform_append()
755 * Sends an APPEND command to initiate the upload of a message.
757 static CURLcode imap_perform_append(struct connectdata *conn)
759 CURLcode result = CURLE_OK;
760 struct IMAP *imap = conn->data->req.protop;
763 /* Check we have a mailbox */
765 failf(conn->data, "Cannot APPEND without a mailbox.");
766 return CURLE_URL_MALFORMAT;
769 /* Check we know the size of the upload */
770 if(conn->data->state.infilesize < 0) {
771 failf(conn->data, "Cannot APPEND with unknown input file size\n");
772 return CURLE_UPLOAD_FAILED;
775 /* Make sure the mailbox is in the correct atom format */
776 mailbox = imap_atom(imap->mailbox, false);
778 return CURLE_OUT_OF_MEMORY;
780 /* Send the APPEND command */
781 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
782 mailbox, conn->data->state.infilesize);
787 state(conn, IMAP_APPEND);
792 /***********************************************************************
794 * imap_perform_search()
796 * Sends a SEARCH command.
798 static CURLcode imap_perform_search(struct connectdata *conn)
800 CURLcode result = CURLE_OK;
801 struct IMAP *imap = conn->data->req.protop;
803 /* Check we have a query string */
805 failf(conn->data, "Cannot SEARCH without a query string.");
806 return CURLE_URL_MALFORMAT;
809 /* Send the SEARCH command */
810 result = imap_sendf(conn, "SEARCH %s", imap->query);
813 state(conn, IMAP_SEARCH);
818 /***********************************************************************
820 * imap_perform_logout()
822 * Performs the logout action prior to sclose() being called.
824 static CURLcode imap_perform_logout(struct connectdata *conn)
826 CURLcode result = CURLE_OK;
828 /* Send the LOGOUT command */
829 result = imap_sendf(conn, "LOGOUT");
832 state(conn, IMAP_LOGOUT);
837 /* For the initial server greeting */
838 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
842 CURLcode result = CURLE_OK;
843 struct SessionHandle *data = conn->data;
845 (void)instate; /* no use for this yet */
847 if(imapcode != 'O') {
848 failf(data, "Got unexpected imap-server response");
849 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
852 result = imap_perform_capability(conn);
857 /* For CAPABILITY responses */
858 static CURLcode imap_state_capability_resp(struct connectdata *conn,
862 CURLcode result = CURLE_OK;
863 struct SessionHandle *data = conn->data;
864 struct imap_conn *imapc = &conn->proto.imapc;
865 const char *line = data->state.buffer;
868 (void)instate; /* no use for this yet */
870 /* Do we have a untagged response? */
871 if(imapcode == '*') {
874 /* Loop through the data line */
877 (*line == ' ' || *line == '\t' ||
878 *line == '\r' || *line == '\n')) {
886 /* Extract the word */
887 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
888 line[wordlen] != '\t' && line[wordlen] != '\r' &&
889 line[wordlen] != '\n';)
892 /* Does the server support the STARTTLS capability? */
893 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
894 imapc->tls_supported = TRUE;
896 /* Has the server explicitly disabled clear text authentication? */
897 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
898 imapc->login_disabled = TRUE;
900 /* Does the server support the SASL-IR capability? */
901 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
902 imapc->ir_supported = TRUE;
904 /* Do we have a SASL based authentication mechanism? */
905 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
907 unsigned int mechbit;
912 /* Test the word for a matching authentication mechanism */
913 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
914 if(mechbit && llen == wordlen)
915 imapc->sasl.authmechs |= mechbit;
921 else if(imapcode == 'O') {
922 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
923 /* We don't have a SSL/TLS connection yet, but SSL is requested */
924 if(imapc->tls_supported)
925 /* Switch to TLS connection now */
926 result = imap_perform_starttls(conn);
927 else if(data->set.use_ssl == CURLUSESSL_TRY)
928 /* Fallback and carry on with authentication */
929 result = imap_perform_authentication(conn);
931 failf(data, "STARTTLS not supported.");
932 result = CURLE_USE_SSL_FAILED;
936 result = imap_perform_authentication(conn);
939 result = imap_perform_authentication(conn);
944 /* For STARTTLS responses */
945 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
949 CURLcode result = CURLE_OK;
950 struct SessionHandle *data = conn->data;
952 (void)instate; /* no use for this yet */
954 if(imapcode != 'O') {
955 if(data->set.use_ssl != CURLUSESSL_TRY) {
956 failf(data, "STARTTLS denied. %c", imapcode);
957 result = CURLE_USE_SSL_FAILED;
960 result = imap_perform_authentication(conn);
963 result = imap_perform_upgrade_tls(conn);
968 /* For SASL authentication responses */
969 static CURLcode imap_state_auth_resp(struct connectdata *conn,
973 CURLcode result = CURLE_OK;
974 struct SessionHandle *data = conn->data;
975 struct imap_conn *imapc = &conn->proto.imapc;
976 saslprogress progress;
978 (void)instate; /* no use for this yet */
980 result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
984 state(conn, IMAP_STOP); /* Authenticated */
986 case SASL_IDLE: /* No mechanism left after cancellation */
987 if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
988 /* Perform clear text authentication */
989 result = imap_perform_login(conn);
991 failf(data, "Authentication cancelled");
992 result = CURLE_LOGIN_DENIED;
1002 /* For LOGIN responses */
1003 static CURLcode imap_state_login_resp(struct connectdata *conn,
1007 CURLcode result = CURLE_OK;
1008 struct SessionHandle *data = conn->data;
1010 (void)instate; /* no use for this yet */
1012 if(imapcode != 'O') {
1013 failf(data, "Access denied. %c", imapcode);
1014 result = CURLE_LOGIN_DENIED;
1017 /* End of connect phase */
1018 state(conn, IMAP_STOP);
1023 /* For LIST responses */
1024 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1027 CURLcode result = CURLE_OK;
1028 char *line = conn->data->state.buffer;
1029 size_t len = strlen(line);
1031 (void)instate; /* No use for this yet */
1033 if(imapcode == '*') {
1034 /* Temporarily add the LF character back and send as body to the client */
1036 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1039 else if(imapcode != 'O')
1040 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1042 /* End of DO phase */
1043 state(conn, IMAP_STOP);
1048 /* For SELECT responses */
1049 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1052 CURLcode result = CURLE_OK;
1053 struct SessionHandle *data = conn->data;
1054 struct IMAP *imap = conn->data->req.protop;
1055 struct imap_conn *imapc = &conn->proto.imapc;
1056 const char *line = data->state.buffer;
1059 (void)instate; /* no use for this yet */
1061 if(imapcode == '*') {
1062 /* See if this is an UIDVALIDITY response */
1063 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1064 Curl_safefree(imapc->mailbox_uidvalidity);
1065 imapc->mailbox_uidvalidity = strdup(tmp);
1068 else if(imapcode == 'O') {
1069 /* Check if the UIDVALIDITY has been specified and matches */
1070 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1071 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1072 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1073 result = CURLE_REMOTE_FILE_NOT_FOUND;
1076 /* Note the currently opened mailbox on this connection */
1077 imapc->mailbox = strdup(imap->mailbox);
1080 result = imap_perform_list(conn);
1081 else if(imap->query)
1082 result = imap_perform_search(conn);
1084 result = imap_perform_fetch(conn);
1088 failf(data, "Select failed");
1089 result = CURLE_LOGIN_DENIED;
1095 /* For the (first line of the) FETCH responses */
1096 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1099 CURLcode result = CURLE_OK;
1100 struct SessionHandle *data = conn->data;
1101 struct imap_conn *imapc = &conn->proto.imapc;
1102 struct pingpong *pp = &imapc->pp;
1103 const char *ptr = data->state.buffer;
1104 bool parsed = FALSE;
1105 curl_off_t size = 0;
1107 (void)instate; /* no use for this yet */
1109 if(imapcode != '*') {
1110 Curl_pgrsSetDownloadSize(data, -1);
1111 state(conn, IMAP_STOP);
1112 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1115 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1116 the continuation data contained within the curly brackets */
1117 while(*ptr && (*ptr != '{'))
1122 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1123 if(endptr - ptr > 1 && endptr[0] == '}' &&
1124 endptr[1] == '\r' && endptr[2] == '\0')
1129 infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1131 Curl_pgrsSetDownloadSize(data, size);
1134 /* At this point there is a bunch of data in the header "cache" that is
1135 actually body content, send it as body and then skip it. Do note
1136 that there may even be additional "headers" after the body. */
1137 size_t chunk = pp->cache_size;
1139 if(chunk > (size_t)size)
1140 /* The conversion from curl_off_t to size_t is always fine here */
1141 chunk = (size_t)size;
1143 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1147 data->req.bytecount += chunk;
1149 infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1150 " bytes, %" CURL_FORMAT_CURL_OFF_TU
1151 " bytes are left for transfer\n", (curl_off_t)chunk,
1154 /* Have we used the entire cache or just part of it?*/
1155 if(pp->cache_size > chunk) {
1156 /* Only part of it so shrink the cache to fit the trailing data */
1157 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1158 pp->cache_size -= chunk;
1161 /* Free the cache */
1162 Curl_safefree(pp->cache);
1164 /* Reset the cache size */
1169 if(data->req.bytecount == size)
1170 /* The entire data is already transferred! */
1171 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1174 data->req.maxdownload = size;
1175 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1179 /* We don't know how to parse this line */
1180 failf(pp->conn->data, "Failed to parse FETCH response.");
1181 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1184 /* End of DO phase */
1185 state(conn, IMAP_STOP);
1190 /* For final FETCH responses performed after the download */
1191 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1195 CURLcode result = CURLE_OK;
1197 (void)instate; /* No use for this yet */
1200 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1202 /* End of DONE phase */
1203 state(conn, IMAP_STOP);
1208 /* For APPEND responses */
1209 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1212 CURLcode result = CURLE_OK;
1213 struct SessionHandle *data = conn->data;
1215 (void)instate; /* No use for this yet */
1217 if(imapcode != '+') {
1218 result = CURLE_UPLOAD_FAILED;
1221 /* Set the progress upload size */
1222 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1225 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1227 /* End of DO phase */
1228 state(conn, IMAP_STOP);
1234 /* For final APPEND responses performed after the upload */
1235 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1239 CURLcode result = CURLE_OK;
1241 (void)instate; /* No use for this yet */
1244 result = CURLE_UPLOAD_FAILED;
1246 /* End of DONE phase */
1247 state(conn, IMAP_STOP);
1252 /* For SEARCH responses */
1253 static CURLcode imap_state_search_resp(struct connectdata *conn, int imapcode,
1256 CURLcode result = CURLE_OK;
1257 char *line = conn->data->state.buffer;
1258 size_t len = strlen(line);
1260 (void)instate; /* No use for this yet */
1262 if(imapcode == '*') {
1263 /* Temporarily add the LF character back and send as body to the client */
1265 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1268 else if(imapcode != 'O')
1269 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1271 /* End of DO phase */
1272 state(conn, IMAP_STOP);
1277 static CURLcode imap_statemach_act(struct connectdata *conn)
1279 CURLcode result = CURLE_OK;
1280 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1282 struct imap_conn *imapc = &conn->proto.imapc;
1283 struct pingpong *pp = &imapc->pp;
1286 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1287 if(imapc->state == IMAP_UPGRADETLS)
1288 return imap_perform_upgrade_tls(conn);
1290 /* Flush any data that needs to be sent */
1292 return Curl_pp_flushsend(pp);
1295 /* Read the response from the server */
1296 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1300 /* Was there an error parsing the response line? */
1302 return CURLE_FTP_WEIRD_SERVER_REPLY;
1307 /* We have now received a full IMAP server response */
1308 switch(imapc->state) {
1309 case IMAP_SERVERGREET:
1310 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1313 case IMAP_CAPABILITY:
1314 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1318 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1321 case IMAP_AUTHENTICATE:
1322 result = imap_state_auth_resp(conn, imapcode, imapc->state);
1326 result = imap_state_login_resp(conn, imapcode, imapc->state);
1330 result = imap_state_list_resp(conn, imapcode, imapc->state);
1334 result = imap_state_select_resp(conn, imapcode, imapc->state);
1338 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1341 case IMAP_FETCH_FINAL:
1342 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1346 result = imap_state_append_resp(conn, imapcode, imapc->state);
1349 case IMAP_APPEND_FINAL:
1350 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1354 result = imap_state_search_resp(conn, imapcode, imapc->state);
1358 /* fallthrough, just stop! */
1360 /* internal error */
1361 state(conn, IMAP_STOP);
1364 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1369 /* Called repeatedly until done from multi.c */
1370 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1372 CURLcode result = CURLE_OK;
1373 struct imap_conn *imapc = &conn->proto.imapc;
1375 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1376 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1377 if(result || !imapc->ssldone)
1381 result = Curl_pp_statemach(&imapc->pp, FALSE);
1382 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1387 static CURLcode imap_block_statemach(struct connectdata *conn)
1389 CURLcode result = CURLE_OK;
1390 struct imap_conn *imapc = &conn->proto.imapc;
1392 while(imapc->state != IMAP_STOP && !result)
1393 result = Curl_pp_statemach(&imapc->pp, TRUE);
1398 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1400 static CURLcode imap_init(struct connectdata *conn)
1402 CURLcode result = CURLE_OK;
1403 struct SessionHandle *data = conn->data;
1406 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1408 result = CURLE_OUT_OF_MEMORY;
1413 /* For the IMAP "protocol connect" and "doing" phases only */
1414 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1417 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1420 /***********************************************************************
1424 * This function should do everything that is to be considered a part of the
1427 * The variable 'done' points to will be TRUE if the protocol-layer connect
1428 * phase is done when this function returns, or FALSE if not.
1430 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1432 CURLcode result = CURLE_OK;
1433 struct imap_conn *imapc = &conn->proto.imapc;
1434 struct pingpong *pp = &imapc->pp;
1436 *done = FALSE; /* default to not done yet */
1438 /* We always support persistent connections in IMAP */
1439 connkeep(conn, "IMAP default");
1441 /* Set the default response time-out */
1442 pp->response_time = RESP_TIMEOUT;
1443 pp->statemach_act = imap_statemach_act;
1444 pp->endofresp = imap_endofresp;
1447 /* Set the default preferred authentication type and mechanism */
1448 imapc->preftype = IMAP_TYPE_ANY;
1449 Curl_sasl_init(&imapc->sasl, &saslimap);
1451 /* Initialise the pingpong layer */
1454 /* Parse the URL options */
1455 result = imap_parse_url_options(conn);
1459 /* Start off waiting for the server greeting response */
1460 state(conn, IMAP_SERVERGREET);
1462 /* Start off with an response id of '*' */
1463 strcpy(imapc->resptag, "*");
1465 result = imap_multi_statemach(conn, done);
1470 /***********************************************************************
1474 * The DONE function. This does what needs to be done after a single DO has
1477 * Input argument is already checked for validity.
1479 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1482 CURLcode result = CURLE_OK;
1483 struct SessionHandle *data = conn->data;
1484 struct IMAP *imap = data->req.protop;
1489 /* When the easy handle is removed from the multi interface while libcurl
1490 is still trying to resolve the host name, the IMAP struct is not yet
1491 initialized. However, the removal action calls Curl_done() which in
1492 turn calls this function, so we simply return success. */
1496 connclose(conn, "IMAP done with bad status"); /* marked for closure */
1497 result = status; /* use the already set error code */
1499 else if(!data->set.connect_only && !imap->custom &&
1500 (imap->uid || data->set.upload)) {
1501 /* Handle responses after FETCH or APPEND transfer has finished */
1502 if(!data->set.upload)
1503 state(conn, IMAP_FETCH_FINAL);
1505 /* End the APPEND command first by sending an empty line */
1506 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1508 state(conn, IMAP_APPEND_FINAL);
1511 /* Run the state-machine
1513 TODO: when the multi interface is used, this _really_ should be using
1514 the imap_multi_statemach function but we have no general support for
1515 non-blocking DONE operations, not in the multi state machine and with
1516 Curl_done() invokes on several places in the code!
1519 result = imap_block_statemach(conn);
1522 /* Cleanup our per-request based variables */
1523 Curl_safefree(imap->mailbox);
1524 Curl_safefree(imap->uidvalidity);
1525 Curl_safefree(imap->uid);
1526 Curl_safefree(imap->section);
1527 Curl_safefree(imap->partial);
1528 Curl_safefree(imap->query);
1529 Curl_safefree(imap->custom);
1530 Curl_safefree(imap->custom_params);
1532 /* Clear the transfer mode for the next request */
1533 imap->transfer = FTPTRANSFER_BODY;
1538 /***********************************************************************
1542 * This is the actual DO function for IMAP. Fetch or append a message, or do
1543 * other things according to the options previously setup.
1545 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1548 /* This is IMAP and no proxy */
1549 CURLcode result = CURLE_OK;
1550 struct SessionHandle *data = conn->data;
1551 struct IMAP *imap = data->req.protop;
1552 struct imap_conn *imapc = &conn->proto.imapc;
1553 bool selected = FALSE;
1555 DEBUGF(infof(conn->data, "DO phase starts\n"));
1557 if(conn->data->set.opt_no_body) {
1558 /* Requested no body means no transfer */
1559 imap->transfer = FTPTRANSFER_INFO;
1562 *dophase_done = FALSE; /* not done yet */
1564 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1565 has already been selected on this connection */
1566 if(imap->mailbox && imapc->mailbox &&
1567 !strcmp(imap->mailbox, imapc->mailbox) &&
1568 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1569 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1572 /* Start the first command in the DO phase */
1573 if(conn->data->set.upload)
1574 /* APPEND can be executed directly */
1575 result = imap_perform_append(conn);
1576 else if(imap->custom && (selected || !imap->mailbox))
1577 /* Custom command using the same mailbox or no mailbox */
1578 result = imap_perform_list(conn);
1579 else if(!imap->custom && selected && imap->uid)
1580 /* FETCH from the same mailbox */
1581 result = imap_perform_fetch(conn);
1582 else if(!imap->custom && selected && imap->query)
1583 /* SEARCH the current mailbox */
1584 result = imap_perform_search(conn);
1585 else if(imap->mailbox && !selected &&
1586 (imap->custom || imap->uid || imap->query))
1587 /* SELECT the mailbox */
1588 result = imap_perform_select(conn);
1591 result = imap_perform_list(conn);
1596 /* Run the state-machine */
1597 result = imap_multi_statemach(conn, dophase_done);
1599 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1602 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1607 /***********************************************************************
1611 * This function is registered as 'curl_do' function. It decodes the path
1612 * parts etc as a wrapper to the actual DO function (imap_perform).
1614 * The input argument is already checked for validity.
1616 static CURLcode imap_do(struct connectdata *conn, bool *done)
1618 CURLcode result = CURLE_OK;
1620 *done = FALSE; /* default to false */
1622 /* Parse the URL path */
1623 result = imap_parse_url_path(conn);
1627 /* Parse the custom request */
1628 result = imap_parse_custom_request(conn);
1632 result = imap_regular_transfer(conn, done);
1637 /***********************************************************************
1641 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1642 * resources. BLOCKING.
1644 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1646 struct imap_conn *imapc = &conn->proto.imapc;
1648 /* We cannot send quit unconditionally. If this connection is stale or
1649 bad in any way, sending quit and waiting around here will make the
1650 disconnect wait in vain and cause more problems than we need to. */
1652 /* The IMAP session may or may not have been allocated/setup at this
1654 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
1655 if(!imap_perform_logout(conn))
1656 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1658 /* Disconnect from the server */
1659 Curl_pp_disconnect(&imapc->pp);
1661 /* Cleanup the SASL module */
1662 Curl_sasl_cleanup(conn, imapc->sasl.authused);
1664 /* Cleanup our connection based variables */
1665 Curl_safefree(imapc->mailbox);
1666 Curl_safefree(imapc->mailbox_uidvalidity);
1671 /* Call this when the DO phase has completed */
1672 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1674 struct IMAP *imap = conn->data->req.protop;
1678 if(imap->transfer != FTPTRANSFER_BODY)
1679 /* no data to transfer */
1680 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1685 /* Called from multi.c while DOing */
1686 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1688 CURLcode result = imap_multi_statemach(conn, dophase_done);
1691 DEBUGF(infof(conn->data, "DO phase failed\n"));
1692 else if(*dophase_done) {
1693 result = imap_dophase_done(conn, FALSE /* not connected */);
1695 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1701 /***********************************************************************
1703 * imap_regular_transfer()
1705 * The input argument is already checked for validity.
1707 * Performs all commands done before a regular transfer between a local and a
1710 static CURLcode imap_regular_transfer(struct connectdata *conn,
1713 CURLcode result = CURLE_OK;
1714 bool connected = FALSE;
1715 struct SessionHandle *data = conn->data;
1717 /* Make sure size is unknown at this point */
1718 data->req.size = -1;
1720 /* Set the progress data */
1721 Curl_pgrsSetUploadCounter(data, 0);
1722 Curl_pgrsSetDownloadCounter(data, 0);
1723 Curl_pgrsSetUploadSize(data, -1);
1724 Curl_pgrsSetDownloadSize(data, -1);
1726 /* Carry out the perform */
1727 result = imap_perform(conn, &connected, dophase_done);
1729 /* Perform post DO phase operations if necessary */
1730 if(!result && *dophase_done)
1731 result = imap_dophase_done(conn, connected);
1736 static CURLcode imap_setup_connection(struct connectdata *conn)
1738 struct SessionHandle *data = conn->data;
1740 /* Initialise the IMAP layer */
1741 CURLcode result = imap_init(conn);
1745 /* Clear the TLS upgraded flag */
1746 conn->tls_upgraded = FALSE;
1748 /* Set up the proxy if necessary */
1749 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1750 /* Unless we have asked to tunnel IMAP operations through the proxy, we
1751 switch and use HTTP operations only */
1752 #ifndef CURL_DISABLE_HTTP
1753 if(conn->handler == &Curl_handler_imap)
1754 conn->handler = &Curl_handler_imap_proxy;
1757 conn->handler = &Curl_handler_imaps_proxy;
1759 failf(data, "IMAPS not supported!");
1760 return CURLE_UNSUPPORTED_PROTOCOL;
1764 /* set it up as an HTTP connection instead */
1765 return conn->handler->setup_connection(conn);
1767 failf(data, "IMAP over http proxy requires HTTP support built-in!");
1768 return CURLE_UNSUPPORTED_PROTOCOL;
1772 data->state.path++; /* don't include the initial slash */
1777 /***********************************************************************
1781 * Sends the formated string as an IMAP command to the server.
1783 * Designed to never block.
1785 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
1787 CURLcode result = CURLE_OK;
1788 struct imap_conn *imapc = &conn->proto.imapc;
1794 /* Calculate the next command ID wrapping at 3 digits */
1795 imapc->cmdid = (imapc->cmdid + 1) % 1000;
1797 /* Calculate the tag based on the connection ID and command ID */
1798 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1799 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
1801 /* Prefix the format with the tag */
1802 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
1804 return CURLE_OUT_OF_MEMORY;
1806 /* Send the data with the tag */
1808 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
1816 /***********************************************************************
1820 * Checks the input string for characters that need escaping and returns an
1821 * atom ready for sending to the server.
1823 * The returned string needs to be freed.
1826 static char *imap_atom(const char *str, bool escape_only)
1828 const char atom_specials[] = "(){ %*]";
1831 size_t backsp_count = 0;
1832 size_t quote_count = 0;
1833 bool others_exists = FALSE;
1835 char *newstr = NULL;
1840 /* Look for "atom-specials", counting the backslash and quote characters as
1841 these will need escapping */
1848 else if(!escape_only) {
1849 const char *p3 = atom_specials;
1851 while(*p3 && !others_exists) {
1853 others_exists = TRUE;
1862 /* Does the input contain any "atom-special" characters? */
1863 if(!backsp_count && !quote_count && !others_exists)
1866 /* Calculate the new string length */
1867 newlen = strlen(str) + backsp_count + quote_count + (others_exists ? 2 : 0);
1869 /* Allocate the new string */
1870 newstr = (char *) malloc((newlen + 1) * sizeof(char));
1874 /* Surround the string in quotes if necessary */
1878 newstr[newlen - 1] = '"';
1882 /* Copy the string, escaping backslash and quote characters along the way */
1885 if(*p1 == '\\' || *p1 == '"') {
1896 /* Terminate the string */
1897 newstr[newlen] = '\0';
1902 /***********************************************************************
1906 * Portable test of whether the specified char is a "bchar" as defined in the
1907 * grammar of RFC-5092.
1909 static bool imap_is_bchar(char ch)
1913 case ':': case '@': case '/':
1914 /* bchar -> achar */
1916 /* bchar -> achar -> uchar -> unreserved */
1917 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
1918 case '7': case '8': case '9':
1919 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
1920 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
1921 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
1922 case 'V': case 'W': case 'X': case 'Y': case 'Z':
1923 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
1924 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
1925 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
1926 case 'v': case 'w': case 'x': case 'y': case 'z':
1927 case '-': case '.': case '_': case '~':
1928 /* bchar -> achar -> uchar -> sub-delims-sh */
1929 case '!': case '$': case '\'': case '(': case ')': case '*':
1931 /* bchar -> achar -> uchar -> pct-encoded */
1932 case '%': /* HEXDIG chars are already included above */
1940 /***********************************************************************
1942 * imap_parse_url_options()
1944 * Parse the URL login options.
1946 static CURLcode imap_parse_url_options(struct connectdata *conn)
1948 CURLcode result = CURLE_OK;
1949 struct imap_conn *imapc = &conn->proto.imapc;
1950 const char *ptr = conn->options;
1952 imapc->sasl.resetprefs = TRUE;
1954 while(!result && ptr && *ptr) {
1955 const char *key = ptr;
1958 while(*ptr && *ptr != '=')
1963 while(*ptr && *ptr != ';')
1966 if(strnequal(key, "AUTH=", 5))
1967 result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
1968 value, ptr - value);
1970 result = CURLE_URL_MALFORMAT;
1976 switch(imapc->sasl.prefmech) {
1977 case SASL_AUTH_NONE:
1978 imapc->preftype = IMAP_TYPE_NONE;
1980 case SASL_AUTH_DEFAULT:
1981 imapc->preftype = IMAP_TYPE_ANY;
1984 imapc->preftype = IMAP_TYPE_SASL;
1991 /***********************************************************************
1993 * imap_parse_url_path()
1995 * Parse the URL path into separate path components.
1998 static CURLcode imap_parse_url_path(struct connectdata *conn)
2000 /* The imap struct is already initialised in imap_connect() */
2001 CURLcode result = CURLE_OK;
2002 struct SessionHandle *data = conn->data;
2003 struct IMAP *imap = data->req.protop;
2004 const char *begin = data->state.path;
2005 const char *ptr = begin;
2007 /* See how much of the URL is a valid path and decode it */
2008 while(imap_is_bchar(*ptr))
2012 /* Remove the trailing slash if present */
2013 const char *end = ptr;
2014 if(end > begin && end[-1] == '/')
2017 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2023 imap->mailbox = NULL;
2025 /* There can be any number of parameters in the form ";NAME=VALUE" */
2026 while(*ptr == ';') {
2031 /* Find the length of the name parameter */
2033 while(*ptr && *ptr != '=')
2037 return CURLE_URL_MALFORMAT;
2039 /* Decode the name parameter */
2040 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2044 /* Find the length of the value parameter */
2046 while(imap_is_bchar(*ptr))
2049 /* Decode the value parameter */
2050 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2056 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2058 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2059 PARTIAL) stripping of the trailing slash character if it is present.
2061 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2062 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2063 if(valuelen > 0 && value[valuelen - 1] == '/')
2064 value[valuelen - 1] = '\0';
2066 imap->uidvalidity = value;
2069 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2070 if(valuelen > 0 && value[valuelen - 1] == '/')
2071 value[valuelen - 1] = '\0';
2076 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2077 if(valuelen > 0 && value[valuelen - 1] == '/')
2078 value[valuelen - 1] = '\0';
2080 imap->section = value;
2083 else if(Curl_raw_equal(name, "PARTIAL") && !imap->partial) {
2084 if(valuelen > 0 && value[valuelen - 1] == '/')
2085 value[valuelen - 1] = '\0';
2087 imap->partial = value;
2094 return CURLE_URL_MALFORMAT;
2101 /* Does the URL contain a query parameter? Only valid when we have a mailbox
2102 and no UID as per RFC-5092 */
2103 if(imap->mailbox && !imap->uid && *ptr == '?') {
2104 /* Find the length of the query parameter */
2106 while(imap_is_bchar(*ptr))
2109 /* Decode the query parameter */
2110 result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2116 /* Any extra stuff at the end of the URL is an error */
2118 return CURLE_URL_MALFORMAT;
2123 /***********************************************************************
2125 * imap_parse_custom_request()
2127 * Parse the custom request.
2129 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2131 CURLcode result = CURLE_OK;
2132 struct SessionHandle *data = conn->data;
2133 struct IMAP *imap = data->req.protop;
2134 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2137 /* URL decode the custom request */
2138 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2140 /* Extract the parameters if specified */
2142 const char *params = imap->custom;
2144 while(*params && *params != ' ')
2148 imap->custom_params = strdup(params);
2149 imap->custom[params - imap->custom] = '\0';
2151 if(!imap->custom_params)
2152 result = CURLE_OUT_OF_MEMORY;
2160 #endif /* CURL_DISABLE_IMAP */