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;
694 struct imap_conn *imapc = &conn->proto.imapc;
697 /* Make sure the mailbox is in the correct atom format */
698 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
700 return CURLE_OUT_OF_MEMORY;
702 /* Send the LIST command */
703 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
705 Curl_safefree(mailbox);
708 state(conn, IMAP_LIST);
713 static CURLcode imap_select(struct connectdata *conn)
715 CURLcode result = CURLE_OK;
716 struct SessionHandle *data = conn->data;
717 struct IMAP *imap = data->state.proto.imap;
718 struct imap_conn *imapc = &conn->proto.imapc;
721 /* Invalidate old information as we are switching mailboxes */
722 Curl_safefree(imapc->mailbox);
723 Curl_safefree(imapc->mailbox_uidvalidity);
725 /* Check we have a mailbox */
727 failf(conn->data, "Cannot SELECT without a mailbox.");
728 return CURLE_URL_MALFORMAT;
731 /* Make sure the mailbox is in the correct atom format */
732 mailbox = imap_atom(imap->mailbox);
734 return CURLE_OUT_OF_MEMORY;
736 /* Send the SELECT command */
737 result = imap_sendf(conn, "SELECT %s", mailbox);
739 Curl_safefree(mailbox);
742 state(conn, IMAP_SELECT);
747 static CURLcode imap_fetch(struct connectdata *conn)
749 CURLcode result = CURLE_OK;
750 struct IMAP *imap = conn->data->state.proto.imap;
752 /* Send the FETCH command */
753 result = imap_sendf(conn, "FETCH %s BODY[%s]",
754 imap->uid ? imap->uid : "1",
755 imap->section ? imap->section : "");
758 state(conn, IMAP_FETCH);
763 static CURLcode imap_append(struct connectdata *conn)
766 struct IMAP *imap = conn->data->state.proto.imap;
769 /* Check we have a mailbox */
771 failf(conn->data, "Cannot APPEND without a mailbox.");
772 return CURLE_URL_MALFORMAT;
775 /* Check we know the size of the upload */
776 if(conn->data->set.infilesize < 0) {
777 failf(conn->data, "Cannot APPEND with unknown input file size\n");
778 return CURLE_UPLOAD_FAILED;
781 /* Make sure the mailbox is in the correct atom format */
782 mailbox = imap_atom(imap->mailbox);
784 return CURLE_OUT_OF_MEMORY;
786 /* Send the APPEND command */
787 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
788 mailbox, conn->data->set.infilesize);
790 Curl_safefree(mailbox);
793 state(conn, IMAP_APPEND);
798 static CURLcode imap_custom(struct connectdata *conn)
800 struct IMAP *imap = conn->data->state.proto.imap;
802 /* Send the custom request */
803 CURLcode result = imap_sendf(conn, "%s%s", imap->custom,
804 imap->custom_params ? imap->custom_params : "");
807 /* We don't know how much data will be received */
808 Curl_pgrsSetDownloadSize(conn->data, -1);
810 state(conn, IMAP_CUSTOM);
816 /* For the initial server greeting */
817 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
821 CURLcode result = CURLE_OK;
822 struct SessionHandle *data = conn->data;
824 (void)instate; /* no use for this yet */
826 if(imapcode != 'O') {
827 failf(data, "Got unexpected imap-server response");
828 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
831 result = imap_state_capability(conn);
836 /* For CAPABILITY responses */
837 static CURLcode imap_state_capability_resp(struct connectdata *conn,
841 CURLcode result = CURLE_OK;
842 struct SessionHandle *data = conn->data;
843 struct imap_conn *imapc = &conn->proto.imapc;
844 const char *line = data->state.buffer;
847 (void)instate; /* no use for this yet */
849 /* Do we have a untagged response? */
850 if(imapcode == '*') {
853 /* Loop through the data line */
856 (*line == ' ' || *line == '\t' ||
857 *line == '\r' || *line == '\n')) {
865 /* Extract the word */
866 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
867 line[wordlen] != '\t' && line[wordlen] != '\r' &&
868 line[wordlen] != '\n';)
871 /* Does the server support the STARTTLS capability? */
872 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
873 imapc->tls_supported = TRUE;
875 /* Has the server explicitly disabled clear text authentication? */
876 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
877 imapc->login_disabled = TRUE;
879 /* Does the server support the SASL-IR capability? */
880 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
881 imapc->ir_supported = TRUE;
883 /* Do we have a SASL based authentication mechanism? */
884 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
888 /* Test the word for a matching authentication mechanism */
889 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
890 imapc->authmechs |= SASL_MECH_LOGIN;
891 if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
892 imapc->authmechs |= SASL_MECH_PLAIN;
893 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
894 imapc->authmechs |= SASL_MECH_CRAM_MD5;
895 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
896 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
897 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
898 imapc->authmechs |= SASL_MECH_GSSAPI;
899 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
900 imapc->authmechs |= SASL_MECH_EXTERNAL;
901 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
902 imapc->authmechs |= SASL_MECH_NTLM;
908 else if(imapcode == 'O') {
909 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
910 /* We don't have a SSL/TLS connection yet, but SSL is requested */
911 if(imapc->tls_supported)
912 /* Switch to TLS connection now */
913 result = imap_state_starttls(conn);
914 else if(data->set.use_ssl == CURLUSESSL_TRY)
915 /* Fallback and carry on with authentication */
916 result = imap_authenticate(conn);
918 failf(data, "STARTTLS not supported.");
919 result = CURLE_USE_SSL_FAILED;
923 result = imap_authenticate(conn);
926 result = imap_state_login(conn);
931 /* For STARTTLS responses */
932 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
936 CURLcode result = CURLE_OK;
937 struct SessionHandle *data = conn->data;
939 (void)instate; /* no use for this yet */
941 if(imapcode != 'O') {
942 if(data->set.use_ssl != CURLUSESSL_TRY) {
943 failf(data, "STARTTLS denied. %c", imapcode);
944 result = CURLE_USE_SSL_FAILED;
947 result = imap_authenticate(conn);
950 result = imap_state_upgrade_tls(conn);
955 /* For AUTHENTICATE PLAIN (without initial response) responses */
956 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
960 CURLcode result = CURLE_OK;
961 struct SessionHandle *data = conn->data;
963 char *plainauth = NULL;
965 (void)instate; /* no use for this yet */
967 if(imapcode != '+') {
968 failf(data, "Access denied. %c", imapcode);
969 result = CURLE_LOGIN_DENIED;
972 /* Create the authorisation message */
973 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
976 /* Send the message */
979 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
982 state(conn, IMAP_AUTHENTICATE_FINAL);
985 Curl_safefree(plainauth);
992 /* For AUTHENTICATE LOGIN (without initial response) responses */
993 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
997 CURLcode result = CURLE_OK;
998 struct SessionHandle *data = conn->data;
1000 char *authuser = NULL;
1002 (void)instate; /* no use for this yet */
1004 if(imapcode != '+') {
1005 failf(data, "Access denied: %d", imapcode);
1006 result = CURLE_LOGIN_DENIED;
1009 /* Create the user message */
1010 result = Curl_sasl_create_login_message(data, conn->user,
1016 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1019 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1022 Curl_safefree(authuser);
1029 /* For AUTHENTICATE LOGIN user entry responses */
1030 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1034 CURLcode result = CURLE_OK;
1035 struct SessionHandle *data = conn->data;
1037 char *authpasswd = NULL;
1039 (void)instate; /* no use for this yet */
1041 if(imapcode != '+') {
1042 failf(data, "Access denied: %d", imapcode);
1043 result = CURLE_LOGIN_DENIED;
1046 /* Create the password message */
1047 result = Curl_sasl_create_login_message(data, conn->passwd,
1050 /* Send the password */
1053 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1056 state(conn, IMAP_AUTHENTICATE_FINAL);
1059 Curl_safefree(authpasswd);
1066 #ifndef CURL_DISABLE_CRYPTO_AUTH
1067 /* For AUTHENTICATE CRAM-MD5 responses */
1068 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1072 CURLcode result = CURLE_OK;
1073 struct SessionHandle *data = conn->data;
1074 char *chlg64 = data->state.buffer;
1076 char *rplyb64 = NULL;
1078 (void)instate; /* no use for this yet */
1080 if(imapcode != '+') {
1081 failf(data, "Access denied: %d", imapcode);
1082 return CURLE_LOGIN_DENIED;
1085 /* Get the challenge */
1086 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1089 /* Terminate the challenge */
1090 if(*chlg64 != '=') {
1091 for(len = strlen(chlg64); len--;)
1092 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
1093 chlg64[len] != '\t')
1101 /* Create the response message */
1102 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
1103 conn->passwd, &rplyb64, &len);
1105 /* Send the response */
1108 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1111 state(conn, IMAP_AUTHENTICATE_FINAL);
1114 Curl_safefree(rplyb64);
1120 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1121 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1125 CURLcode result = CURLE_OK;
1126 struct SessionHandle *data = conn->data;
1127 char *chlg64 = data->state.buffer;
1129 char *rplyb64 = NULL;
1131 (void)instate; /* no use for this yet */
1133 if(imapcode != '+') {
1134 failf(data, "Access denied: %d", imapcode);
1135 return CURLE_LOGIN_DENIED;
1138 /* Get the challenge */
1139 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1142 /* Create the response message */
1143 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
1144 conn->passwd, "imap",
1147 /* Send the response */
1150 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1153 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1156 Curl_safefree(rplyb64);
1162 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1163 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1167 CURLcode result = CURLE_OK;
1168 struct SessionHandle *data = conn->data;
1170 (void)instate; /* no use for this yet */
1172 if(imapcode != '+') {
1173 failf(data, "Authentication failed: %d", imapcode);
1174 result = CURLE_LOGIN_DENIED;
1177 /* Send an empty response */
1178 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1181 state(conn, IMAP_AUTHENTICATE_FINAL);
1189 /* For AUTHENTICATE NTLM (without initial response) responses */
1190 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1194 CURLcode result = CURLE_OK;
1195 struct SessionHandle *data = conn->data;
1197 char *type1msg = NULL;
1199 (void)instate; /* no use for this yet */
1201 if(imapcode != '+') {
1202 failf(data, "Access denied: %d", imapcode);
1203 result = CURLE_LOGIN_DENIED;
1206 /* Create the type-1 message */
1207 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1211 /* Send the message */
1214 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1217 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1220 Curl_safefree(type1msg);
1227 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1228 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1232 CURLcode result = CURLE_OK;
1233 struct SessionHandle *data = conn->data;
1235 char *type3msg = NULL;
1237 (void)instate; /* no use for this yet */
1239 if(imapcode != '+') {
1240 failf(data, "Access denied: %d", imapcode);
1241 result = CURLE_LOGIN_DENIED;
1244 /* Create the type-3 message */
1245 result = Curl_sasl_create_ntlm_type3_message(data,
1246 data->state.buffer + 2,
1247 conn->user, conn->passwd,
1251 /* Send the message */
1254 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1257 state(conn, IMAP_AUTHENTICATE_FINAL);
1260 Curl_safefree(type3msg);
1268 /* For final responses to the AUTHENTICATE sequence */
1269 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1273 CURLcode result = CURLE_OK;
1274 struct SessionHandle *data = conn->data;
1276 (void)instate; /* no use for this yet */
1278 if(imapcode != 'O') {
1279 failf(data, "Authentication failed: %d", imapcode);
1280 result = CURLE_LOGIN_DENIED;
1283 /* End of connect phase */
1284 state(conn, IMAP_STOP);
1289 /* For LOGIN responses */
1290 static CURLcode imap_state_login_resp(struct connectdata *conn,
1294 CURLcode result = CURLE_OK;
1295 struct SessionHandle *data = conn->data;
1297 (void)instate; /* no use for this yet */
1299 if(imapcode != 'O') {
1300 failf(data, "Access denied. %c", imapcode);
1301 result = CURLE_LOGIN_DENIED;
1304 /* End of connect phase */
1305 state(conn, IMAP_STOP);
1310 /* For LIST responses */
1311 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1314 CURLcode result = CURLE_OK;
1315 char *line = conn->data->state.buffer;
1316 size_t len = strlen(line);
1318 (void)instate; /* No use for this yet */
1320 if(imapcode == '*') {
1321 /* The client which asked for this custom command should know best
1322 how to cope with the result, just send it as body.
1323 Add back the LF character temporarily while saving. */
1325 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1329 /* Final response. Stop and return the final status. */
1331 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1335 /* End of DO phase */
1336 state(conn, IMAP_STOP);
1342 /* For SELECT responses */
1343 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1346 CURLcode result = CURLE_OK;
1347 struct SessionHandle *data = conn->data;
1348 struct IMAP *imap = conn->data->state.proto.imap;
1349 struct imap_conn *imapc = &conn->proto.imapc;
1350 const char *line = data->state.buffer;
1353 (void)instate; /* no use for this yet */
1355 if(imapcode == '*') {
1356 /* See if this is an UIDVALIDITY response */
1357 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1358 Curl_safefree(imapc->mailbox_uidvalidity);
1359 imapc->mailbox_uidvalidity = strdup(tmp);
1362 else if(imapcode == 'O') {
1363 /* Check if the UIDVALIDITY has been specified and matches */
1364 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1365 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1366 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1367 result = CURLE_REMOTE_FILE_NOT_FOUND;
1370 /* Note the currently opened mailbox on this connection */
1371 imapc->mailbox = strdup(imap->mailbox);
1374 result = imap_custom(conn);
1376 result = imap_fetch(conn);
1380 failf(data, "Select failed");
1381 result = CURLE_LOGIN_DENIED;
1387 /* For the (first line of the) FETCH responses */
1388 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1391 CURLcode result = CURLE_OK;
1392 struct SessionHandle *data = conn->data;
1393 struct imap_conn *imapc = &conn->proto.imapc;
1394 struct pingpong *pp = &imapc->pp;
1395 const char *ptr = data->state.buffer;
1396 bool parsed = FALSE;
1399 (void)instate; /* no use for this yet */
1401 if(imapcode != '*') {
1402 Curl_pgrsSetDownloadSize(data, 0);
1403 state(conn, IMAP_STOP);
1404 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1407 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1408 the continuation data contained within the curly brackets */
1409 while(*ptr && (*ptr != '{'))
1414 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1415 if(endptr - ptr > 1 && endptr[0] == '}' &&
1416 endptr[1] == '\r' && endptr[2] == '\0')
1421 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1422 Curl_pgrsSetDownloadSize(data, size);
1425 /* At this point there is a bunch of data in the header "cache" that is
1426 actually body content, send it as body and then skip it. Do note
1427 that there may even be additional "headers" after the body. */
1428 size_t chunk = pp->cache_size;
1430 if(chunk > (size_t)size)
1431 /* The conversion from curl_off_t to size_t is always fine here */
1432 chunk = (size_t)size;
1434 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1438 data->req.bytecount += chunk;
1441 infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1442 " bytes are left for transfer\n", (curl_off_t)chunk, size);
1444 /* Have we used the entire cache or just part of it?*/
1445 if(pp->cache_size > chunk) {
1446 /* Only part of it so shrink the cache to fit the trailing data */
1447 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1448 pp->cache_size -= chunk;
1451 /* Free the cache */
1452 Curl_safefree(pp->cache);
1454 /* Reset the cache size */
1460 /* The entire data is already transferred! */
1461 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1464 data->req.maxdownload = size;
1465 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1469 /* We don't know how to parse this line */
1470 failf(pp->conn->data, "Failed to parse FETCH response.");
1471 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1474 /* End of DO phase */
1475 state(conn, IMAP_STOP);
1480 /* For final FETCH responses performed after the download */
1481 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1485 CURLcode result = CURLE_OK;
1487 (void)instate; /* No use for this yet */
1490 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1494 /* End of DONE phase */
1495 state(conn, IMAP_STOP);
1500 /* For APPEND responses */
1501 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1504 CURLcode result = CURLE_OK;
1505 struct SessionHandle *data = conn->data;
1507 (void)instate; /* No use for this yet */
1509 if(imapcode != '+') {
1510 result = CURLE_UPLOAD_FAILED;
1513 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1516 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1519 /* End of DO phase */
1520 state(conn, IMAP_STOP);
1525 /* For final APPEND responses performed after the upload */
1526 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1530 CURLcode result = CURLE_OK;
1532 (void)instate; /* No use for this yet */
1534 /* Final response, stop and return the final status */
1538 result = CURLE_UPLOAD_FAILED;
1540 /* End of DONE phase */
1541 state(conn, IMAP_STOP);
1546 /* For custom request responses */
1547 static CURLcode imap_state_custom_resp(struct connectdata *conn,
1551 CURLcode result = CURLE_OK;
1552 char *line = conn->data->state.buffer;
1553 size_t len = strlen(line);
1555 (void)instate; /* No use for this yet */
1557 if(imapcode == '*') {
1558 /* The client which asked for this custom command should know best
1559 how to cope with the result, just send it as body.
1560 Add back the LF character temporarily while saving. */
1562 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1566 /* Final response. Stop and return the final status. */
1568 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1572 /* End of DO phase */
1573 state(conn, IMAP_STOP);
1579 static CURLcode imap_statemach_act(struct connectdata *conn)
1581 CURLcode result = CURLE_OK;
1582 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1584 struct imap_conn *imapc = &conn->proto.imapc;
1585 struct pingpong *pp = &imapc->pp;
1588 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1589 if(imapc->state == IMAP_UPGRADETLS)
1590 return imap_state_upgrade_tls(conn);
1592 /* Flush any data that needs to be sent */
1594 return Curl_pp_flushsend(pp);
1597 /* Read the response from the server */
1598 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1602 /* Was there an error parsing the response line? */
1604 return CURLE_FTP_WEIRD_SERVER_REPLY;
1609 /* We have now received a full IMAP server response */
1610 switch(imapc->state) {
1611 case IMAP_SERVERGREET:
1612 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1615 case IMAP_CAPABILITY:
1616 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1620 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1623 case IMAP_AUTHENTICATE_PLAIN:
1624 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1627 case IMAP_AUTHENTICATE_LOGIN:
1628 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1631 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1632 result = imap_state_auth_login_password_resp(conn, imapcode,
1636 #ifndef CURL_DISABLE_CRYPTO_AUTH
1637 case IMAP_AUTHENTICATE_CRAMMD5:
1638 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1641 case IMAP_AUTHENTICATE_DIGESTMD5:
1642 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1645 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1646 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1651 case IMAP_AUTHENTICATE_NTLM:
1652 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1655 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1656 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1661 case IMAP_AUTHENTICATE_FINAL:
1662 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1666 result = imap_state_login_resp(conn, imapcode, imapc->state);
1670 result = imap_state_list_resp(conn, imapcode, imapc->state);
1674 result = imap_state_select_resp(conn, imapcode, imapc->state);
1678 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1681 case IMAP_FETCH_FINAL:
1682 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1686 result = imap_state_append_resp(conn, imapcode, imapc->state);
1689 case IMAP_APPEND_FINAL:
1690 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1694 result = imap_state_custom_resp(conn, imapcode, imapc->state);
1698 /* fallthrough, just stop! */
1700 /* internal error */
1701 state(conn, IMAP_STOP);
1704 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1709 /* Called repeatedly until done from multi.c */
1710 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1712 CURLcode result = CURLE_OK;
1713 struct imap_conn *imapc = &conn->proto.imapc;
1715 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1716 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1718 result = Curl_pp_statemach(&imapc->pp, FALSE);
1720 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1725 static CURLcode imap_block_statemach(struct connectdata *conn)
1727 CURLcode result = CURLE_OK;
1728 struct imap_conn *imapc = &conn->proto.imapc;
1730 while(imapc->state != IMAP_STOP) {
1731 result = Curl_pp_statemach(&imapc->pp, TRUE);
1739 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1741 static CURLcode imap_init(struct connectdata *conn)
1743 struct SessionHandle *data = conn->data;
1744 struct IMAP *imap = data->state.proto.imap;
1747 imap = data->state.proto.imap = calloc(sizeof(struct IMAP), 1);
1749 return CURLE_OUT_OF_MEMORY;
1755 /* For the IMAP "protocol connect" and "doing" phases only */
1756 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1759 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1762 /***********************************************************************
1764 * imap_connect() should do everything that is to be considered a part of
1765 * the connection phase.
1767 * The variable 'done' points to will be TRUE if the protocol-layer connect
1768 * phase is done when this function returns, or FALSE is not. When called as
1769 * a part of the easy interface, it will always be TRUE.
1771 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1773 CURLcode result = CURLE_OK;
1774 struct imap_conn *imapc = &conn->proto.imapc;
1775 struct pingpong *pp = &imapc->pp;
1777 *done = FALSE; /* default to not done yet */
1779 /* If there already is a protocol-specific struct allocated for this
1780 sessionhandle, deal with it */
1781 Curl_reset_reqproto(conn);
1783 /* Initialise the IMAP layer */
1784 result = imap_init(conn);
1788 /* We always support persistent connections in IMAP */
1789 conn->bits.close = FALSE;
1791 /* Set the default response time-out */
1792 pp->response_time = RESP_TIMEOUT;
1793 pp->statemach_act = imap_statemach_act;
1794 pp->endofresp = imap_endofresp;
1797 /* Initialise the pingpong layer */
1800 /* Start off waiting for the server greeting response */
1801 state(conn, IMAP_SERVERGREET);
1803 /* Start off with an response id of '*' */
1804 strcpy(imapc->resptag, "*");
1806 result = imap_multi_statemach(conn, done);
1811 /***********************************************************************
1815 * The DONE function. This does what needs to be done after a single DO has
1818 * Input argument is already checked for validity.
1820 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1823 CURLcode result = CURLE_OK;
1824 struct SessionHandle *data = conn->data;
1825 struct IMAP *imap = data->state.proto.imap;
1830 /* When the easy handle is removed from the multi interface while libcurl
1831 is still trying to resolve the host name, the IMAP struct is not yet
1832 initialized. However, the removal action calls Curl_done() which in
1833 turn calls this function, so we simply return success. */
1837 conn->bits.close = TRUE; /* marked for closure */
1838 result = status; /* use the already set error code */
1840 else if(!data->set.connect_only && !imap->custom) {
1841 /* Handle responses after FETCH or APPEND transfer has finished */
1842 if(!data->set.upload)
1843 state(conn, IMAP_FETCH_FINAL);
1845 /* End the APPEND command first by sending an empty line */
1846 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1848 state(conn, IMAP_APPEND_FINAL);
1851 /* Run the state-machine
1853 TODO: when the multi interface is used, this _really_ should be using
1854 the imap_multi_statemach function but we have no general support for
1855 non-blocking DONE operations, not in the multi state machine and with
1856 Curl_done() invokes on several places in the code!
1859 result = imap_block_statemach(conn);
1862 /* Cleanup our per-request based variables */
1863 Curl_safefree(imap->mailbox);
1864 Curl_safefree(imap->uidvalidity);
1865 Curl_safefree(imap->uid);
1866 Curl_safefree(imap->section);
1867 Curl_safefree(imap->custom);
1868 Curl_safefree(imap->custom_params);
1870 /* Clear the transfer mode for the next request */
1871 imap->transfer = FTPTRANSFER_BODY;
1876 /***********************************************************************
1880 * This is the actual DO function for IMAP. Fetch or append a message, or do
1881 * other things according to the options previously setup.
1883 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1886 /* This is IMAP and no proxy */
1887 CURLcode result = CURLE_OK;
1888 struct SessionHandle *data = conn->data;
1889 struct IMAP *imap = data->state.proto.imap;
1890 struct imap_conn *imapc = &conn->proto.imapc;
1891 bool selected = FALSE;
1893 DEBUGF(infof(conn->data, "DO phase starts\n"));
1895 if(conn->data->set.opt_no_body) {
1896 /* Requested no body means no transfer */
1897 imap->transfer = FTPTRANSFER_INFO;
1900 *dophase_done = FALSE; /* not done yet */
1902 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1903 has already been selected on this connection */
1904 if(imap->mailbox && imapc->mailbox &&
1905 !strcmp(imap->mailbox, imapc->mailbox) &&
1906 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1907 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1910 /* Start the first command in the DO phase */
1911 if(conn->data->set.upload)
1912 /* APPEND can be executed directly */
1913 result = imap_append(conn);
1914 else if(imap->custom && (selected || !imap->mailbox))
1915 /* Custom command using the same mailbox or no mailbox */
1916 result = imap_custom(conn);
1917 else if(!imap->custom && selected)
1918 /* FETCH from the same mailbox */
1919 result = imap_fetch(conn);
1921 /* SELECT the mailbox */
1922 result = imap_select(conn);
1927 /* Run the state-machine */
1928 result = imap_multi_statemach(conn, dophase_done);
1930 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1933 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1938 /***********************************************************************
1942 * This function is registered as 'curl_do' function. It decodes the path
1943 * parts etc as a wrapper to the actual DO function (imap_perform).
1945 * The input argument is already checked for validity.
1947 static CURLcode imap_do(struct connectdata *conn, bool *done)
1949 CURLcode result = CURLE_OK;
1951 *done = FALSE; /* default to false */
1953 /* Since connections can be re-used between SessionHandles, there might be a
1954 connection already existing but on a fresh SessionHandle struct. As such
1955 we make sure we have a good IMAP struct to play with. For new connections
1956 the IMAP struct is allocated and setup in the imap_connect() function. */
1957 Curl_reset_reqproto(conn);
1958 result = imap_init(conn);
1962 /* Parse the URL path */
1963 result = imap_parse_url_path(conn);
1967 /* Parse the custom request */
1968 result = imap_parse_custom_request(conn);
1972 result = imap_regular_transfer(conn, done);
1977 /***********************************************************************
1981 * This should be called before calling sclose(). We should then wait for the
1982 * response from the server before returning. The calling code should then try
1983 * to close the connection.
1986 static CURLcode imap_logout(struct connectdata *conn)
1988 CURLcode result = CURLE_OK;
1990 /* Send the LOGOUT command */
1991 result = imap_sendf(conn, "LOGOUT", NULL);
1995 state(conn, IMAP_LOGOUT);
1997 result = imap_block_statemach(conn);
2002 /***********************************************************************
2006 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
2007 * resources. BLOCKING.
2009 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2011 struct imap_conn *imapc = &conn->proto.imapc;
2013 /* We cannot send quit unconditionally. If this connection is stale or
2014 bad in any way, sending quit and waiting around here will make the
2015 disconnect wait in vain and cause more problems than we need to. */
2017 /* The IMAP session may or may not have been allocated/setup at this
2019 if(!dead_connection && imapc->pp.conn)
2020 (void)imap_logout(conn); /* ignore errors on LOGOUT */
2022 /* Disconnect from the server */
2023 Curl_pp_disconnect(&imapc->pp);
2025 /* Cleanup the SASL module */
2026 Curl_sasl_cleanup(conn, imapc->authused);
2028 /* Cleanup our connection based variables */
2029 Curl_safefree(imapc->mailbox);
2030 Curl_safefree(imapc->mailbox_uidvalidity);
2035 /***********************************************************************
2039 * Portable test of whether the specified char is a "bchar" as defined in the
2040 * grammar of RFC-5092.
2042 static bool imap_is_bchar(char ch)
2046 case ':': case '@': case '/':
2047 /* bchar -> achar */
2049 /* bchar -> achar -> uchar -> unreserved */
2050 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2051 case '7': case '8': case '9':
2052 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2053 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2054 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2055 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2056 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2057 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2058 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2059 case 'v': case 'w': case 'x': case 'y': case 'z':
2060 case '-': case '.': case '_': case '~':
2061 /* bchar -> achar -> uchar -> sub-delims-sh */
2062 case '!': case '$': case '\'': case '(': case ')': case '*':
2064 /* bchar -> achar -> uchar -> pct-encoded */
2065 case '%': /* HEXDIG chars are already included above */
2073 /***********************************************************************
2075 * imap_parse_url_path()
2077 * Parse the URL path into separate path components.
2080 static CURLcode imap_parse_url_path(struct connectdata *conn)
2082 /* The imap struct is already initialised in imap_connect() */
2083 CURLcode result = CURLE_OK;
2084 struct SessionHandle *data = conn->data;
2085 struct IMAP *imap = data->state.proto.imap;
2086 const char *begin = data->state.path;
2087 const char *ptr = begin;
2089 /* See how much of the URL is a valid path and decode it */
2090 while(imap_is_bchar(*ptr))
2094 /* Remove the trailing slash if present */
2095 const char *end = ptr;
2096 if(end > begin && end[-1] == '/')
2099 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2105 imap->mailbox = NULL;
2107 /* There can be any number of parameters in the form ";NAME=VALUE" */
2108 while(*ptr == ';') {
2113 /* Find the length of the name parameter */
2115 while(*ptr && *ptr != '=')
2119 return CURLE_URL_MALFORMAT;
2121 /* Decode the name parameter */
2122 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2126 /* Find the length of the value parameter */
2128 while(imap_is_bchar(*ptr))
2131 /* Decode the value parameter */
2132 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2134 Curl_safefree(name);
2138 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2140 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2141 stripping of the trailing slash character if it is present.
2143 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2144 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2145 if(valuelen > 0 && value[valuelen - 1] == '/')
2146 value[valuelen - 1] = '\0';
2148 imap->uidvalidity = value;
2151 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2152 if(valuelen > 0 && value[valuelen - 1] == '/')
2153 value[valuelen - 1] = '\0';
2158 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2159 if(valuelen > 0 && value[valuelen - 1] == '/')
2160 value[valuelen - 1] = '\0';
2162 imap->section = value;
2166 Curl_safefree(name);
2167 Curl_safefree(value);
2169 return CURLE_URL_MALFORMAT;
2172 Curl_safefree(name);
2173 Curl_safefree(value);
2176 /* Any extra stuff at the end of the URL is an error */
2178 return CURLE_URL_MALFORMAT;
2183 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2185 CURLcode result = CURLE_OK;
2186 struct SessionHandle *data = conn->data;
2187 struct IMAP *imap = data->state.proto.imap;
2188 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2191 /* URL decode the custom request */
2192 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2194 /* Extract the parameters if specified */
2196 const char *params = imap->custom;
2198 while(*params && *params != ' ')
2202 imap->custom_params = strdup(params);
2203 imap->custom[params - imap->custom] = '\0';
2205 if(!imap->custom_params)
2206 result = CURLE_OUT_OF_MEMORY;
2214 /* Call this when the DO phase has completed */
2215 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2217 struct IMAP *imap = conn->data->state.proto.imap;
2221 if(imap->transfer != FTPTRANSFER_BODY)
2222 /* no data to transfer */
2223 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2228 /* Called from multi.c while DOing */
2229 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2231 CURLcode result = imap_multi_statemach(conn, dophase_done);
2234 DEBUGF(infof(conn->data, "DO phase failed\n"));
2237 result = imap_dophase_done(conn, FALSE /* not connected */);
2239 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2246 /***********************************************************************
2248 * imap_regular_transfer()
2250 * The input argument is already checked for validity.
2252 * Performs all commands done before a regular transfer between a local and a
2255 static CURLcode imap_regular_transfer(struct connectdata *conn,
2258 CURLcode result = CURLE_OK;
2259 bool connected = FALSE;
2260 struct SessionHandle *data = conn->data;
2262 /* Make sure size is unknown at this point */
2263 data->req.size = -1;
2265 Curl_pgrsSetUploadCounter(data, 0);
2266 Curl_pgrsSetDownloadCounter(data, 0);
2267 Curl_pgrsSetUploadSize(data, 0);
2268 Curl_pgrsSetDownloadSize(data, 0);
2270 result = imap_perform(conn, &connected, dophase_done);
2274 /* The DO phase has not completed yet */
2277 result = imap_dophase_done(conn, connected);
2283 static CURLcode imap_setup_connection(struct connectdata *conn)
2285 struct SessionHandle *data = conn->data;
2287 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2288 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2289 switch and use HTTP operations only */
2290 #ifndef CURL_DISABLE_HTTP
2291 if(conn->handler == &Curl_handler_imap)
2292 conn->handler = &Curl_handler_imap_proxy;
2295 conn->handler = &Curl_handler_imaps_proxy;
2297 failf(data, "IMAPS not supported!");
2298 return CURLE_UNSUPPORTED_PROTOCOL;
2302 /* We explicitly mark this connection as persistent here as we're doing
2303 IMAP over HTTP and thus we accidentally avoid setting this value
2305 conn->bits.close = FALSE;
2307 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2308 return CURLE_UNSUPPORTED_PROTOCOL;
2312 data->state.path++; /* don't include the initial slash */
2317 #endif /* CURL_DISABLE_IMAP */