1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC2195 CRAM-MD5 authentication
22 * RFC2595 Using TLS with IMAP, POP3 and ACAP
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3501 IMAPv4 protocol
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4959 IMAP Extension for SASL Initial Client Response
28 * RFC5092 IMAP URL Scheme
30 ***************************************************************************/
32 #include "curl_setup.h"
34 #ifndef CURL_DISABLE_IMAP
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
43 #include <sys/utsname.h>
53 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
55 #define in_addr_t unsigned long
58 #include <curl/curl.h>
66 #include "http.h" /* for HTTP proxy tunnel stuff */
70 #include "strtoofft.h"
79 #include "curl_sasl.h"
81 #define _MPRINTF_REPLACE /* use our functions only */
82 #include <curl/mprintf.h>
84 #include "curl_memory.h"
85 /* The last #include file should be: */
88 /* Local API functions */
89 static CURLcode imap_parse_url_path(struct connectdata *conn);
90 static CURLcode imap_parse_custom_request(struct connectdata *conn);
91 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
92 static CURLcode imap_do(struct connectdata *conn, bool *done);
93 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
95 static CURLcode imap_connect(struct connectdata *conn, bool *done);
96 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
97 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
98 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
100 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
101 static CURLcode imap_setup_connection(struct connectdata *conn);
104 * IMAP protocol handler.
107 const struct Curl_handler Curl_handler_imap = {
109 imap_setup_connection, /* setup_connection */
111 imap_done, /* done */
112 ZERO_NULL, /* do_more */
113 imap_connect, /* connect_it */
114 imap_multi_statemach, /* connecting */
115 imap_doing, /* doing */
116 imap_getsock, /* proto_getsock */
117 imap_getsock, /* doing_getsock */
118 ZERO_NULL, /* domore_getsock */
119 ZERO_NULL, /* perform_getsock */
120 imap_disconnect, /* disconnect */
121 ZERO_NULL, /* readwrite */
122 PORT_IMAP, /* defport */
123 CURLPROTO_IMAP, /* protocol */
124 PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
125 | PROTOPT_NOURLQUERY /* flags */
130 * IMAPS protocol handler.
133 const struct Curl_handler Curl_handler_imaps = {
134 "IMAPS", /* scheme */
135 imap_setup_connection, /* setup_connection */
137 imap_done, /* done */
138 ZERO_NULL, /* do_more */
139 imap_connect, /* connect_it */
140 imap_multi_statemach, /* connecting */
141 imap_doing, /* doing */
142 imap_getsock, /* proto_getsock */
143 imap_getsock, /* doing_getsock */
144 ZERO_NULL, /* domore_getsock */
145 ZERO_NULL, /* perform_getsock */
146 imap_disconnect, /* disconnect */
147 ZERO_NULL, /* readwrite */
148 PORT_IMAPS, /* defport */
149 CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
150 PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
151 | PROTOPT_NOURLQUERY /* flags */
155 #ifndef CURL_DISABLE_HTTP
157 * HTTP-proxyed IMAP protocol handler.
160 static const struct Curl_handler Curl_handler_imap_proxy = {
162 ZERO_NULL, /* setup_connection */
163 Curl_http, /* do_it */
164 Curl_http_done, /* done */
165 ZERO_NULL, /* do_more */
166 ZERO_NULL, /* connect_it */
167 ZERO_NULL, /* connecting */
168 ZERO_NULL, /* doing */
169 ZERO_NULL, /* proto_getsock */
170 ZERO_NULL, /* doing_getsock */
171 ZERO_NULL, /* domore_getsock */
172 ZERO_NULL, /* perform_getsock */
173 ZERO_NULL, /* disconnect */
174 ZERO_NULL, /* readwrite */
175 PORT_IMAP, /* defport */
176 CURLPROTO_HTTP, /* protocol */
177 PROTOPT_NONE /* flags */
182 * HTTP-proxyed IMAPS protocol handler.
185 static const struct Curl_handler Curl_handler_imaps_proxy = {
186 "IMAPS", /* scheme */
187 ZERO_NULL, /* setup_connection */
188 Curl_http, /* do_it */
189 Curl_http_done, /* done */
190 ZERO_NULL, /* do_more */
191 ZERO_NULL, /* connect_it */
192 ZERO_NULL, /* connecting */
193 ZERO_NULL, /* doing */
194 ZERO_NULL, /* proto_getsock */
195 ZERO_NULL, /* doing_getsock */
196 ZERO_NULL, /* domore_getsock */
197 ZERO_NULL, /* perform_getsock */
198 ZERO_NULL, /* disconnect */
199 ZERO_NULL, /* readwrite */
200 PORT_IMAPS, /* defport */
201 CURLPROTO_HTTP, /* protocol */
202 PROTOPT_NONE /* flags */
208 static void imap_to_imaps(struct connectdata *conn)
210 conn->handler = &Curl_handler_imaps;
213 #define imap_to_imaps(x) Curl_nop_stmt
216 /***********************************************************************
220 * Sends the formated string as an IMAP command to the server.
222 * Designed to never block.
224 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
226 CURLcode result = CURLE_OK;
227 struct imap_conn *imapc = &conn->proto.imapc;
232 /* Calculate the next command ID wrapping at 3 digits */
233 imapc->cmdid = (imapc->cmdid + 1) % 1000;
235 /* Calculate the tag based on the connection ID and command ID */
236 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
237 'A' + (conn->connection_id % 26), imapc->cmdid);
239 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
241 return CURLE_OUT_OF_MEMORY;
243 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
245 Curl_safefree(taggedfmt);
251 /***********************************************************************
255 * Checks the input string for characters that need escaping and returns an
256 * atom ready for sending to the server.
258 * The returned string needs to be freed.
261 static char *imap_atom(const char *str)
265 size_t backsp_count = 0;
266 size_t quote_count = 0;
267 bool space_exists = FALSE;
274 /* Count any unescapped characters */
287 /* Does the input contain any unescapped characters? */
288 if(!backsp_count && !quote_count && !space_exists)
291 /* Calculate the new string length */
292 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
294 /* Allocate the new string */
295 newstr = (char *) malloc((newlen + 1) * sizeof(char));
299 /* Surround the string in quotes if necessary */
303 newstr[newlen - 1] = '"';
307 /* Copy the string, escaping backslash and quote characters along the way */
310 if(*p1 == '\\' || *p1 == '"') {
321 /* Terminate the string */
322 newstr[newlen] = '\0';
327 /* Determines whether the untagged response is related to a specified
328 command by checking if it is in format "* <command-name> ..." or
329 "* <number> <command-name> ...". The "* " marker is assumed to have
330 already been checked by the caller. */
331 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
333 const char *end = line + len;
334 size_t cmd_len = strlen(cmd);
336 /* Skip the untagged response marker */
339 /* Do we have a number after the marker? */
340 if(line < end && ISDIGIT(*line)) {
341 /* Skip the number */
344 while(line < end && ISDIGIT(*line));
346 /* Do we have the space character? */
347 if(line == end || *line != ' ')
353 /* Does the command name match and is it followed by a space character or at
355 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
356 (line[cmd_len] == ' ' || line + cmd_len == end))
362 /* Function that checks whether the given string is a valid tagged, untagged
363 or continuation response which can be processed by the response handler. */
364 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
367 struct IMAP *imap = conn->data->state.proto.imap;
368 struct imap_conn *imapc = &conn->proto.imapc;
369 const char *id = imapc->resptag;
370 size_t id_len = strlen(id);
372 /* Do we have a tagged command response? */
373 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
377 if(len >= 2 && !memcmp(line, "OK", 2))
379 else if(len >= 2 && !memcmp(line, "NO", 2))
381 else if(len >= 3 && !memcmp(line, "BAD", 3))
384 failf(conn->data, "Bad tagged response");
391 /* Do we have an untagged command response? */
392 if(len >= 2 && !memcmp("* ", line, 2)) {
393 switch(imapc->state) {
394 /* States which are interested in untagged responses */
395 case IMAP_CAPABILITY:
396 if(!imap_matchresp(line, len, "CAPABILITY"))
401 if(!imap_matchresp(line, len, "LIST"))
406 /* SELECT is special in that its untagged responses does not have a
407 common prefix so accept anything! */
411 if(!imap_matchresp(line, len, "FETCH"))
416 /* When dealing with a custom command, we are interested in all
417 intermediate responses which match the parameter name. The
418 exceptions are SELECT and EXAMINE commands, for which no
419 filtering is (or can be easily) done. */
420 if(!imap_matchresp(line, len, imap->custom) &&
421 strcmp(imap->custom, "SELECT") &&
422 strcmp(imap->custom, "EXAMINE"))
426 /* Ignore other untagged responses */
435 /* Do we have a continuation response? */
436 if((len == 3 && !memcmp("+", line, 1)) ||
437 (len >= 2 && !memcmp("+ ", line, 2))) {
438 switch(imapc->state) {
439 /* States which are interested in continuation responses */
440 case IMAP_AUTHENTICATE_PLAIN:
441 case IMAP_AUTHENTICATE_LOGIN:
442 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
443 case IMAP_AUTHENTICATE_CRAMMD5:
444 case IMAP_AUTHENTICATE_DIGESTMD5:
445 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
446 case IMAP_AUTHENTICATE_NTLM:
447 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
448 case IMAP_AUTHENTICATE_FINAL:
454 failf(conn->data, "Unexpected continuation response");
462 return FALSE; /* Nothing for us */
465 /* This is the ONLY way to change IMAP state! */
466 static void state(struct connectdata *conn, imapstate newstate)
468 struct imap_conn *imapc = &conn->proto.imapc;
469 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
470 /* for debug purposes */
471 static const char * const names[]={
477 "AUTHENTICATE_PLAIN",
478 "AUTHENTICATE_LOGIN",
479 "AUTHENTICATE_LOGIN_PASSWD",
480 "AUTHENTICATE_CRAMMD5",
481 "AUTHENTICATE_DIGESTMD5",
482 "AUTHENTICATE_DIGESTMD5_RESP",
484 "AUTHENTICATE_NTLM_TYPE2MSG",
485 "AUTHENTICATE_FINAL",
498 if(imapc->state != newstate)
499 infof(conn->data, "IMAP %p state change from %s to %s\n",
500 imapc, names[imapc->state], names[newstate]);
503 imapc->state = newstate;
506 static CURLcode imap_state_capability(struct connectdata *conn)
508 CURLcode result = CURLE_OK;
509 struct imap_conn *imapc = &conn->proto.imapc;
511 imapc->authmechs = 0; /* No known authentication mechanisms yet */
512 imapc->authused = 0; /* Clear the authentication mechanism used */
513 imapc->tls_supported = FALSE; /* Clear the TLS capability */
515 /* Send the CAPABILITY command */
516 result = imap_sendf(conn, "CAPABILITY");
519 state(conn, IMAP_CAPABILITY);
524 static CURLcode imap_state_starttls(struct connectdata *conn)
526 CURLcode result = CURLE_OK;
528 /* Send the STARTTLS command */
529 result = imap_sendf(conn, "STARTTLS");
532 state(conn, IMAP_STARTTLS);
537 static CURLcode imap_state_upgrade_tls(struct connectdata *conn)
539 CURLcode result = CURLE_OK;
540 struct imap_conn *imapc = &conn->proto.imapc;
542 /* Start the SSL connection */
543 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
546 if(imapc->state != IMAP_UPGRADETLS)
547 state(conn, IMAP_UPGRADETLS);
551 result = imap_state_capability(conn);
558 static CURLcode imap_state_login(struct connectdata *conn)
560 CURLcode result = CURLE_OK;
564 /* Check we have a username and password to authenticate with and end the
565 connect phase if we don't */
566 if(!conn->bits.user_passwd) {
567 state(conn, IMAP_STOP);
572 /* Make sure the username and password are in the correct atom format */
573 user = imap_atom(conn->user);
574 passwd = imap_atom(conn->passwd);
576 /* Send the LOGIN command */
577 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
578 passwd ? passwd : "");
581 Curl_safefree(passwd);
584 state(conn, IMAP_LOGIN);
589 static CURLcode imap_authenticate(struct connectdata *conn)
591 CURLcode result = CURLE_OK;
592 struct imap_conn *imapc = &conn->proto.imapc;
593 const char *mech = NULL;
594 char *initresp = NULL;
596 imapstate state1 = IMAP_STOP;
597 imapstate state2 = IMAP_STOP;
599 /* Check we have a username and password to authenticate with and end the
600 connect phase if we don't */
601 if(!conn->bits.user_passwd) {
602 state(conn, IMAP_STOP);
607 /* Calculate the supported authentication mechanism by decreasing order of
609 #ifndef CURL_DISABLE_CRYPTO_AUTH
610 if(imapc->authmechs & SASL_MECH_DIGEST_MD5) {
612 state1 = IMAP_AUTHENTICATE_DIGESTMD5;
613 imapc->authused = SASL_MECH_DIGEST_MD5;
615 else if(imapc->authmechs & SASL_MECH_CRAM_MD5) {
617 state1 = IMAP_AUTHENTICATE_CRAMMD5;
618 imapc->authused = SASL_MECH_CRAM_MD5;
623 if(imapc->authmechs & SASL_MECH_NTLM) {
625 state1 = IMAP_AUTHENTICATE_NTLM;
626 state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
627 imapc->authused = SASL_MECH_NTLM;
629 if(imapc->ir_supported)
630 result = Curl_sasl_create_login_message(conn->data, conn->user,
635 if(imapc->authmechs & SASL_MECH_LOGIN) {
637 state1 = IMAP_AUTHENTICATE_LOGIN;
638 state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
639 imapc->authused = SASL_MECH_LOGIN;
641 if(imapc->ir_supported)
642 result = Curl_sasl_create_plain_message(conn->data, conn->user,
643 conn->passwd, &initresp, &len);
645 else if(imapc->authmechs & SASL_MECH_PLAIN) {
647 state1 = IMAP_AUTHENTICATE_PLAIN;
648 state2 = IMAP_AUTHENTICATE_FINAL;
649 imapc->authused = SASL_MECH_PLAIN;
651 if(imapc->ir_supported)
652 result = Curl_sasl_create_plain_message(conn->data, conn->user,
653 conn->passwd, &initresp, &len);
661 /* Perform SASL based authentication */
662 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
668 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
674 Curl_safefree(initresp);
676 else if(!imapc->login_disabled)
677 /* Perform clear text authentication */
678 result = imap_state_login(conn);
680 /* Other mechanisms not supported */
681 infof(conn->data, "No known authentication mechanisms supported!\n");
682 result = CURLE_LOGIN_DENIED;
688 /* Start the DO phase */
689 static CURLcode imap_list(struct connectdata *conn)
691 CURLcode result = CURLE_OK;
692 struct SessionHandle *data = conn->data;
693 struct IMAP *imap = data->state.proto.imap;
696 /* Make sure the mailbox is in the correct atom format */
697 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
699 return CURLE_OUT_OF_MEMORY;
701 /* Send the LIST command */
702 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
704 Curl_safefree(mailbox);
707 state(conn, IMAP_LIST);
712 static CURLcode imap_select(struct connectdata *conn)
714 CURLcode result = CURLE_OK;
715 struct SessionHandle *data = conn->data;
716 struct IMAP *imap = data->state.proto.imap;
717 struct imap_conn *imapc = &conn->proto.imapc;
720 /* Invalidate old information as we are switching mailboxes */
721 Curl_safefree(imapc->mailbox);
722 Curl_safefree(imapc->mailbox_uidvalidity);
724 /* Check we have a mailbox */
726 failf(conn->data, "Cannot SELECT without a mailbox.");
727 return CURLE_URL_MALFORMAT;
730 /* Make sure the mailbox is in the correct atom format */
731 mailbox = imap_atom(imap->mailbox);
733 return CURLE_OUT_OF_MEMORY;
735 /* Send the SELECT command */
736 result = imap_sendf(conn, "SELECT %s", mailbox);
738 Curl_safefree(mailbox);
741 state(conn, IMAP_SELECT);
746 static CURLcode imap_fetch(struct connectdata *conn)
748 CURLcode result = CURLE_OK;
749 struct IMAP *imap = conn->data->state.proto.imap;
751 /* Check we have a UID */
753 failf(conn->data, "Cannot FETCH without a UID.");
754 return CURLE_URL_MALFORMAT;
757 /* Send the FETCH command */
758 result = imap_sendf(conn, "FETCH %s BODY[%s]",
760 imap->section ? imap->section : "");
763 state(conn, IMAP_FETCH);
768 static CURLcode imap_append(struct connectdata *conn)
771 struct IMAP *imap = conn->data->state.proto.imap;
774 /* Check we have a mailbox */
776 failf(conn->data, "Cannot APPEND without a mailbox.");
777 return CURLE_URL_MALFORMAT;
780 /* Check we know the size of the upload */
781 if(conn->data->set.infilesize < 0) {
782 failf(conn->data, "Cannot APPEND with unknown input file size\n");
783 return CURLE_UPLOAD_FAILED;
786 /* Make sure the mailbox is in the correct atom format */
787 mailbox = imap_atom(imap->mailbox);
789 return CURLE_OUT_OF_MEMORY;
791 /* Send the APPEND command */
792 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
793 mailbox, conn->data->set.infilesize);
795 Curl_safefree(mailbox);
798 state(conn, IMAP_APPEND);
803 static CURLcode imap_custom(struct connectdata *conn)
805 struct IMAP *imap = conn->data->state.proto.imap;
807 /* Send the custom request */
808 CURLcode result = imap_sendf(conn, "%s%s", imap->custom,
809 imap->custom_params ? imap->custom_params : "");
812 /* We don't know how much data will be received */
813 Curl_pgrsSetDownloadSize(conn->data, -1);
815 state(conn, IMAP_CUSTOM);
821 /* For the initial server greeting */
822 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
826 CURLcode result = CURLE_OK;
827 struct SessionHandle *data = conn->data;
829 (void)instate; /* no use for this yet */
831 if(imapcode != 'O') {
832 failf(data, "Got unexpected imap-server response");
833 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
836 result = imap_state_capability(conn);
841 /* For CAPABILITY responses */
842 static CURLcode imap_state_capability_resp(struct connectdata *conn,
846 CURLcode result = CURLE_OK;
847 struct SessionHandle *data = conn->data;
848 struct imap_conn *imapc = &conn->proto.imapc;
849 const char *line = data->state.buffer;
852 (void)instate; /* no use for this yet */
854 /* Do we have a untagged response? */
855 if(imapcode == '*') {
858 /* Loop through the data line */
861 (*line == ' ' || *line == '\t' ||
862 *line == '\r' || *line == '\n')) {
870 /* Extract the word */
871 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
872 line[wordlen] != '\t' && line[wordlen] != '\r' &&
873 line[wordlen] != '\n';)
876 /* Does the server support the STARTTLS capability? */
877 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
878 imapc->tls_supported = TRUE;
880 /* Has the server explicitly disabled clear text authentication? */
881 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
882 imapc->login_disabled = TRUE;
884 /* Does the server support the SASL-IR capability? */
885 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
886 imapc->ir_supported = TRUE;
888 /* Do we have a SASL based authentication mechanism? */
889 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
893 /* Test the word for a matching authentication mechanism */
894 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
895 imapc->authmechs |= SASL_MECH_LOGIN;
896 if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
897 imapc->authmechs |= SASL_MECH_PLAIN;
898 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
899 imapc->authmechs |= SASL_MECH_CRAM_MD5;
900 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
901 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
902 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
903 imapc->authmechs |= SASL_MECH_GSSAPI;
904 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
905 imapc->authmechs |= SASL_MECH_EXTERNAL;
906 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
907 imapc->authmechs |= SASL_MECH_NTLM;
913 else if(imapcode == 'O') {
914 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
915 /* We don't have a SSL/TLS connection yet, but SSL is requested */
916 if(imapc->tls_supported)
917 /* Switch to TLS connection now */
918 result = imap_state_starttls(conn);
919 else if(data->set.use_ssl == CURLUSESSL_TRY)
920 /* Fallback and carry on with authentication */
921 result = imap_authenticate(conn);
923 failf(data, "STARTTLS not supported.");
924 result = CURLE_USE_SSL_FAILED;
928 result = imap_authenticate(conn);
931 result = imap_state_login(conn);
936 /* For STARTTLS responses */
937 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
941 CURLcode result = CURLE_OK;
942 struct SessionHandle *data = conn->data;
944 (void)instate; /* no use for this yet */
946 if(imapcode != 'O') {
947 if(data->set.use_ssl != CURLUSESSL_TRY) {
948 failf(data, "STARTTLS denied. %c", imapcode);
949 result = CURLE_USE_SSL_FAILED;
952 result = imap_authenticate(conn);
955 result = imap_state_upgrade_tls(conn);
960 /* For AUTHENTICATE PLAIN (without initial response) responses */
961 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
965 CURLcode result = CURLE_OK;
966 struct SessionHandle *data = conn->data;
968 char *plainauth = NULL;
970 (void)instate; /* no use for this yet */
972 if(imapcode != '+') {
973 failf(data, "Access denied. %c", imapcode);
974 result = CURLE_LOGIN_DENIED;
977 /* Create the authorisation message */
978 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
981 /* Send the message */
984 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
987 state(conn, IMAP_AUTHENTICATE_FINAL);
990 Curl_safefree(plainauth);
997 /* For AUTHENTICATE LOGIN (without initial response) responses */
998 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1002 CURLcode result = CURLE_OK;
1003 struct SessionHandle *data = conn->data;
1005 char *authuser = NULL;
1007 (void)instate; /* no use for this yet */
1009 if(imapcode != '+') {
1010 failf(data, "Access denied: %d", imapcode);
1011 result = CURLE_LOGIN_DENIED;
1014 /* Create the user message */
1015 result = Curl_sasl_create_login_message(data, conn->user,
1021 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1024 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1027 Curl_safefree(authuser);
1034 /* For AUTHENTICATE LOGIN user entry responses */
1035 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1039 CURLcode result = CURLE_OK;
1040 struct SessionHandle *data = conn->data;
1042 char *authpasswd = NULL;
1044 (void)instate; /* no use for this yet */
1046 if(imapcode != '+') {
1047 failf(data, "Access denied: %d", imapcode);
1048 result = CURLE_LOGIN_DENIED;
1051 /* Create the password message */
1052 result = Curl_sasl_create_login_message(data, conn->passwd,
1055 /* Send the password */
1058 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1061 state(conn, IMAP_AUTHENTICATE_FINAL);
1064 Curl_safefree(authpasswd);
1071 #ifndef CURL_DISABLE_CRYPTO_AUTH
1072 /* For AUTHENTICATE CRAM-MD5 responses */
1073 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1077 CURLcode result = CURLE_OK;
1078 struct SessionHandle *data = conn->data;
1079 char *chlg64 = data->state.buffer;
1081 char *rplyb64 = NULL;
1083 (void)instate; /* no use for this yet */
1085 if(imapcode != '+') {
1086 failf(data, "Access denied: %d", imapcode);
1087 return CURLE_LOGIN_DENIED;
1090 /* Get the challenge */
1091 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1094 /* Terminate the challenge */
1095 if(*chlg64 != '=') {
1096 for(len = strlen(chlg64); len--;)
1097 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
1098 chlg64[len] != '\t')
1106 /* Create the response message */
1107 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
1108 conn->passwd, &rplyb64, &len);
1110 /* Send the response */
1113 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1116 state(conn, IMAP_AUTHENTICATE_FINAL);
1119 Curl_safefree(rplyb64);
1125 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1126 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1130 CURLcode result = CURLE_OK;
1131 struct SessionHandle *data = conn->data;
1132 char *chlg64 = data->state.buffer;
1134 char *rplyb64 = NULL;
1136 (void)instate; /* no use for this yet */
1138 if(imapcode != '+') {
1139 failf(data, "Access denied: %d", imapcode);
1140 return CURLE_LOGIN_DENIED;
1143 /* Get the challenge */
1144 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1147 /* Create the response message */
1148 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
1149 conn->passwd, "imap",
1152 /* Send the response */
1155 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1158 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1161 Curl_safefree(rplyb64);
1167 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1168 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1172 CURLcode result = CURLE_OK;
1173 struct SessionHandle *data = conn->data;
1175 (void)instate; /* no use for this yet */
1177 if(imapcode != '+') {
1178 failf(data, "Authentication failed: %d", imapcode);
1179 result = CURLE_LOGIN_DENIED;
1182 /* Send an empty response */
1183 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1186 state(conn, IMAP_AUTHENTICATE_FINAL);
1194 /* For AUTHENTICATE NTLM (without initial response) responses */
1195 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1199 CURLcode result = CURLE_OK;
1200 struct SessionHandle *data = conn->data;
1202 char *type1msg = NULL;
1204 (void)instate; /* no use for this yet */
1206 if(imapcode != '+') {
1207 failf(data, "Access denied: %d", imapcode);
1208 result = CURLE_LOGIN_DENIED;
1211 /* Create the type-1 message */
1212 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1216 /* Send the message */
1219 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1222 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1225 Curl_safefree(type1msg);
1232 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1233 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1237 CURLcode result = CURLE_OK;
1238 struct SessionHandle *data = conn->data;
1240 char *type3msg = NULL;
1242 (void)instate; /* no use for this yet */
1244 if(imapcode != '+') {
1245 failf(data, "Access denied: %d", imapcode);
1246 result = CURLE_LOGIN_DENIED;
1249 /* Create the type-3 message */
1250 result = Curl_sasl_create_ntlm_type3_message(data,
1251 data->state.buffer + 2,
1252 conn->user, conn->passwd,
1256 /* Send the message */
1259 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1262 state(conn, IMAP_AUTHENTICATE_FINAL);
1265 Curl_safefree(type3msg);
1273 /* For final responses to the AUTHENTICATE sequence */
1274 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1278 CURLcode result = CURLE_OK;
1279 struct SessionHandle *data = conn->data;
1281 (void)instate; /* no use for this yet */
1283 if(imapcode != 'O') {
1284 failf(data, "Authentication failed: %d", imapcode);
1285 result = CURLE_LOGIN_DENIED;
1288 /* End of connect phase */
1289 state(conn, IMAP_STOP);
1294 /* For LOGIN responses */
1295 static CURLcode imap_state_login_resp(struct connectdata *conn,
1299 CURLcode result = CURLE_OK;
1300 struct SessionHandle *data = conn->data;
1302 (void)instate; /* no use for this yet */
1304 if(imapcode != 'O') {
1305 failf(data, "Access denied. %c", imapcode);
1306 result = CURLE_LOGIN_DENIED;
1309 /* End of connect phase */
1310 state(conn, IMAP_STOP);
1315 /* For LIST responses */
1316 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1319 CURLcode result = CURLE_OK;
1320 char *line = conn->data->state.buffer;
1321 size_t len = strlen(line);
1323 (void)instate; /* No use for this yet */
1325 if(imapcode == '*') {
1326 /* Temporarily add the LF character back and send as body to the client */
1328 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1331 else if(imapcode != 'O')
1332 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1334 /* End of DO phase */
1335 state(conn, IMAP_STOP);
1340 /* For SELECT responses */
1341 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1344 CURLcode result = CURLE_OK;
1345 struct SessionHandle *data = conn->data;
1346 struct IMAP *imap = conn->data->state.proto.imap;
1347 struct imap_conn *imapc = &conn->proto.imapc;
1348 const char *line = data->state.buffer;
1351 (void)instate; /* no use for this yet */
1353 if(imapcode == '*') {
1354 /* See if this is an UIDVALIDITY response */
1355 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1356 Curl_safefree(imapc->mailbox_uidvalidity);
1357 imapc->mailbox_uidvalidity = strdup(tmp);
1360 else if(imapcode == 'O') {
1361 /* Check if the UIDVALIDITY has been specified and matches */
1362 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1363 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1364 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1365 result = CURLE_REMOTE_FILE_NOT_FOUND;
1368 /* Note the currently opened mailbox on this connection */
1369 imapc->mailbox = strdup(imap->mailbox);
1372 result = imap_custom(conn);
1374 result = imap_fetch(conn);
1378 failf(data, "Select failed");
1379 result = CURLE_LOGIN_DENIED;
1385 /* For the (first line of the) FETCH responses */
1386 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1389 CURLcode result = CURLE_OK;
1390 struct SessionHandle *data = conn->data;
1391 struct imap_conn *imapc = &conn->proto.imapc;
1392 struct pingpong *pp = &imapc->pp;
1393 const char *ptr = data->state.buffer;
1394 bool parsed = FALSE;
1397 (void)instate; /* no use for this yet */
1399 if(imapcode != '*') {
1400 Curl_pgrsSetDownloadSize(data, 0);
1401 state(conn, IMAP_STOP);
1402 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1405 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1406 the continuation data contained within the curly brackets */
1407 while(*ptr && (*ptr != '{'))
1412 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1413 if(endptr - ptr > 1 && endptr[0] == '}' &&
1414 endptr[1] == '\r' && endptr[2] == '\0')
1419 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1420 Curl_pgrsSetDownloadSize(data, size);
1423 /* At this point there is a bunch of data in the header "cache" that is
1424 actually body content, send it as body and then skip it. Do note
1425 that there may even be additional "headers" after the body. */
1426 size_t chunk = pp->cache_size;
1428 if(chunk > (size_t)size)
1429 /* The conversion from curl_off_t to size_t is always fine here */
1430 chunk = (size_t)size;
1432 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1436 data->req.bytecount += chunk;
1439 infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1440 " bytes are left for transfer\n", (curl_off_t)chunk, size);
1442 /* Have we used the entire cache or just part of it?*/
1443 if(pp->cache_size > chunk) {
1444 /* Only part of it so shrink the cache to fit the trailing data */
1445 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1446 pp->cache_size -= chunk;
1449 /* Free the cache */
1450 Curl_safefree(pp->cache);
1452 /* Reset the cache size */
1458 /* The entire data is already transferred! */
1459 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1462 data->req.maxdownload = size;
1463 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1467 /* We don't know how to parse this line */
1468 failf(pp->conn->data, "Failed to parse FETCH response.");
1469 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1472 /* End of DO phase */
1473 state(conn, IMAP_STOP);
1478 /* For final FETCH responses performed after the download */
1479 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1483 CURLcode result = CURLE_OK;
1485 (void)instate; /* No use for this yet */
1488 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1490 /* End of DONE phase */
1491 state(conn, IMAP_STOP);
1496 /* For APPEND responses */
1497 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1500 CURLcode result = CURLE_OK;
1501 struct SessionHandle *data = conn->data;
1503 (void)instate; /* No use for this yet */
1505 if(imapcode != '+') {
1506 result = CURLE_UPLOAD_FAILED;
1509 /* Set the progress upload size */
1510 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1513 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1515 /* End of DO phase */
1516 state(conn, IMAP_STOP);
1522 /* For final APPEND responses performed after the upload */
1523 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1527 CURLcode result = CURLE_OK;
1529 (void)instate; /* No use for this yet */
1532 result = CURLE_UPLOAD_FAILED;
1534 /* End of DONE phase */
1535 state(conn, IMAP_STOP);
1540 static CURLcode imap_statemach_act(struct connectdata *conn)
1542 CURLcode result = CURLE_OK;
1543 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1545 struct imap_conn *imapc = &conn->proto.imapc;
1546 struct pingpong *pp = &imapc->pp;
1549 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1550 if(imapc->state == IMAP_UPGRADETLS)
1551 return imap_state_upgrade_tls(conn);
1553 /* Flush any data that needs to be sent */
1555 return Curl_pp_flushsend(pp);
1558 /* Read the response from the server */
1559 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1563 /* Was there an error parsing the response line? */
1565 return CURLE_FTP_WEIRD_SERVER_REPLY;
1570 /* We have now received a full IMAP server response */
1571 switch(imapc->state) {
1572 case IMAP_SERVERGREET:
1573 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1576 case IMAP_CAPABILITY:
1577 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1581 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1584 case IMAP_AUTHENTICATE_PLAIN:
1585 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1588 case IMAP_AUTHENTICATE_LOGIN:
1589 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1592 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1593 result = imap_state_auth_login_password_resp(conn, imapcode,
1597 #ifndef CURL_DISABLE_CRYPTO_AUTH
1598 case IMAP_AUTHENTICATE_CRAMMD5:
1599 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1602 case IMAP_AUTHENTICATE_DIGESTMD5:
1603 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1606 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1607 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1612 case IMAP_AUTHENTICATE_NTLM:
1613 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1616 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1617 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1622 case IMAP_AUTHENTICATE_FINAL:
1623 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1627 result = imap_state_login_resp(conn, imapcode, imapc->state);
1632 result = imap_state_list_resp(conn, imapcode, imapc->state);
1636 result = imap_state_select_resp(conn, imapcode, imapc->state);
1640 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1643 case IMAP_FETCH_FINAL:
1644 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1648 result = imap_state_append_resp(conn, imapcode, imapc->state);
1651 case IMAP_APPEND_FINAL:
1652 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1656 /* fallthrough, just stop! */
1658 /* internal error */
1659 state(conn, IMAP_STOP);
1662 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1667 /* Called repeatedly until done from multi.c */
1668 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1670 CURLcode result = CURLE_OK;
1671 struct imap_conn *imapc = &conn->proto.imapc;
1673 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1674 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1676 result = Curl_pp_statemach(&imapc->pp, FALSE);
1678 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1683 static CURLcode imap_block_statemach(struct connectdata *conn)
1685 CURLcode result = CURLE_OK;
1686 struct imap_conn *imapc = &conn->proto.imapc;
1688 while(imapc->state != IMAP_STOP && !result) {
1689 result = Curl_pp_statemach(&imapc->pp, TRUE);
1695 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1697 static CURLcode imap_init(struct connectdata *conn)
1699 struct SessionHandle *data = conn->data;
1700 struct IMAP *imap = data->state.proto.imap;
1703 imap = data->state.proto.imap = calloc(sizeof(struct IMAP), 1);
1705 return CURLE_OUT_OF_MEMORY;
1711 /* For the IMAP "protocol connect" and "doing" phases only */
1712 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1715 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1718 /***********************************************************************
1720 * imap_connect() should do everything that is to be considered a part of
1721 * the connection phase.
1723 * The variable 'done' points to will be TRUE if the protocol-layer connect
1724 * phase is done when this function returns, or FALSE is not. When called as
1725 * a part of the easy interface, it will always be TRUE.
1727 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1729 CURLcode result = CURLE_OK;
1730 struct imap_conn *imapc = &conn->proto.imapc;
1731 struct pingpong *pp = &imapc->pp;
1733 *done = FALSE; /* default to not done yet */
1735 /* If there already is a protocol-specific struct allocated for this
1736 sessionhandle, deal with it */
1737 Curl_reset_reqproto(conn);
1739 /* Initialise the IMAP layer */
1740 result = imap_init(conn);
1744 /* We always support persistent connections in IMAP */
1745 conn->bits.close = FALSE;
1747 /* Set the default response time-out */
1748 pp->response_time = RESP_TIMEOUT;
1749 pp->statemach_act = imap_statemach_act;
1750 pp->endofresp = imap_endofresp;
1753 /* Initialise the pingpong layer */
1756 /* Start off waiting for the server greeting response */
1757 state(conn, IMAP_SERVERGREET);
1759 /* Start off with an response id of '*' */
1760 strcpy(imapc->resptag, "*");
1762 result = imap_multi_statemach(conn, done);
1767 /***********************************************************************
1771 * The DONE function. This does what needs to be done after a single DO has
1774 * Input argument is already checked for validity.
1776 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1779 CURLcode result = CURLE_OK;
1780 struct SessionHandle *data = conn->data;
1781 struct IMAP *imap = data->state.proto.imap;
1786 /* When the easy handle is removed from the multi interface while libcurl
1787 is still trying to resolve the host name, the IMAP struct is not yet
1788 initialized. However, the removal action calls Curl_done() which in
1789 turn calls this function, so we simply return success. */
1793 conn->bits.close = TRUE; /* marked for closure */
1794 result = status; /* use the already set error code */
1796 else if(!data->set.connect_only && !imap->custom &&
1797 (imap->uid || data->set.upload)) {
1798 /* Handle responses after FETCH or APPEND transfer has finished */
1799 if(!data->set.upload)
1800 state(conn, IMAP_FETCH_FINAL);
1802 /* End the APPEND command first by sending an empty line */
1803 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1805 state(conn, IMAP_APPEND_FINAL);
1808 /* Run the state-machine
1810 TODO: when the multi interface is used, this _really_ should be using
1811 the imap_multi_statemach function but we have no general support for
1812 non-blocking DONE operations, not in the multi state machine and with
1813 Curl_done() invokes on several places in the code!
1816 result = imap_block_statemach(conn);
1819 /* Cleanup our per-request based variables */
1820 Curl_safefree(imap->mailbox);
1821 Curl_safefree(imap->uidvalidity);
1822 Curl_safefree(imap->uid);
1823 Curl_safefree(imap->section);
1824 Curl_safefree(imap->custom);
1825 Curl_safefree(imap->custom_params);
1827 /* Clear the transfer mode for the next request */
1828 imap->transfer = FTPTRANSFER_BODY;
1833 /***********************************************************************
1837 * This is the actual DO function for IMAP. Fetch or append a message, or do
1838 * other things according to the options previously setup.
1840 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1843 /* This is IMAP and no proxy */
1844 CURLcode result = CURLE_OK;
1845 struct SessionHandle *data = conn->data;
1846 struct IMAP *imap = data->state.proto.imap;
1847 struct imap_conn *imapc = &conn->proto.imapc;
1848 bool selected = FALSE;
1850 DEBUGF(infof(conn->data, "DO phase starts\n"));
1852 if(conn->data->set.opt_no_body) {
1853 /* Requested no body means no transfer */
1854 imap->transfer = FTPTRANSFER_INFO;
1857 *dophase_done = FALSE; /* not done yet */
1859 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1860 has already been selected on this connection */
1861 if(imap->mailbox && imapc->mailbox &&
1862 !strcmp(imap->mailbox, imapc->mailbox) &&
1863 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1864 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1867 /* Start the first command in the DO phase */
1868 if(conn->data->set.upload)
1869 /* APPEND can be executed directly */
1870 result = imap_append(conn);
1871 else if(imap->custom && (selected || !imap->mailbox))
1872 /* Custom command using the same mailbox or no mailbox */
1873 result = imap_custom(conn);
1874 else if(!imap->custom && selected && imap->uid)
1875 /* FETCH from the same mailbox */
1876 result = imap_fetch(conn);
1878 /* SELECT the mailbox */
1879 result = imap_select(conn);
1882 result = imap_list(conn);
1887 /* Run the state-machine */
1888 result = imap_multi_statemach(conn, dophase_done);
1890 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1893 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1898 /***********************************************************************
1902 * This function is registered as 'curl_do' function. It decodes the path
1903 * parts etc as a wrapper to the actual DO function (imap_perform).
1905 * The input argument is already checked for validity.
1907 static CURLcode imap_do(struct connectdata *conn, bool *done)
1909 CURLcode result = CURLE_OK;
1911 *done = FALSE; /* default to false */
1913 /* Since connections can be re-used between SessionHandles, there might be a
1914 connection already existing but on a fresh SessionHandle struct. As such
1915 we make sure we have a good IMAP struct to play with. For new connections
1916 the IMAP struct is allocated and setup in the imap_connect() function. */
1917 Curl_reset_reqproto(conn);
1918 result = imap_init(conn);
1922 /* Parse the URL path */
1923 result = imap_parse_url_path(conn);
1927 /* Parse the custom request */
1928 result = imap_parse_custom_request(conn);
1932 result = imap_regular_transfer(conn, done);
1937 /***********************************************************************
1941 * This should be called before calling sclose(). We should then wait for the
1942 * response from the server before returning. The calling code should then try
1943 * to close the connection.
1946 static CURLcode imap_logout(struct connectdata *conn)
1948 CURLcode result = CURLE_OK;
1950 /* Send the LOGOUT command */
1951 result = imap_sendf(conn, "LOGOUT", NULL);
1955 state(conn, IMAP_LOGOUT);
1957 result = imap_block_statemach(conn);
1962 /***********************************************************************
1966 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1967 * resources. BLOCKING.
1969 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1971 struct imap_conn *imapc = &conn->proto.imapc;
1973 /* We cannot send quit unconditionally. If this connection is stale or
1974 bad in any way, sending quit and waiting around here will make the
1975 disconnect wait in vain and cause more problems than we need to. */
1977 /* The IMAP session may or may not have been allocated/setup at this
1979 if(!dead_connection && imapc->pp.conn)
1980 (void)imap_logout(conn); /* ignore errors on LOGOUT */
1982 /* Disconnect from the server */
1983 Curl_pp_disconnect(&imapc->pp);
1985 /* Cleanup the SASL module */
1986 Curl_sasl_cleanup(conn, imapc->authused);
1988 /* Cleanup our connection based variables */
1989 Curl_safefree(imapc->mailbox);
1990 Curl_safefree(imapc->mailbox_uidvalidity);
1995 /***********************************************************************
1999 * Portable test of whether the specified char is a "bchar" as defined in the
2000 * grammar of RFC-5092.
2002 static bool imap_is_bchar(char ch)
2006 case ':': case '@': case '/':
2007 /* bchar -> achar */
2009 /* bchar -> achar -> uchar -> unreserved */
2010 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2011 case '7': case '8': case '9':
2012 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2013 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2014 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2015 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2016 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2017 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2018 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2019 case 'v': case 'w': case 'x': case 'y': case 'z':
2020 case '-': case '.': case '_': case '~':
2021 /* bchar -> achar -> uchar -> sub-delims-sh */
2022 case '!': case '$': case '\'': case '(': case ')': case '*':
2024 /* bchar -> achar -> uchar -> pct-encoded */
2025 case '%': /* HEXDIG chars are already included above */
2033 /***********************************************************************
2035 * imap_parse_url_path()
2037 * Parse the URL path into separate path components.
2040 static CURLcode imap_parse_url_path(struct connectdata *conn)
2042 /* The imap struct is already initialised in imap_connect() */
2043 CURLcode result = CURLE_OK;
2044 struct SessionHandle *data = conn->data;
2045 struct IMAP *imap = data->state.proto.imap;
2046 const char *begin = data->state.path;
2047 const char *ptr = begin;
2049 /* See how much of the URL is a valid path and decode it */
2050 while(imap_is_bchar(*ptr))
2054 /* Remove the trailing slash if present */
2055 const char *end = ptr;
2056 if(end > begin && end[-1] == '/')
2059 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2065 imap->mailbox = NULL;
2067 /* There can be any number of parameters in the form ";NAME=VALUE" */
2068 while(*ptr == ';') {
2073 /* Find the length of the name parameter */
2075 while(*ptr && *ptr != '=')
2079 return CURLE_URL_MALFORMAT;
2081 /* Decode the name parameter */
2082 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2086 /* Find the length of the value parameter */
2088 while(imap_is_bchar(*ptr))
2091 /* Decode the value parameter */
2092 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2094 Curl_safefree(name);
2098 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2100 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2101 stripping of the trailing slash character if it is present.
2103 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2104 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2105 if(valuelen > 0 && value[valuelen - 1] == '/')
2106 value[valuelen - 1] = '\0';
2108 imap->uidvalidity = value;
2111 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2112 if(valuelen > 0 && value[valuelen - 1] == '/')
2113 value[valuelen - 1] = '\0';
2118 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2119 if(valuelen > 0 && value[valuelen - 1] == '/')
2120 value[valuelen - 1] = '\0';
2122 imap->section = value;
2126 Curl_safefree(name);
2127 Curl_safefree(value);
2129 return CURLE_URL_MALFORMAT;
2132 Curl_safefree(name);
2133 Curl_safefree(value);
2136 /* Any extra stuff at the end of the URL is an error */
2138 return CURLE_URL_MALFORMAT;
2143 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2145 CURLcode result = CURLE_OK;
2146 struct SessionHandle *data = conn->data;
2147 struct IMAP *imap = data->state.proto.imap;
2148 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2151 /* URL decode the custom request */
2152 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2154 /* Extract the parameters if specified */
2156 const char *params = imap->custom;
2158 while(*params && *params != ' ')
2162 imap->custom_params = strdup(params);
2163 imap->custom[params - imap->custom] = '\0';
2165 if(!imap->custom_params)
2166 result = CURLE_OUT_OF_MEMORY;
2174 /* Call this when the DO phase has completed */
2175 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2177 struct IMAP *imap = conn->data->state.proto.imap;
2181 if(imap->transfer != FTPTRANSFER_BODY)
2182 /* no data to transfer */
2183 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2188 /* Called from multi.c while DOing */
2189 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2191 CURLcode result = imap_multi_statemach(conn, dophase_done);
2194 DEBUGF(infof(conn->data, "DO phase failed\n"));
2197 result = imap_dophase_done(conn, FALSE /* not connected */);
2199 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2206 /***********************************************************************
2208 * imap_regular_transfer()
2210 * The input argument is already checked for validity.
2212 * Performs all commands done before a regular transfer between a local and a
2215 static CURLcode imap_regular_transfer(struct connectdata *conn,
2218 CURLcode result = CURLE_OK;
2219 bool connected = FALSE;
2220 struct SessionHandle *data = conn->data;
2222 /* Make sure size is unknown at this point */
2223 data->req.size = -1;
2225 Curl_pgrsSetUploadCounter(data, 0);
2226 Curl_pgrsSetDownloadCounter(data, 0);
2227 Curl_pgrsSetUploadSize(data, 0);
2228 Curl_pgrsSetDownloadSize(data, 0);
2230 result = imap_perform(conn, &connected, dophase_done);
2234 /* The DO phase has not completed yet */
2237 result = imap_dophase_done(conn, connected);
2243 static CURLcode imap_setup_connection(struct connectdata *conn)
2245 struct SessionHandle *data = conn->data;
2247 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2248 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2249 switch and use HTTP operations only */
2250 #ifndef CURL_DISABLE_HTTP
2251 if(conn->handler == &Curl_handler_imap)
2252 conn->handler = &Curl_handler_imap_proxy;
2255 conn->handler = &Curl_handler_imaps_proxy;
2257 failf(data, "IMAPS not supported!");
2258 return CURLE_UNSUPPORTED_PROTOCOL;
2262 /* We explicitly mark this connection as persistent here as we're doing
2263 IMAP over HTTP and thus we accidentally avoid setting this value
2265 conn->bits.close = FALSE;
2267 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2268 return CURLE_UNSUPPORTED_PROTOCOL;
2272 data->state.path++; /* don't include the initial slash */
2277 #endif /* CURL_DISABLE_IMAP */