1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, 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.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC2195 CRAM-MD5 authentication
22 * RFC2595 Using TLS with IMAP, POP3 and ACAP
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3501 IMAPv4 protocol
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28 * RFC4959 IMAP Extension for SASL Initial Client Response
29 * RFC5092 IMAP URL Scheme
30 * RFC6749 OAuth 2.0 Authorization Framework
31 * RFC8314 Use of TLS for Email Submission and Access
32 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
34 ***************************************************************************/
36 #include "curl_setup.h"
38 #ifndef CURL_DISABLE_IMAP
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
47 #include <sys/utsname.h>
57 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
59 #define in_addr_t unsigned long
62 #include <curl/curl.h>
69 #include "http.h" /* for HTTP proxy tunnel stuff */
73 #include "strtoofft.h"
75 #include "vtls/vtls.h"
82 #include "curl_sasl.h"
85 /* The last 3 #include files should be in this order */
86 #include "curl_printf.h"
87 #include "curl_memory.h"
90 /* Local API functions */
91 static CURLcode imap_regular_transfer(struct Curl_easy *data, bool *done);
92 static CURLcode imap_do(struct Curl_easy *data, bool *done);
93 static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
95 static CURLcode imap_connect(struct Curl_easy *data, bool *done);
96 static CURLcode imap_disconnect(struct Curl_easy *data,
97 struct connectdata *conn, bool dead);
98 static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done);
99 static int imap_getsock(struct Curl_easy *data, struct connectdata *conn,
100 curl_socket_t *socks);
101 static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done);
102 static CURLcode imap_setup_connection(struct Curl_easy *data,
103 struct connectdata *conn);
104 static char *imap_atom(const char *str, bool escape_only);
105 static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...);
106 static CURLcode imap_parse_url_options(struct connectdata *conn);
107 static CURLcode imap_parse_url_path(struct Curl_easy *data);
108 static CURLcode imap_parse_custom_request(struct Curl_easy *data);
109 static CURLcode imap_perform_authenticate(struct Curl_easy *data,
111 const struct bufref *initresp);
112 static CURLcode imap_continue_authenticate(struct Curl_easy *data,
114 const struct bufref *resp);
115 static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
117 static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out);
120 * IMAP protocol handler.
123 const struct Curl_handler Curl_handler_imap = {
125 imap_setup_connection, /* setup_connection */
127 imap_done, /* done */
128 ZERO_NULL, /* do_more */
129 imap_connect, /* connect_it */
130 imap_multi_statemach, /* connecting */
131 imap_doing, /* doing */
132 imap_getsock, /* proto_getsock */
133 imap_getsock, /* doing_getsock */
134 ZERO_NULL, /* domore_getsock */
135 ZERO_NULL, /* perform_getsock */
136 imap_disconnect, /* disconnect */
137 ZERO_NULL, /* readwrite */
138 ZERO_NULL, /* connection_check */
139 ZERO_NULL, /* attach connection */
140 PORT_IMAP, /* defport */
141 CURLPROTO_IMAP, /* protocol */
142 CURLPROTO_IMAP, /* family */
143 PROTOPT_CLOSEACTION| /* flags */
149 * IMAPS protocol handler.
152 const struct Curl_handler Curl_handler_imaps = {
153 "IMAPS", /* scheme */
154 imap_setup_connection, /* setup_connection */
156 imap_done, /* done */
157 ZERO_NULL, /* do_more */
158 imap_connect, /* connect_it */
159 imap_multi_statemach, /* connecting */
160 imap_doing, /* doing */
161 imap_getsock, /* proto_getsock */
162 imap_getsock, /* doing_getsock */
163 ZERO_NULL, /* domore_getsock */
164 ZERO_NULL, /* perform_getsock */
165 imap_disconnect, /* disconnect */
166 ZERO_NULL, /* readwrite */
167 ZERO_NULL, /* connection_check */
168 ZERO_NULL, /* attach connection */
169 PORT_IMAPS, /* defport */
170 CURLPROTO_IMAPS, /* protocol */
171 CURLPROTO_IMAP, /* family */
172 PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
177 #define IMAP_RESP_OK 1
178 #define IMAP_RESP_NOT_OK 2
179 #define IMAP_RESP_PREAUTH 3
181 /* SASL parameters for the imap protocol */
182 static const struct SASLproto saslimap = {
183 "imap", /* The service name */
184 imap_perform_authenticate, /* Send authentication command */
185 imap_continue_authenticate, /* Send authentication continuation */
186 imap_cancel_authenticate, /* Send authentication cancellation */
187 imap_get_message, /* Get SASL response message */
188 0, /* No maximum initial response length */
189 '+', /* Code received when continuation is expected */
190 IMAP_RESP_OK, /* Code to receive upon authentication success */
191 SASL_AUTH_DEFAULT, /* Default mechanisms */
192 SASL_FLAG_BASE64 /* Configuration flags */
197 static void imap_to_imaps(struct connectdata *conn)
199 /* Change the connection handler */
200 conn->handler = &Curl_handler_imaps;
202 /* Set the connection's upgraded to TLS flag */
203 conn->bits.tls_upgraded = TRUE;
206 #define imap_to_imaps(x) Curl_nop_stmt
209 /***********************************************************************
213 * Determines whether the untagged response is related to the specified
214 * command by checking if it is in format "* <command-name> ..." or
215 * "* <number> <command-name> ...".
217 * The "* " marker is assumed to have already been checked by the caller.
219 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
221 const char *end = line + len;
222 size_t cmd_len = strlen(cmd);
224 /* Skip the untagged response marker */
227 /* Do we have a number after the marker? */
228 if(line < end && ISDIGIT(*line)) {
229 /* Skip the number */
232 while(line < end && ISDIGIT(*line));
234 /* Do we have the space character? */
235 if(line == end || *line != ' ')
241 /* Does the command name match and is it followed by a space character or at
243 if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
244 (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
250 /***********************************************************************
254 * Checks whether the given string is a valid tagged, untagged or continuation
255 * response which can be processed by the response handler.
257 static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
258 char *line, size_t len, int *resp)
260 struct IMAP *imap = data->req.p.imap;
261 struct imap_conn *imapc = &conn->proto.imapc;
262 const char *id = imapc->resptag;
263 size_t id_len = strlen(id);
265 /* Do we have a tagged command response? */
266 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
270 if(len >= 2 && !memcmp(line, "OK", 2))
271 *resp = IMAP_RESP_OK;
272 else if(len >= 7 && !memcmp(line, "PREAUTH", 7))
273 *resp = IMAP_RESP_PREAUTH;
275 *resp = IMAP_RESP_NOT_OK;
280 /* Do we have an untagged command response? */
281 if(len >= 2 && !memcmp("* ", line, 2)) {
282 switch(imapc->state) {
283 /* States which are interested in untagged responses */
284 case IMAP_CAPABILITY:
285 if(!imap_matchresp(line, len, "CAPABILITY"))
290 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
291 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
292 (!strcasecompare(imap->custom, "STORE") ||
293 !imap_matchresp(line, len, "FETCH")) &&
294 !strcasecompare(imap->custom, "SELECT") &&
295 !strcasecompare(imap->custom, "EXAMINE") &&
296 !strcasecompare(imap->custom, "SEARCH") &&
297 !strcasecompare(imap->custom, "EXPUNGE") &&
298 !strcasecompare(imap->custom, "LSUB") &&
299 !strcasecompare(imap->custom, "UID") &&
300 !strcasecompare(imap->custom, "GETQUOTAROOT") &&
301 !strcasecompare(imap->custom, "NOOP")))
306 /* SELECT is special in that its untagged responses do not have a
307 common prefix so accept anything! */
311 if(!imap_matchresp(line, len, "FETCH"))
316 if(!imap_matchresp(line, len, "SEARCH"))
320 /* Ignore other untagged responses */
329 /* Do we have a continuation response? This should be a + symbol followed by
330 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
331 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
332 some email servers ignore this and only send a single + instead. */
333 if(imap && !imap->custom && ((len == 3 && line[0] == '+') ||
334 (len >= 2 && !memcmp("+ ", line, 2)))) {
335 switch(imapc->state) {
336 /* States which are interested in continuation responses */
337 case IMAP_AUTHENTICATE:
343 failf(data, "Unexpected continuation response");
351 return FALSE; /* Nothing for us */
354 /***********************************************************************
358 * Gets the authentication message from the response buffer.
360 static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
362 char *message = data->state.buffer;
363 size_t len = strlen(message);
366 /* Find the start of the message */
368 for(message += 2; *message == ' ' || *message == '\t'; message++, len--)
371 /* Find the end of the message */
373 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
374 message[len] != '\t')
377 /* Terminate the message */
378 message[++len] = '\0';
379 Curl_bufref_set(out, message, len, NULL);
382 /* junk input => zero length output */
383 Curl_bufref_set(out, "", 0, NULL);
388 /***********************************************************************
392 * This is the ONLY way to change IMAP state!
394 static void state(struct Curl_easy *data, imapstate newstate)
396 struct imap_conn *imapc = &data->conn->proto.imapc;
397 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
398 /* for debug purposes */
399 static const char * const names[]={
418 if(imapc->state != newstate)
419 infof(data, "IMAP %p state change from %s to %s",
420 (void *)imapc, names[imapc->state], names[newstate]);
423 imapc->state = newstate;
426 /***********************************************************************
428 * imap_perform_capability()
430 * Sends the CAPABILITY command in order to obtain a list of server side
431 * supported capabilities.
433 static CURLcode imap_perform_capability(struct Curl_easy *data,
434 struct connectdata *conn)
436 CURLcode result = CURLE_OK;
437 struct imap_conn *imapc = &conn->proto.imapc;
438 imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
439 imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
440 imapc->tls_supported = FALSE; /* Clear the TLS capability */
442 /* Send the CAPABILITY command */
443 result = imap_sendf(data, "CAPABILITY");
446 state(data, IMAP_CAPABILITY);
451 /***********************************************************************
453 * imap_perform_starttls()
455 * Sends the STARTTLS command to start the upgrade to TLS.
457 static CURLcode imap_perform_starttls(struct Curl_easy *data)
459 /* Send the STARTTLS command */
460 CURLcode result = imap_sendf(data, "STARTTLS");
463 state(data, IMAP_STARTTLS);
468 /***********************************************************************
470 * imap_perform_upgrade_tls()
472 * Performs the upgrade to TLS.
474 static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
475 struct connectdata *conn)
477 /* Start the SSL connection */
478 struct imap_conn *imapc = &conn->proto.imapc;
479 CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
480 FIRSTSOCKET, &imapc->ssldone);
483 if(imapc->state != IMAP_UPGRADETLS)
484 state(data, IMAP_UPGRADETLS);
488 result = imap_perform_capability(data, conn);
495 /***********************************************************************
497 * imap_perform_login()
499 * Sends a clear text LOGIN command to authenticate with.
501 static CURLcode imap_perform_login(struct Curl_easy *data,
502 struct connectdata *conn)
504 CURLcode result = CURLE_OK;
508 /* Check we have a username and password to authenticate with and end the
509 connect phase if we don't */
510 if(!data->state.aptr.user) {
511 state(data, IMAP_STOP);
516 /* Make sure the username and password are in the correct atom format */
517 user = imap_atom(conn->user, false);
518 passwd = imap_atom(conn->passwd, false);
520 /* Send the LOGIN command */
521 result = imap_sendf(data, "LOGIN %s %s", user ? user : "",
522 passwd ? passwd : "");
528 state(data, IMAP_LOGIN);
533 /***********************************************************************
535 * imap_perform_authenticate()
537 * Sends an AUTHENTICATE command allowing the client to login with the given
538 * SASL authentication mechanism.
540 static CURLcode imap_perform_authenticate(struct Curl_easy *data,
542 const struct bufref *initresp)
544 CURLcode result = CURLE_OK;
545 const char *ir = (const char *) Curl_bufref_ptr(initresp);
548 /* Send the AUTHENTICATE command with the initial response */
549 result = imap_sendf(data, "AUTHENTICATE %s %s", mech, ir);
552 /* Send the AUTHENTICATE command */
553 result = imap_sendf(data, "AUTHENTICATE %s", mech);
559 /***********************************************************************
561 * imap_continue_authenticate()
563 * Sends SASL continuation data.
565 static CURLcode imap_continue_authenticate(struct Curl_easy *data,
567 const struct bufref *resp)
569 struct imap_conn *imapc = &data->conn->proto.imapc;
573 return Curl_pp_sendf(data, &imapc->pp,
574 "%s", (const char *) Curl_bufref_ptr(resp));
577 /***********************************************************************
579 * imap_cancel_authenticate()
581 * Sends SASL cancellation.
583 static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
586 struct imap_conn *imapc = &data->conn->proto.imapc;
590 return Curl_pp_sendf(data, &imapc->pp, "*");
593 /***********************************************************************
595 * imap_perform_authentication()
597 * Initiates the authentication sequence, with the appropriate SASL
598 * authentication mechanism, falling back to clear text should a common
599 * mechanism not be available between the client and server.
601 static CURLcode imap_perform_authentication(struct Curl_easy *data,
602 struct connectdata *conn)
604 CURLcode result = CURLE_OK;
605 struct imap_conn *imapc = &conn->proto.imapc;
606 saslprogress progress;
608 /* Check if already authenticated OR if there is enough data to authenticate
609 with and end the connect phase if we don't */
611 !Curl_sasl_can_authenticate(&imapc->sasl, data)) {
612 state(data, IMAP_STOP);
616 /* Calculate the SASL login details */
617 result = Curl_sasl_start(&imapc->sasl, data, imapc->ir_supported, &progress);
620 if(progress == SASL_INPROGRESS)
621 state(data, IMAP_AUTHENTICATE);
622 else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
623 /* Perform clear text authentication */
624 result = imap_perform_login(data, conn);
626 /* Other mechanisms not supported */
627 infof(data, "No known authentication mechanisms supported");
628 result = CURLE_LOGIN_DENIED;
635 /***********************************************************************
637 * imap_perform_list()
639 * Sends a LIST command or an alternative custom request.
641 static CURLcode imap_perform_list(struct Curl_easy *data)
643 CURLcode result = CURLE_OK;
644 struct IMAP *imap = data->req.p.imap;
647 /* Send the custom request */
648 result = imap_sendf(data, "%s%s", imap->custom,
649 imap->custom_params ? imap->custom_params : "");
651 /* Make sure the mailbox is in the correct atom format if necessary */
652 char *mailbox = imap->mailbox ? imap_atom(imap->mailbox, true)
655 return CURLE_OUT_OF_MEMORY;
657 /* Send the LIST command */
658 result = imap_sendf(data, "LIST \"%s\" *", mailbox);
664 state(data, IMAP_LIST);
669 /***********************************************************************
671 * imap_perform_select()
673 * Sends a SELECT command to ask the server to change the selected mailbox.
675 static CURLcode imap_perform_select(struct Curl_easy *data)
677 CURLcode result = CURLE_OK;
678 struct connectdata *conn = data->conn;
679 struct IMAP *imap = data->req.p.imap;
680 struct imap_conn *imapc = &conn->proto.imapc;
683 /* Invalidate old information as we are switching mailboxes */
684 Curl_safefree(imapc->mailbox);
685 Curl_safefree(imapc->mailbox_uidvalidity);
687 /* Check we have a mailbox */
689 failf(data, "Cannot SELECT without a mailbox.");
690 return CURLE_URL_MALFORMAT;
693 /* Make sure the mailbox is in the correct atom format */
694 mailbox = imap_atom(imap->mailbox, false);
696 return CURLE_OUT_OF_MEMORY;
698 /* Send the SELECT command */
699 result = imap_sendf(data, "SELECT %s", mailbox);
704 state(data, IMAP_SELECT);
709 /***********************************************************************
711 * imap_perform_fetch()
713 * Sends a FETCH command to initiate the download of a message.
715 static CURLcode imap_perform_fetch(struct Curl_easy *data)
717 CURLcode result = CURLE_OK;
718 struct IMAP *imap = data->req.p.imap;
719 /* Check we have a UID */
722 /* Send the FETCH command */
724 result = imap_sendf(data, "UID FETCH %s BODY[%s]<%s>",
725 imap->uid, imap->section ? imap->section : "",
728 result = imap_sendf(data, "UID FETCH %s BODY[%s]",
729 imap->uid, imap->section ? imap->section : "");
731 else if(imap->mindex) {
732 /* Send the FETCH command */
734 result = imap_sendf(data, "FETCH %s BODY[%s]<%s>",
735 imap->mindex, imap->section ? imap->section : "",
738 result = imap_sendf(data, "FETCH %s BODY[%s]",
739 imap->mindex, imap->section ? imap->section : "");
742 failf(data, "Cannot FETCH without a UID.");
743 return CURLE_URL_MALFORMAT;
746 state(data, 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 Curl_easy *data)
759 CURLcode result = CURLE_OK;
760 struct IMAP *imap = data->req.p.imap;
763 /* Check we have a mailbox */
765 failf(data, "Cannot APPEND without a mailbox.");
766 return CURLE_URL_MALFORMAT;
769 /* Prepare the mime data if some. */
770 if(data->set.mimepost.kind != MIMEKIND_NONE) {
771 /* Use the whole structure as data. */
772 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
774 /* Add external headers and mime version. */
775 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
776 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
777 NULL, MIMESTRATEGY_MAIL);
780 if(!Curl_checkheaders(data, STRCONST("Mime-Version")))
781 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
782 "Mime-Version: 1.0");
784 /* Make sure we will read the entire mime structure. */
786 result = Curl_mime_rewind(&data->set.mimepost);
791 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
793 /* Read from mime structure. */
794 data->state.fread_func = (curl_read_callback) Curl_mime_read;
795 data->state.in = (void *) &data->set.mimepost;
798 /* Check we know the size of the upload */
799 if(data->state.infilesize < 0) {
800 failf(data, "Cannot APPEND with unknown input file size");
801 return CURLE_UPLOAD_FAILED;
804 /* Make sure the mailbox is in the correct atom format */
805 mailbox = imap_atom(imap->mailbox, false);
807 return CURLE_OUT_OF_MEMORY;
809 /* Send the APPEND command */
810 result = imap_sendf(data,
811 "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
812 mailbox, data->state.infilesize);
817 state(data, IMAP_APPEND);
822 /***********************************************************************
824 * imap_perform_search()
826 * Sends a SEARCH command.
828 static CURLcode imap_perform_search(struct Curl_easy *data)
830 CURLcode result = CURLE_OK;
831 struct IMAP *imap = data->req.p.imap;
833 /* Check we have a query string */
835 failf(data, "Cannot SEARCH without a query string.");
836 return CURLE_URL_MALFORMAT;
839 /* Send the SEARCH command */
840 result = imap_sendf(data, "SEARCH %s", imap->query);
843 state(data, IMAP_SEARCH);
848 /***********************************************************************
850 * imap_perform_logout()
852 * Performs the logout action prior to sclose() being called.
854 static CURLcode imap_perform_logout(struct Curl_easy *data)
856 /* Send the LOGOUT command */
857 CURLcode result = imap_sendf(data, "LOGOUT");
860 state(data, IMAP_LOGOUT);
865 /* For the initial server greeting */
866 static CURLcode imap_state_servergreet_resp(struct Curl_easy *data,
870 struct connectdata *conn = data->conn;
871 (void)instate; /* no use for this yet */
873 if(imapcode == IMAP_RESP_PREAUTH) {
875 struct imap_conn *imapc = &conn->proto.imapc;
876 imapc->preauth = TRUE;
877 infof(data, "PREAUTH connection, already authenticated");
879 else if(imapcode != IMAP_RESP_OK) {
880 failf(data, "Got unexpected imap-server response");
881 return CURLE_WEIRD_SERVER_REPLY;
884 return imap_perform_capability(data, conn);
887 /* For CAPABILITY responses */
888 static CURLcode imap_state_capability_resp(struct Curl_easy *data,
892 CURLcode result = CURLE_OK;
893 struct connectdata *conn = data->conn;
894 struct imap_conn *imapc = &conn->proto.imapc;
895 const char *line = data->state.buffer;
897 (void)instate; /* no use for this yet */
899 /* Do we have a untagged response? */
900 if(imapcode == '*') {
903 /* Loop through the data line */
907 (*line == ' ' || *line == '\t' ||
908 *line == '\r' || *line == '\n')) {
916 /* Extract the word */
917 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
918 line[wordlen] != '\t' && line[wordlen] != '\r' &&
919 line[wordlen] != '\n';)
922 /* Does the server support the STARTTLS capability? */
923 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
924 imapc->tls_supported = TRUE;
926 /* Has the server explicitly disabled clear text authentication? */
927 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
928 imapc->login_disabled = TRUE;
930 /* Does the server support the SASL-IR capability? */
931 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
932 imapc->ir_supported = TRUE;
934 /* Do we have a SASL based authentication mechanism? */
935 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
937 unsigned short mechbit;
942 /* Test the word for a matching authentication mechanism */
943 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
944 if(mechbit && llen == wordlen)
945 imapc->sasl.authmechs |= mechbit;
951 else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
952 /* PREAUTH is not compatible with STARTTLS. */
953 if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
954 /* Switch to TLS connection now */
955 result = imap_perform_starttls(data);
957 else if(data->set.use_ssl <= CURLUSESSL_TRY)
958 result = imap_perform_authentication(data, conn);
960 failf(data, "STARTTLS not available.");
961 result = CURLE_USE_SSL_FAILED;
965 result = imap_perform_authentication(data, conn);
970 /* For STARTTLS responses */
971 static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
975 CURLcode result = CURLE_OK;
976 struct connectdata *conn = data->conn;
978 (void)instate; /* no use for this yet */
980 /* Pipelining in response is forbidden. */
981 if(data->conn->proto.imapc.pp.cache_size)
982 return CURLE_WEIRD_SERVER_REPLY;
984 if(imapcode != IMAP_RESP_OK) {
985 if(data->set.use_ssl != CURLUSESSL_TRY) {
986 failf(data, "STARTTLS denied");
987 result = CURLE_USE_SSL_FAILED;
990 result = imap_perform_authentication(data, conn);
993 result = imap_perform_upgrade_tls(data, conn);
998 /* For SASL authentication responses */
999 static CURLcode imap_state_auth_resp(struct Curl_easy *data,
1000 struct connectdata *conn,
1004 CURLcode result = CURLE_OK;
1005 struct imap_conn *imapc = &conn->proto.imapc;
1006 saslprogress progress;
1008 (void)instate; /* no use for this yet */
1010 result = Curl_sasl_continue(&imapc->sasl, data, imapcode, &progress);
1014 state(data, IMAP_STOP); /* Authenticated */
1016 case SASL_IDLE: /* No mechanism left after cancellation */
1017 if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
1018 /* Perform clear text authentication */
1019 result = imap_perform_login(data, conn);
1021 failf(data, "Authentication cancelled");
1022 result = CURLE_LOGIN_DENIED;
1032 /* For LOGIN responses */
1033 static CURLcode imap_state_login_resp(struct Curl_easy *data,
1037 CURLcode result = CURLE_OK;
1038 (void)instate; /* no use for this yet */
1040 if(imapcode != IMAP_RESP_OK) {
1041 failf(data, "Access denied. %c", imapcode);
1042 result = CURLE_LOGIN_DENIED;
1045 /* End of connect phase */
1046 state(data, IMAP_STOP);
1051 /* For LIST and SEARCH responses */
1052 static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
1056 CURLcode result = CURLE_OK;
1057 char *line = data->state.buffer;
1058 size_t len = strlen(line);
1060 (void)instate; /* No use for this yet */
1062 if(imapcode == '*') {
1063 /* Temporarily add the LF character back and send as body to the client */
1065 result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1);
1068 else if(imapcode != IMAP_RESP_OK)
1069 result = CURLE_QUOTE_ERROR;
1071 /* End of DO phase */
1072 state(data, IMAP_STOP);
1077 /* For SELECT responses */
1078 static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
1081 CURLcode result = CURLE_OK;
1082 struct connectdata *conn = data->conn;
1083 struct IMAP *imap = data->req.p.imap;
1084 struct imap_conn *imapc = &conn->proto.imapc;
1085 const char *line = data->state.buffer;
1087 (void)instate; /* no use for this yet */
1089 if(imapcode == '*') {
1090 /* See if this is an UIDVALIDITY response */
1092 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1093 Curl_safefree(imapc->mailbox_uidvalidity);
1094 imapc->mailbox_uidvalidity = strdup(tmp);
1097 else if(imapcode == IMAP_RESP_OK) {
1098 /* Check if the UIDVALIDITY has been specified and matches */
1099 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1100 !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1101 failf(data, "Mailbox UIDVALIDITY has changed");
1102 result = CURLE_REMOTE_FILE_NOT_FOUND;
1105 /* Note the currently opened mailbox on this connection */
1106 imapc->mailbox = strdup(imap->mailbox);
1109 result = imap_perform_list(data);
1110 else if(imap->query)
1111 result = imap_perform_search(data);
1113 result = imap_perform_fetch(data);
1117 failf(data, "Select failed");
1118 result = CURLE_LOGIN_DENIED;
1124 /* For the (first line of the) FETCH responses */
1125 static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
1126 struct connectdata *conn, int imapcode,
1129 CURLcode result = CURLE_OK;
1130 struct imap_conn *imapc = &conn->proto.imapc;
1131 struct pingpong *pp = &imapc->pp;
1132 const char *ptr = data->state.buffer;
1133 bool parsed = FALSE;
1134 curl_off_t size = 0;
1136 (void)instate; /* no use for this yet */
1138 if(imapcode != '*') {
1139 Curl_pgrsSetDownloadSize(data, -1);
1140 state(data, IMAP_STOP);
1141 return CURLE_REMOTE_FILE_NOT_FOUND;
1144 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1145 the continuation data contained within the curly brackets */
1146 while(*ptr && (*ptr != '{'))
1151 if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
1152 if(endptr - ptr > 1 && endptr[0] == '}' &&
1153 endptr[1] == '\r' && endptr[2] == '\0')
1159 infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download",
1161 Curl_pgrsSetDownloadSize(data, size);
1164 /* At this point there is a bunch of data in the header "cache" that is
1165 actually body content, send it as body and then skip it. Do note
1166 that there may even be additional "headers" after the body. */
1167 size_t chunk = pp->cache_size;
1169 if(chunk > (size_t)size)
1170 /* The conversion from curl_off_t to size_t is always fine here */
1171 chunk = (size_t)size;
1174 /* no size, we're done with the data */
1175 state(data, IMAP_STOP);
1178 result = Curl_client_write(data, CLIENTWRITE_BODY, pp->cache, chunk);
1182 data->req.bytecount += chunk;
1184 infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
1185 " bytes are left for transfer", chunk, size - chunk);
1187 /* Have we used the entire cache or just part of it?*/
1188 if(pp->cache_size > chunk) {
1189 /* Only part of it so shrink the cache to fit the trailing data */
1190 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1191 pp->cache_size -= chunk;
1194 /* Free the cache */
1195 Curl_safefree(pp->cache);
1197 /* Reset the cache size */
1202 if(data->req.bytecount == size)
1203 /* The entire data is already transferred! */
1204 Curl_setup_transfer(data, -1, -1, FALSE, -1);
1207 data->req.maxdownload = size;
1208 /* force a recv/send check of this connection, as the data might've been
1209 read off the socket already */
1210 data->conn->cselect_bits = CURL_CSELECT_IN;
1211 Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1);
1215 /* We don't know how to parse this line */
1216 failf(data, "Failed to parse FETCH response.");
1217 result = CURLE_WEIRD_SERVER_REPLY;
1220 /* End of DO phase */
1221 state(data, IMAP_STOP);
1226 /* For final FETCH responses performed after the download */
1227 static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data,
1231 CURLcode result = CURLE_OK;
1233 (void)instate; /* No use for this yet */
1235 if(imapcode != IMAP_RESP_OK)
1236 result = CURLE_WEIRD_SERVER_REPLY;
1238 /* End of DONE phase */
1239 state(data, IMAP_STOP);
1244 /* For APPEND responses */
1245 static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
1248 CURLcode result = CURLE_OK;
1249 (void)instate; /* No use for this yet */
1251 if(imapcode != '+') {
1252 result = CURLE_UPLOAD_FAILED;
1255 /* Set the progress upload size */
1256 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1259 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
1261 /* End of DO phase */
1262 state(data, IMAP_STOP);
1268 /* For final APPEND responses performed after the upload */
1269 static CURLcode imap_state_append_final_resp(struct Curl_easy *data,
1273 CURLcode result = CURLE_OK;
1275 (void)instate; /* No use for this yet */
1277 if(imapcode != IMAP_RESP_OK)
1278 result = CURLE_UPLOAD_FAILED;
1280 /* End of DONE phase */
1281 state(data, IMAP_STOP);
1286 static CURLcode imap_statemachine(struct Curl_easy *data,
1287 struct connectdata *conn)
1289 CURLcode result = CURLE_OK;
1290 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1292 struct imap_conn *imapc = &conn->proto.imapc;
1293 struct pingpong *pp = &imapc->pp;
1297 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1298 if(imapc->state == IMAP_UPGRADETLS)
1299 return imap_perform_upgrade_tls(data, conn);
1301 /* Flush any data that needs to be sent */
1303 return Curl_pp_flushsend(data, pp);
1306 /* Read the response from the server */
1307 result = Curl_pp_readresp(data, sock, pp, &imapcode, &nread);
1311 /* Was there an error parsing the response line? */
1313 return CURLE_WEIRD_SERVER_REPLY;
1318 /* We have now received a full IMAP server response */
1319 switch(imapc->state) {
1320 case IMAP_SERVERGREET:
1321 result = imap_state_servergreet_resp(data, imapcode, imapc->state);
1324 case IMAP_CAPABILITY:
1325 result = imap_state_capability_resp(data, imapcode, imapc->state);
1329 result = imap_state_starttls_resp(data, imapcode, imapc->state);
1332 case IMAP_AUTHENTICATE:
1333 result = imap_state_auth_resp(data, conn, imapcode, imapc->state);
1337 result = imap_state_login_resp(data, imapcode, imapc->state);
1342 result = imap_state_listsearch_resp(data, imapcode, imapc->state);
1346 result = imap_state_select_resp(data, imapcode, imapc->state);
1350 result = imap_state_fetch_resp(data, conn, imapcode, imapc->state);
1353 case IMAP_FETCH_FINAL:
1354 result = imap_state_fetch_final_resp(data, imapcode, imapc->state);
1358 result = imap_state_append_resp(data, imapcode, imapc->state);
1361 case IMAP_APPEND_FINAL:
1362 result = imap_state_append_final_resp(data, imapcode, imapc->state);
1366 /* fallthrough, just stop! */
1368 /* internal error */
1369 state(data, IMAP_STOP);
1372 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1377 /* Called repeatedly until done from multi.c */
1378 static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
1380 CURLcode result = CURLE_OK;
1381 struct connectdata *conn = data->conn;
1382 struct imap_conn *imapc = &conn->proto.imapc;
1384 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1385 result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
1386 FIRSTSOCKET, &imapc->ssldone);
1387 if(result || !imapc->ssldone)
1391 result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE);
1392 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1397 static CURLcode imap_block_statemach(struct Curl_easy *data,
1398 struct connectdata *conn,
1401 CURLcode result = CURLE_OK;
1402 struct imap_conn *imapc = &conn->proto.imapc;
1404 while(imapc->state != IMAP_STOP && !result)
1405 result = Curl_pp_statemach(data, &imapc->pp, TRUE, disconnecting);
1410 /* Allocate and initialize the struct IMAP for the current Curl_easy if
1412 static CURLcode imap_init(struct Curl_easy *data)
1414 CURLcode result = CURLE_OK;
1417 imap = data->req.p.imap = calloc(sizeof(struct IMAP), 1);
1419 result = CURLE_OUT_OF_MEMORY;
1424 /* For the IMAP "protocol connect" and "doing" phases only */
1425 static int imap_getsock(struct Curl_easy *data,
1426 struct connectdata *conn,
1427 curl_socket_t *socks)
1429 return Curl_pp_getsock(data, &conn->proto.imapc.pp, socks);
1432 /***********************************************************************
1436 * This function should do everything that is to be considered a part of the
1439 * The variable 'done' points to will be TRUE if the protocol-layer connect
1440 * phase is done when this function returns, or FALSE if not.
1442 static CURLcode imap_connect(struct Curl_easy *data, bool *done)
1444 CURLcode result = CURLE_OK;
1445 struct connectdata *conn = data->conn;
1446 struct imap_conn *imapc = &conn->proto.imapc;
1447 struct pingpong *pp = &imapc->pp;
1449 *done = FALSE; /* default to not done yet */
1451 /* We always support persistent connections in IMAP */
1452 connkeep(conn, "IMAP default");
1454 PINGPONG_SETUP(pp, imap_statemachine, imap_endofresp);
1456 /* Set the default preferred authentication type and mechanism */
1457 imapc->preftype = IMAP_TYPE_ANY;
1458 Curl_sasl_init(&imapc->sasl, data, &saslimap);
1460 Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
1461 /* Initialise the pingpong layer */
1463 Curl_pp_init(data, pp);
1465 /* Parse the URL options */
1466 result = imap_parse_url_options(conn);
1470 /* Start off waiting for the server greeting response */
1471 state(data, IMAP_SERVERGREET);
1473 /* Start off with an response id of '*' */
1474 strcpy(imapc->resptag, "*");
1476 result = imap_multi_statemach(data, done);
1481 /***********************************************************************
1485 * The DONE function. This does what needs to be done after a single DO has
1488 * Input argument is already checked for validity.
1490 static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
1493 CURLcode result = CURLE_OK;
1494 struct connectdata *conn = data->conn;
1495 struct IMAP *imap = data->req.p.imap;
1503 connclose(conn, "IMAP done with bad status"); /* marked for closure */
1504 result = status; /* use the already set error code */
1506 else if(!data->set.connect_only && !imap->custom &&
1507 (imap->uid || imap->mindex || data->set.upload ||
1508 data->set.mimepost.kind != MIMEKIND_NONE)) {
1509 /* Handle responses after FETCH or APPEND transfer has finished */
1511 if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
1512 state(data, IMAP_FETCH_FINAL);
1514 /* End the APPEND command first by sending an empty line */
1515 result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", "");
1517 state(data, IMAP_APPEND_FINAL);
1520 /* Run the state-machine */
1522 result = imap_block_statemach(data, conn, FALSE);
1525 /* Cleanup our per-request based variables */
1526 Curl_safefree(imap->mailbox);
1527 Curl_safefree(imap->uidvalidity);
1528 Curl_safefree(imap->uid);
1529 Curl_safefree(imap->mindex);
1530 Curl_safefree(imap->section);
1531 Curl_safefree(imap->partial);
1532 Curl_safefree(imap->query);
1533 Curl_safefree(imap->custom);
1534 Curl_safefree(imap->custom_params);
1536 /* Clear the transfer mode for the next request */
1537 imap->transfer = PPTRANSFER_BODY;
1542 /***********************************************************************
1546 * This is the actual DO function for IMAP. Fetch or append a message, or do
1547 * other things according to the options previously setup.
1549 static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
1552 /* This is IMAP and no proxy */
1553 CURLcode result = CURLE_OK;
1554 struct connectdata *conn = data->conn;
1555 struct IMAP *imap = data->req.p.imap;
1556 struct imap_conn *imapc = &conn->proto.imapc;
1557 bool selected = FALSE;
1559 DEBUGF(infof(data, "DO phase starts"));
1561 if(data->set.opt_no_body) {
1562 /* Requested no body means no transfer */
1563 imap->transfer = PPTRANSFER_INFO;
1566 *dophase_done = FALSE; /* not done yet */
1568 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1569 has already been selected on this connection */
1570 if(imap->mailbox && imapc->mailbox &&
1571 strcasecompare(imap->mailbox, imapc->mailbox) &&
1572 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1573 strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1576 /* Start the first command in the DO phase */
1577 if(data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
1578 /* APPEND can be executed directly */
1579 result = imap_perform_append(data);
1580 else if(imap->custom && (selected || !imap->mailbox))
1581 /* Custom command using the same mailbox or no mailbox */
1582 result = imap_perform_list(data);
1583 else if(!imap->custom && selected && (imap->uid || imap->mindex))
1584 /* FETCH from the same mailbox */
1585 result = imap_perform_fetch(data);
1586 else if(!imap->custom && selected && imap->query)
1587 /* SEARCH the current mailbox */
1588 result = imap_perform_search(data);
1589 else if(imap->mailbox && !selected &&
1590 (imap->custom || imap->uid || imap->mindex || imap->query))
1591 /* SELECT the mailbox */
1592 result = imap_perform_select(data);
1595 result = imap_perform_list(data);
1600 /* Run the state-machine */
1601 result = imap_multi_statemach(data, dophase_done);
1603 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1606 DEBUGF(infof(data, "DO phase is complete"));
1611 /***********************************************************************
1615 * This function is registered as 'curl_do' function. It decodes the path
1616 * parts etc as a wrapper to the actual DO function (imap_perform).
1618 * The input argument is already checked for validity.
1620 static CURLcode imap_do(struct Curl_easy *data, bool *done)
1622 CURLcode result = CURLE_OK;
1623 *done = FALSE; /* default to false */
1625 /* Parse the URL path */
1626 result = imap_parse_url_path(data);
1630 /* Parse the custom request */
1631 result = imap_parse_custom_request(data);
1635 result = imap_regular_transfer(data, done);
1640 /***********************************************************************
1644 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1645 * resources. BLOCKING.
1647 static CURLcode imap_disconnect(struct Curl_easy *data,
1648 struct connectdata *conn, bool dead_connection)
1650 struct imap_conn *imapc = &conn->proto.imapc;
1653 /* We cannot send quit unconditionally. If this connection is stale or
1654 bad in any way, sending quit and waiting around here will make the
1655 disconnect wait in vain and cause more problems than we need to. */
1657 /* The IMAP session may or may not have been allocated/setup at this
1659 if(!dead_connection && conn->bits.protoconnstart) {
1660 if(!imap_perform_logout(data))
1661 (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */
1664 /* Disconnect from the server */
1665 Curl_pp_disconnect(&imapc->pp);
1666 Curl_dyn_free(&imapc->dyn);
1668 /* Cleanup the SASL module */
1669 Curl_sasl_cleanup(conn, imapc->sasl.authused);
1671 /* Cleanup our connection based variables */
1672 Curl_safefree(imapc->mailbox);
1673 Curl_safefree(imapc->mailbox_uidvalidity);
1678 /* Call this when the DO phase has completed */
1679 static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected)
1681 struct IMAP *imap = data->req.p.imap;
1685 if(imap->transfer != PPTRANSFER_BODY)
1686 /* no data to transfer */
1687 Curl_setup_transfer(data, -1, -1, FALSE, -1);
1692 /* Called from multi.c while DOing */
1693 static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done)
1695 CURLcode result = imap_multi_statemach(data, dophase_done);
1698 DEBUGF(infof(data, "DO phase failed"));
1699 else if(*dophase_done) {
1700 result = imap_dophase_done(data, FALSE /* not connected */);
1702 DEBUGF(infof(data, "DO phase is complete"));
1708 /***********************************************************************
1710 * imap_regular_transfer()
1712 * The input argument is already checked for validity.
1714 * Performs all commands done before a regular transfer between a local and a
1717 static CURLcode imap_regular_transfer(struct Curl_easy *data,
1720 CURLcode result = CURLE_OK;
1721 bool connected = FALSE;
1723 /* Make sure size is unknown at this point */
1724 data->req.size = -1;
1726 /* Set the progress data */
1727 Curl_pgrsSetUploadCounter(data, 0);
1728 Curl_pgrsSetDownloadCounter(data, 0);
1729 Curl_pgrsSetUploadSize(data, -1);
1730 Curl_pgrsSetDownloadSize(data, -1);
1732 /* Carry out the perform */
1733 result = imap_perform(data, &connected, dophase_done);
1735 /* Perform post DO phase operations if necessary */
1736 if(!result && *dophase_done)
1737 result = imap_dophase_done(data, connected);
1742 static CURLcode imap_setup_connection(struct Curl_easy *data,
1743 struct connectdata *conn)
1745 /* Initialise the IMAP layer */
1746 CURLcode result = imap_init(data);
1750 /* Clear the TLS upgraded flag */
1751 conn->bits.tls_upgraded = FALSE;
1756 /***********************************************************************
1760 * Sends the formatted string as an IMAP command to the server.
1762 * Designed to never block.
1764 static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
1766 CURLcode result = CURLE_OK;
1767 struct imap_conn *imapc = &data->conn->proto.imapc;
1771 /* Calculate the tag based on the connection ID and command ID */
1772 msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1773 'A' + curlx_sltosi(data->conn->connection_id % 26),
1774 (++imapc->cmdid)%1000);
1776 /* start with a blank buffer */
1777 Curl_dyn_reset(&imapc->dyn);
1779 /* append tag + space + fmt */
1780 result = Curl_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt);
1784 result = Curl_pp_vsendf(data, &imapc->pp, Curl_dyn_ptr(&imapc->dyn), ap);
1790 /***********************************************************************
1794 * Checks the input string for characters that need escaping and returns an
1795 * atom ready for sending to the server.
1797 * The returned string needs to be freed.
1800 static char *imap_atom(const char *str, bool escape_only)
1802 /* !checksrc! disable PARENBRACE 1 */
1803 const char atom_specials[] = "(){ %*]";
1806 size_t backsp_count = 0;
1807 size_t quote_count = 0;
1808 bool others_exists = FALSE;
1810 char *newstr = NULL;
1815 /* Look for "atom-specials", counting the backslash and quote characters as
1816 these will need escaping */
1823 else if(!escape_only) {
1824 const char *p3 = atom_specials;
1826 while(*p3 && !others_exists) {
1828 others_exists = TRUE;
1837 /* Does the input contain any "atom-special" characters? */
1838 if(!backsp_count && !quote_count && !others_exists)
1841 /* Calculate the new string length */
1842 newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2);
1844 /* Allocate the new string */
1845 newstr = (char *) malloc((newlen + 1) * sizeof(char));
1849 /* Surround the string in quotes if necessary */
1853 newstr[newlen - 1] = '"';
1857 /* Copy the string, escaping backslash and quote characters along the way */
1860 if(*p1 == '\\' || *p1 == '"') {
1871 /* Terminate the string */
1872 newstr[newlen] = '\0';
1877 /***********************************************************************
1881 * Portable test of whether the specified char is a "bchar" as defined in the
1882 * grammar of RFC-5092.
1884 static bool imap_is_bchar(char ch)
1888 case ':': case '@': case '/':
1889 /* bchar -> achar */
1891 /* bchar -> achar -> uchar -> unreserved */
1892 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
1893 case '7': case '8': case '9':
1894 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
1895 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
1896 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
1897 case 'V': case 'W': case 'X': case 'Y': case 'Z':
1898 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
1899 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
1900 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
1901 case 'v': case 'w': case 'x': case 'y': case 'z':
1902 case '-': case '.': case '_': case '~':
1903 /* bchar -> achar -> uchar -> sub-delims-sh */
1904 case '!': case '$': case '\'': case '(': case ')': case '*':
1906 /* bchar -> achar -> uchar -> pct-encoded */
1907 case '%': /* HEXDIG chars are already included above */
1915 /***********************************************************************
1917 * imap_parse_url_options()
1919 * Parse the URL login options.
1921 static CURLcode imap_parse_url_options(struct connectdata *conn)
1923 CURLcode result = CURLE_OK;
1924 struct imap_conn *imapc = &conn->proto.imapc;
1925 const char *ptr = conn->options;
1927 while(!result && ptr && *ptr) {
1928 const char *key = ptr;
1931 while(*ptr && *ptr != '=')
1936 while(*ptr && *ptr != ';')
1939 if(strncasecompare(key, "AUTH=", 5))
1940 result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
1941 value, ptr - value);
1943 result = CURLE_URL_MALFORMAT;
1949 switch(imapc->sasl.prefmech) {
1950 case SASL_AUTH_NONE:
1951 imapc->preftype = IMAP_TYPE_NONE;
1953 case SASL_AUTH_DEFAULT:
1954 imapc->preftype = IMAP_TYPE_ANY;
1957 imapc->preftype = IMAP_TYPE_SASL;
1964 /***********************************************************************
1966 * imap_parse_url_path()
1968 * Parse the URL path into separate path components.
1971 static CURLcode imap_parse_url_path(struct Curl_easy *data)
1973 /* The imap struct is already initialised in imap_connect() */
1974 CURLcode result = CURLE_OK;
1975 struct IMAP *imap = data->req.p.imap;
1976 const char *begin = &data->state.up.path[1]; /* skip leading slash */
1977 const char *ptr = begin;
1979 /* See how much of the URL is a valid path and decode it */
1980 while(imap_is_bchar(*ptr))
1984 /* Remove the trailing slash if present */
1985 const char *end = ptr;
1986 if(end > begin && end[-1] == '/')
1989 result = Curl_urldecode(begin, end - begin, &imap->mailbox, NULL,
1995 imap->mailbox = NULL;
1997 /* There can be any number of parameters in the form ";NAME=VALUE" */
1998 while(*ptr == ';') {
2003 /* Find the length of the name parameter */
2005 while(*ptr && *ptr != '=')
2009 return CURLE_URL_MALFORMAT;
2011 /* Decode the name parameter */
2012 result = Curl_urldecode(begin, ptr - begin, &name, NULL,
2017 /* Find the length of the value parameter */
2019 while(imap_is_bchar(*ptr))
2022 /* Decode the value parameter */
2023 result = Curl_urldecode(begin, ptr - begin, &value, &valuelen,
2030 DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value));
2032 /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2033 PARTIAL) stripping of the trailing slash character if it is present.
2035 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2036 if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
2037 if(valuelen > 0 && value[valuelen - 1] == '/')
2038 value[valuelen - 1] = '\0';
2040 imap->uidvalidity = value;
2043 else if(strcasecompare(name, "UID") && !imap->uid) {
2044 if(valuelen > 0 && value[valuelen - 1] == '/')
2045 value[valuelen - 1] = '\0';
2050 else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) {
2051 if(valuelen > 0 && value[valuelen - 1] == '/')
2052 value[valuelen - 1] = '\0';
2054 imap->mindex = value;
2057 else if(strcasecompare(name, "SECTION") && !imap->section) {
2058 if(valuelen > 0 && value[valuelen - 1] == '/')
2059 value[valuelen - 1] = '\0';
2061 imap->section = value;
2064 else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
2065 if(valuelen > 0 && value[valuelen - 1] == '/')
2066 value[valuelen - 1] = '\0';
2068 imap->partial = value;
2075 return CURLE_URL_MALFORMAT;
2082 /* Does the URL contain a query parameter? Only valid when we have a mailbox
2083 and no UID as per RFC-5092 */
2084 if(imap->mailbox && !imap->uid && !imap->mindex) {
2085 /* Get the query parameter, URL decoded */
2086 (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query,
2090 /* Any extra stuff at the end of the URL is an error */
2092 return CURLE_URL_MALFORMAT;
2097 /***********************************************************************
2099 * imap_parse_custom_request()
2101 * Parse the custom request.
2103 static CURLcode imap_parse_custom_request(struct Curl_easy *data)
2105 CURLcode result = CURLE_OK;
2106 struct IMAP *imap = data->req.p.imap;
2107 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2110 /* URL decode the custom request */
2111 result = Curl_urldecode(custom, 0, &imap->custom, NULL, REJECT_CTRL);
2113 /* Extract the parameters if specified */
2115 const char *params = imap->custom;
2117 while(*params && *params != ' ')
2121 imap->custom_params = strdup(params);
2122 imap->custom[params - imap->custom] = '\0';
2124 if(!imap->custom_params)
2125 result = CURLE_OUT_OF_MEMORY;
2133 #endif /* CURL_DISABLE_IMAP */