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 STORE, which returns untagged responses as FETCH,
419 and SELECT and EXAMINE commands, for which no filtering is (or can
421 if(!imap_matchresp(line, len, imap->custom) &&
422 (strcmp(imap->custom, "STORE") ||
423 !imap_matchresp(line, len, "FETCH")) &&
424 strcmp(imap->custom, "SELECT") &&
425 strcmp(imap->custom, "EXAMINE"))
429 /* Ignore other untagged responses */
438 /* Do we have a continuation response? */
439 if((len == 3 && !memcmp("+", line, 1)) ||
440 (len >= 2 && !memcmp("+ ", line, 2))) {
441 switch(imapc->state) {
442 /* States which are interested in continuation responses */
443 case IMAP_AUTHENTICATE_PLAIN:
444 case IMAP_AUTHENTICATE_LOGIN:
445 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
446 case IMAP_AUTHENTICATE_CRAMMD5:
447 case IMAP_AUTHENTICATE_DIGESTMD5:
448 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
449 case IMAP_AUTHENTICATE_NTLM:
450 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
451 case IMAP_AUTHENTICATE_FINAL:
457 failf(conn->data, "Unexpected continuation response");
465 return FALSE; /* Nothing for us */
468 /* This is the ONLY way to change IMAP state! */
469 static void state(struct connectdata *conn, imapstate newstate)
471 struct imap_conn *imapc = &conn->proto.imapc;
472 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
473 /* for debug purposes */
474 static const char * const names[]={
480 "AUTHENTICATE_PLAIN",
481 "AUTHENTICATE_LOGIN",
482 "AUTHENTICATE_LOGIN_PASSWD",
483 "AUTHENTICATE_CRAMMD5",
484 "AUTHENTICATE_DIGESTMD5",
485 "AUTHENTICATE_DIGESTMD5_RESP",
487 "AUTHENTICATE_NTLM_TYPE2MSG",
488 "AUTHENTICATE_FINAL",
501 if(imapc->state != newstate)
502 infof(conn->data, "IMAP %p state change from %s to %s\n",
503 imapc, names[imapc->state], names[newstate]);
506 imapc->state = newstate;
509 static CURLcode imap_state_capability(struct connectdata *conn)
511 CURLcode result = CURLE_OK;
512 struct imap_conn *imapc = &conn->proto.imapc;
514 imapc->authmechs = 0; /* No known authentication mechanisms yet */
515 imapc->authused = 0; /* Clear the authentication mechanism used */
516 imapc->tls_supported = FALSE; /* Clear the TLS capability */
518 /* Send the CAPABILITY command */
519 result = imap_sendf(conn, "CAPABILITY");
522 state(conn, IMAP_CAPABILITY);
527 static CURLcode imap_state_starttls(struct connectdata *conn)
529 CURLcode result = CURLE_OK;
531 /* Send the STARTTLS command */
532 result = imap_sendf(conn, "STARTTLS");
535 state(conn, IMAP_STARTTLS);
540 static CURLcode imap_state_upgrade_tls(struct connectdata *conn)
542 CURLcode result = CURLE_OK;
543 struct imap_conn *imapc = &conn->proto.imapc;
545 /* Start the SSL connection */
546 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
549 if(imapc->state != IMAP_UPGRADETLS)
550 state(conn, IMAP_UPGRADETLS);
554 result = imap_state_capability(conn);
561 static CURLcode imap_state_login(struct connectdata *conn)
563 CURLcode result = CURLE_OK;
567 /* Check we have a username and password to authenticate with and end the
568 connect phase if we don't */
569 if(!conn->bits.user_passwd) {
570 state(conn, IMAP_STOP);
575 /* Make sure the username and password are in the correct atom format */
576 user = imap_atom(conn->user);
577 passwd = imap_atom(conn->passwd);
579 /* Send the LOGIN command */
580 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
581 passwd ? passwd : "");
584 Curl_safefree(passwd);
587 state(conn, IMAP_LOGIN);
592 static CURLcode imap_authenticate(struct connectdata *conn)
594 CURLcode result = CURLE_OK;
595 struct imap_conn *imapc = &conn->proto.imapc;
596 const char *mech = NULL;
597 char *initresp = NULL;
599 imapstate state1 = IMAP_STOP;
600 imapstate state2 = IMAP_STOP;
602 /* Check we have a username and password to authenticate with and end the
603 connect phase if we don't */
604 if(!conn->bits.user_passwd) {
605 state(conn, IMAP_STOP);
610 /* Calculate the supported authentication mechanism by decreasing order of
612 #ifndef CURL_DISABLE_CRYPTO_AUTH
613 if(imapc->authmechs & SASL_MECH_DIGEST_MD5) {
615 state1 = IMAP_AUTHENTICATE_DIGESTMD5;
616 imapc->authused = SASL_MECH_DIGEST_MD5;
618 else if(imapc->authmechs & SASL_MECH_CRAM_MD5) {
620 state1 = IMAP_AUTHENTICATE_CRAMMD5;
621 imapc->authused = SASL_MECH_CRAM_MD5;
626 if(imapc->authmechs & SASL_MECH_NTLM) {
628 state1 = IMAP_AUTHENTICATE_NTLM;
629 state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
630 imapc->authused = SASL_MECH_NTLM;
632 if(imapc->ir_supported)
633 result = Curl_sasl_create_login_message(conn->data, conn->user,
638 if(imapc->authmechs & SASL_MECH_LOGIN) {
640 state1 = IMAP_AUTHENTICATE_LOGIN;
641 state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
642 imapc->authused = SASL_MECH_LOGIN;
644 if(imapc->ir_supported)
645 result = Curl_sasl_create_plain_message(conn->data, conn->user,
646 conn->passwd, &initresp, &len);
648 else if(imapc->authmechs & SASL_MECH_PLAIN) {
650 state1 = IMAP_AUTHENTICATE_PLAIN;
651 state2 = IMAP_AUTHENTICATE_FINAL;
652 imapc->authused = SASL_MECH_PLAIN;
654 if(imapc->ir_supported)
655 result = Curl_sasl_create_plain_message(conn->data, conn->user,
656 conn->passwd, &initresp, &len);
664 /* Perform SASL based authentication */
665 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
671 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
677 Curl_safefree(initresp);
679 else if(!imapc->login_disabled)
680 /* Perform clear text authentication */
681 result = imap_state_login(conn);
683 /* Other mechanisms not supported */
684 infof(conn->data, "No known authentication mechanisms supported!\n");
685 result = CURLE_LOGIN_DENIED;
691 /* Start the DO phase */
692 static CURLcode imap_list(struct connectdata *conn)
694 CURLcode result = CURLE_OK;
695 struct SessionHandle *data = conn->data;
696 struct IMAP *imap = data->state.proto.imap;
699 /* Make sure the mailbox is in the correct atom format */
700 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
702 return CURLE_OUT_OF_MEMORY;
704 /* Send the LIST command */
705 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
707 Curl_safefree(mailbox);
710 state(conn, IMAP_LIST);
715 static CURLcode imap_select(struct connectdata *conn)
717 CURLcode result = CURLE_OK;
718 struct SessionHandle *data = conn->data;
719 struct IMAP *imap = data->state.proto.imap;
720 struct imap_conn *imapc = &conn->proto.imapc;
723 /* Invalidate old information as we are switching mailboxes */
724 Curl_safefree(imapc->mailbox);
725 Curl_safefree(imapc->mailbox_uidvalidity);
727 /* Check we have a mailbox */
729 failf(conn->data, "Cannot SELECT without a mailbox.");
730 return CURLE_URL_MALFORMAT;
733 /* Make sure the mailbox is in the correct atom format */
734 mailbox = imap_atom(imap->mailbox);
736 return CURLE_OUT_OF_MEMORY;
738 /* Send the SELECT command */
739 result = imap_sendf(conn, "SELECT %s", mailbox);
741 Curl_safefree(mailbox);
744 state(conn, IMAP_SELECT);
749 static CURLcode imap_fetch(struct connectdata *conn)
751 CURLcode result = CURLE_OK;
752 struct IMAP *imap = conn->data->state.proto.imap;
754 /* Check we have a UID */
756 failf(conn->data, "Cannot FETCH without a UID.");
757 return CURLE_URL_MALFORMAT;
760 /* Send the FETCH command */
761 result = imap_sendf(conn, "FETCH %s BODY[%s]",
763 imap->section ? imap->section : "");
766 state(conn, IMAP_FETCH);
771 static CURLcode imap_append(struct connectdata *conn)
773 CURLcode result = CURLE_OK;
774 struct IMAP *imap = conn->data->state.proto.imap;
777 /* Check we have a mailbox */
779 failf(conn->data, "Cannot APPEND without a mailbox.");
780 return CURLE_URL_MALFORMAT;
783 /* Check we know the size of the upload */
784 if(conn->data->set.infilesize < 0) {
785 failf(conn->data, "Cannot APPEND with unknown input file size\n");
786 return CURLE_UPLOAD_FAILED;
789 /* Make sure the mailbox is in the correct atom format */
790 mailbox = imap_atom(imap->mailbox);
792 return CURLE_OUT_OF_MEMORY;
794 /* Send the APPEND command */
795 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
796 mailbox, conn->data->set.infilesize);
798 Curl_safefree(mailbox);
801 state(conn, IMAP_APPEND);
806 static CURLcode imap_custom(struct connectdata *conn)
808 struct IMAP *imap = conn->data->state.proto.imap;
810 /* Send the custom request */
811 CURLcode result = imap_sendf(conn, "%s%s", imap->custom,
812 imap->custom_params ? imap->custom_params : "");
815 /* We don't know how much data will be received */
816 Curl_pgrsSetDownloadSize(conn->data, -1);
818 state(conn, IMAP_CUSTOM);
824 /* For the initial server greeting */
825 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
829 CURLcode result = CURLE_OK;
830 struct SessionHandle *data = conn->data;
832 (void)instate; /* no use for this yet */
834 if(imapcode != 'O') {
835 failf(data, "Got unexpected imap-server response");
836 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
839 result = imap_state_capability(conn);
844 /* For CAPABILITY responses */
845 static CURLcode imap_state_capability_resp(struct connectdata *conn,
849 CURLcode result = CURLE_OK;
850 struct SessionHandle *data = conn->data;
851 struct imap_conn *imapc = &conn->proto.imapc;
852 const char *line = data->state.buffer;
855 (void)instate; /* no use for this yet */
857 /* Do we have a untagged response? */
858 if(imapcode == '*') {
861 /* Loop through the data line */
864 (*line == ' ' || *line == '\t' ||
865 *line == '\r' || *line == '\n')) {
873 /* Extract the word */
874 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
875 line[wordlen] != '\t' && line[wordlen] != '\r' &&
876 line[wordlen] != '\n';)
879 /* Does the server support the STARTTLS capability? */
880 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
881 imapc->tls_supported = TRUE;
883 /* Has the server explicitly disabled clear text authentication? */
884 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
885 imapc->login_disabled = TRUE;
887 /* Does the server support the SASL-IR capability? */
888 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
889 imapc->ir_supported = TRUE;
891 /* Do we have a SASL based authentication mechanism? */
892 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
896 /* Test the word for a matching authentication mechanism */
897 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
898 imapc->authmechs |= SASL_MECH_LOGIN;
899 if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
900 imapc->authmechs |= SASL_MECH_PLAIN;
901 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
902 imapc->authmechs |= SASL_MECH_CRAM_MD5;
903 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
904 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
905 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
906 imapc->authmechs |= SASL_MECH_GSSAPI;
907 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
908 imapc->authmechs |= SASL_MECH_EXTERNAL;
909 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
910 imapc->authmechs |= SASL_MECH_NTLM;
916 else if(imapcode == 'O') {
917 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
918 /* We don't have a SSL/TLS connection yet, but SSL is requested */
919 if(imapc->tls_supported)
920 /* Switch to TLS connection now */
921 result = imap_state_starttls(conn);
922 else if(data->set.use_ssl == CURLUSESSL_TRY)
923 /* Fallback and carry on with authentication */
924 result = imap_authenticate(conn);
926 failf(data, "STARTTLS not supported.");
927 result = CURLE_USE_SSL_FAILED;
931 result = imap_authenticate(conn);
934 result = imap_state_login(conn);
939 /* For STARTTLS responses */
940 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
944 CURLcode result = CURLE_OK;
945 struct SessionHandle *data = conn->data;
947 (void)instate; /* no use for this yet */
949 if(imapcode != 'O') {
950 if(data->set.use_ssl != CURLUSESSL_TRY) {
951 failf(data, "STARTTLS denied. %c", imapcode);
952 result = CURLE_USE_SSL_FAILED;
955 result = imap_authenticate(conn);
958 result = imap_state_upgrade_tls(conn);
963 /* For AUTHENTICATE PLAIN (without initial response) responses */
964 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
968 CURLcode result = CURLE_OK;
969 struct SessionHandle *data = conn->data;
971 char *plainauth = NULL;
973 (void)instate; /* no use for this yet */
975 if(imapcode != '+') {
976 failf(data, "Access denied. %c", imapcode);
977 result = CURLE_LOGIN_DENIED;
980 /* Create the authorisation message */
981 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
984 /* Send the message */
987 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
990 state(conn, IMAP_AUTHENTICATE_FINAL);
993 Curl_safefree(plainauth);
1000 /* For AUTHENTICATE LOGIN (without initial response) responses */
1001 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1005 CURLcode result = CURLE_OK;
1006 struct SessionHandle *data = conn->data;
1008 char *authuser = NULL;
1010 (void)instate; /* no use for this yet */
1012 if(imapcode != '+') {
1013 failf(data, "Access denied: %d", imapcode);
1014 result = CURLE_LOGIN_DENIED;
1017 /* Create the user message */
1018 result = Curl_sasl_create_login_message(data, conn->user,
1024 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1027 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1030 Curl_safefree(authuser);
1037 /* For AUTHENTICATE LOGIN user entry responses */
1038 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1042 CURLcode result = CURLE_OK;
1043 struct SessionHandle *data = conn->data;
1045 char *authpasswd = NULL;
1047 (void)instate; /* no use for this yet */
1049 if(imapcode != '+') {
1050 failf(data, "Access denied: %d", imapcode);
1051 result = CURLE_LOGIN_DENIED;
1054 /* Create the password message */
1055 result = Curl_sasl_create_login_message(data, conn->passwd,
1058 /* Send the password */
1061 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1064 state(conn, IMAP_AUTHENTICATE_FINAL);
1067 Curl_safefree(authpasswd);
1074 #ifndef CURL_DISABLE_CRYPTO_AUTH
1075 /* For AUTHENTICATE CRAM-MD5 responses */
1076 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1080 CURLcode result = CURLE_OK;
1081 struct SessionHandle *data = conn->data;
1082 char *chlg64 = data->state.buffer;
1084 char *rplyb64 = NULL;
1086 (void)instate; /* no use for this yet */
1088 if(imapcode != '+') {
1089 failf(data, "Access denied: %d", imapcode);
1090 return CURLE_LOGIN_DENIED;
1093 /* Get the challenge */
1094 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1097 /* Terminate the challenge */
1098 if(*chlg64 != '=') {
1099 for(len = strlen(chlg64); len--;)
1100 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
1101 chlg64[len] != '\t')
1109 /* Create the response message */
1110 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
1111 conn->passwd, &rplyb64, &len);
1113 /* Send the response */
1116 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1119 state(conn, IMAP_AUTHENTICATE_FINAL);
1122 Curl_safefree(rplyb64);
1128 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1129 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1133 CURLcode result = CURLE_OK;
1134 struct SessionHandle *data = conn->data;
1135 char *chlg64 = data->state.buffer;
1137 char *rplyb64 = NULL;
1139 (void)instate; /* no use for this yet */
1141 if(imapcode != '+') {
1142 failf(data, "Access denied: %d", imapcode);
1143 return CURLE_LOGIN_DENIED;
1146 /* Get the challenge */
1147 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1150 /* Create the response message */
1151 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
1152 conn->passwd, "imap",
1155 /* Send the response */
1158 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1161 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1164 Curl_safefree(rplyb64);
1170 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1171 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1175 CURLcode result = CURLE_OK;
1176 struct SessionHandle *data = conn->data;
1178 (void)instate; /* no use for this yet */
1180 if(imapcode != '+') {
1181 failf(data, "Authentication failed: %d", imapcode);
1182 result = CURLE_LOGIN_DENIED;
1185 /* Send an empty response */
1186 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1189 state(conn, IMAP_AUTHENTICATE_FINAL);
1197 /* For AUTHENTICATE NTLM (without initial response) responses */
1198 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1202 CURLcode result = CURLE_OK;
1203 struct SessionHandle *data = conn->data;
1205 char *type1msg = NULL;
1207 (void)instate; /* no use for this yet */
1209 if(imapcode != '+') {
1210 failf(data, "Access denied: %d", imapcode);
1211 result = CURLE_LOGIN_DENIED;
1214 /* Create the type-1 message */
1215 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1219 /* Send the message */
1222 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1225 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1228 Curl_safefree(type1msg);
1235 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1236 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1240 CURLcode result = CURLE_OK;
1241 struct SessionHandle *data = conn->data;
1243 char *type3msg = NULL;
1245 (void)instate; /* no use for this yet */
1247 if(imapcode != '+') {
1248 failf(data, "Access denied: %d", imapcode);
1249 result = CURLE_LOGIN_DENIED;
1252 /* Create the type-3 message */
1253 result = Curl_sasl_create_ntlm_type3_message(data,
1254 data->state.buffer + 2,
1255 conn->user, conn->passwd,
1259 /* Send the message */
1262 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1265 state(conn, IMAP_AUTHENTICATE_FINAL);
1268 Curl_safefree(type3msg);
1276 /* For final responses to the AUTHENTICATE sequence */
1277 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1281 CURLcode result = CURLE_OK;
1282 struct SessionHandle *data = conn->data;
1284 (void)instate; /* no use for this yet */
1286 if(imapcode != 'O') {
1287 failf(data, "Authentication failed: %d", imapcode);
1288 result = CURLE_LOGIN_DENIED;
1291 /* End of connect phase */
1292 state(conn, IMAP_STOP);
1297 /* For LOGIN responses */
1298 static CURLcode imap_state_login_resp(struct connectdata *conn,
1302 CURLcode result = CURLE_OK;
1303 struct SessionHandle *data = conn->data;
1305 (void)instate; /* no use for this yet */
1307 if(imapcode != 'O') {
1308 failf(data, "Access denied. %c", imapcode);
1309 result = CURLE_LOGIN_DENIED;
1312 /* End of connect phase */
1313 state(conn, IMAP_STOP);
1318 /* For LIST responses */
1319 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1322 CURLcode result = CURLE_OK;
1323 char *line = conn->data->state.buffer;
1324 size_t len = strlen(line);
1326 (void)instate; /* No use for this yet */
1328 if(imapcode == '*') {
1329 /* Temporarily add the LF character back and send as body to the client */
1331 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1334 else if(imapcode != 'O')
1335 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1337 /* End of DO phase */
1338 state(conn, IMAP_STOP);
1343 /* For SELECT responses */
1344 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1347 CURLcode result = CURLE_OK;
1348 struct SessionHandle *data = conn->data;
1349 struct IMAP *imap = conn->data->state.proto.imap;
1350 struct imap_conn *imapc = &conn->proto.imapc;
1351 const char *line = data->state.buffer;
1354 (void)instate; /* no use for this yet */
1356 if(imapcode == '*') {
1357 /* See if this is an UIDVALIDITY response */
1358 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1359 Curl_safefree(imapc->mailbox_uidvalidity);
1360 imapc->mailbox_uidvalidity = strdup(tmp);
1363 else if(imapcode == 'O') {
1364 /* Check if the UIDVALIDITY has been specified and matches */
1365 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1366 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1367 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1368 result = CURLE_REMOTE_FILE_NOT_FOUND;
1371 /* Note the currently opened mailbox on this connection */
1372 imapc->mailbox = strdup(imap->mailbox);
1375 result = imap_custom(conn);
1377 result = imap_fetch(conn);
1381 failf(data, "Select failed");
1382 result = CURLE_LOGIN_DENIED;
1388 /* For the (first line of the) FETCH responses */
1389 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1392 CURLcode result = CURLE_OK;
1393 struct SessionHandle *data = conn->data;
1394 struct imap_conn *imapc = &conn->proto.imapc;
1395 struct pingpong *pp = &imapc->pp;
1396 const char *ptr = data->state.buffer;
1397 bool parsed = FALSE;
1400 (void)instate; /* no use for this yet */
1402 if(imapcode != '*') {
1403 Curl_pgrsSetDownloadSize(data, 0);
1404 state(conn, IMAP_STOP);
1405 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1408 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1409 the continuation data contained within the curly brackets */
1410 while(*ptr && (*ptr != '{'))
1415 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1416 if(endptr - ptr > 1 && endptr[0] == '}' &&
1417 endptr[1] == '\r' && endptr[2] == '\0')
1422 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1423 Curl_pgrsSetDownloadSize(data, size);
1426 /* At this point there is a bunch of data in the header "cache" that is
1427 actually body content, send it as body and then skip it. Do note
1428 that there may even be additional "headers" after the body. */
1429 size_t chunk = pp->cache_size;
1431 if(chunk > (size_t)size)
1432 /* The conversion from curl_off_t to size_t is always fine here */
1433 chunk = (size_t)size;
1435 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1439 data->req.bytecount += chunk;
1442 infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1443 " bytes are left for transfer\n", (curl_off_t)chunk, size);
1445 /* Have we used the entire cache or just part of it?*/
1446 if(pp->cache_size > chunk) {
1447 /* Only part of it so shrink the cache to fit the trailing data */
1448 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1449 pp->cache_size -= chunk;
1452 /* Free the cache */
1453 Curl_safefree(pp->cache);
1455 /* Reset the cache size */
1461 /* The entire data is already transferred! */
1462 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1465 data->req.maxdownload = size;
1466 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1470 /* We don't know how to parse this line */
1471 failf(pp->conn->data, "Failed to parse FETCH response.");
1472 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1475 /* End of DO phase */
1476 state(conn, IMAP_STOP);
1481 /* For final FETCH responses performed after the download */
1482 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1486 CURLcode result = CURLE_OK;
1488 (void)instate; /* No use for this yet */
1491 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1493 /* End of DONE phase */
1494 state(conn, IMAP_STOP);
1499 /* For APPEND responses */
1500 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1503 CURLcode result = CURLE_OK;
1504 struct SessionHandle *data = conn->data;
1506 (void)instate; /* No use for this yet */
1508 if(imapcode != '+') {
1509 result = CURLE_UPLOAD_FAILED;
1512 /* Set the progress upload size */
1513 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1516 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1518 /* End of DO phase */
1519 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 */
1535 result = CURLE_UPLOAD_FAILED;
1537 /* End of DONE phase */
1538 state(conn, IMAP_STOP);
1543 static CURLcode imap_statemach_act(struct connectdata *conn)
1545 CURLcode result = CURLE_OK;
1546 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1548 struct imap_conn *imapc = &conn->proto.imapc;
1549 struct pingpong *pp = &imapc->pp;
1552 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1553 if(imapc->state == IMAP_UPGRADETLS)
1554 return imap_state_upgrade_tls(conn);
1556 /* Flush any data that needs to be sent */
1558 return Curl_pp_flushsend(pp);
1561 /* Read the response from the server */
1562 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1566 /* Was there an error parsing the response line? */
1568 return CURLE_FTP_WEIRD_SERVER_REPLY;
1573 /* We have now received a full IMAP server response */
1574 switch(imapc->state) {
1575 case IMAP_SERVERGREET:
1576 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1579 case IMAP_CAPABILITY:
1580 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1584 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1587 case IMAP_AUTHENTICATE_PLAIN:
1588 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1591 case IMAP_AUTHENTICATE_LOGIN:
1592 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1595 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1596 result = imap_state_auth_login_password_resp(conn, imapcode,
1600 #ifndef CURL_DISABLE_CRYPTO_AUTH
1601 case IMAP_AUTHENTICATE_CRAMMD5:
1602 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1605 case IMAP_AUTHENTICATE_DIGESTMD5:
1606 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1609 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1610 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1615 case IMAP_AUTHENTICATE_NTLM:
1616 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1619 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1620 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1625 case IMAP_AUTHENTICATE_FINAL:
1626 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1630 result = imap_state_login_resp(conn, imapcode, imapc->state);
1635 result = imap_state_list_resp(conn, imapcode, imapc->state);
1639 result = imap_state_select_resp(conn, imapcode, imapc->state);
1643 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1646 case IMAP_FETCH_FINAL:
1647 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1651 result = imap_state_append_resp(conn, imapcode, imapc->state);
1654 case IMAP_APPEND_FINAL:
1655 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1659 /* fallthrough, just stop! */
1661 /* internal error */
1662 state(conn, IMAP_STOP);
1665 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1670 /* Called repeatedly until done from multi.c */
1671 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1673 CURLcode result = CURLE_OK;
1674 struct imap_conn *imapc = &conn->proto.imapc;
1676 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1677 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1679 result = Curl_pp_statemach(&imapc->pp, FALSE);
1681 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1686 static CURLcode imap_block_statemach(struct connectdata *conn)
1688 CURLcode result = CURLE_OK;
1689 struct imap_conn *imapc = &conn->proto.imapc;
1691 while(imapc->state != IMAP_STOP && !result)
1692 result = Curl_pp_statemach(&imapc->pp, TRUE);
1697 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1699 static CURLcode imap_init(struct connectdata *conn)
1701 struct SessionHandle *data = conn->data;
1702 struct IMAP *imap = data->state.proto.imap;
1705 imap = data->state.proto.imap = calloc(sizeof(struct IMAP), 1);
1707 return CURLE_OUT_OF_MEMORY;
1713 /* For the IMAP "protocol connect" and "doing" phases only */
1714 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1717 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1720 /***********************************************************************
1722 * imap_connect() should do everything that is to be considered a part of
1723 * the connection phase.
1725 * The variable 'done' points to will be TRUE if the protocol-layer connect
1726 * phase is done when this function returns, or FALSE is not. When called as
1727 * a part of the easy interface, it will always be TRUE.
1729 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1731 CURLcode result = CURLE_OK;
1732 struct imap_conn *imapc = &conn->proto.imapc;
1733 struct pingpong *pp = &imapc->pp;
1735 *done = FALSE; /* default to not done yet */
1737 /* If there already is a protocol-specific struct allocated for this
1738 sessionhandle, deal with it */
1739 Curl_reset_reqproto(conn);
1741 /* Initialise the IMAP layer */
1742 result = imap_init(conn);
1746 /* We always support persistent connections in IMAP */
1747 conn->bits.close = FALSE;
1749 /* Set the default response time-out */
1750 pp->response_time = RESP_TIMEOUT;
1751 pp->statemach_act = imap_statemach_act;
1752 pp->endofresp = imap_endofresp;
1755 /* Initialise the pingpong layer */
1758 /* Start off waiting for the server greeting response */
1759 state(conn, IMAP_SERVERGREET);
1761 /* Start off with an response id of '*' */
1762 strcpy(imapc->resptag, "*");
1764 result = imap_multi_statemach(conn, done);
1769 /***********************************************************************
1773 * The DONE function. This does what needs to be done after a single DO has
1776 * Input argument is already checked for validity.
1778 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1781 CURLcode result = CURLE_OK;
1782 struct SessionHandle *data = conn->data;
1783 struct IMAP *imap = data->state.proto.imap;
1788 /* When the easy handle is removed from the multi interface while libcurl
1789 is still trying to resolve the host name, the IMAP struct is not yet
1790 initialized. However, the removal action calls Curl_done() which in
1791 turn calls this function, so we simply return success. */
1795 conn->bits.close = TRUE; /* marked for closure */
1796 result = status; /* use the already set error code */
1798 else if(!data->set.connect_only && !imap->custom &&
1799 (imap->uid || data->set.upload)) {
1800 /* Handle responses after FETCH or APPEND transfer has finished */
1801 if(!data->set.upload)
1802 state(conn, IMAP_FETCH_FINAL);
1804 /* End the APPEND command first by sending an empty line */
1805 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1807 state(conn, IMAP_APPEND_FINAL);
1810 /* Run the state-machine
1812 TODO: when the multi interface is used, this _really_ should be using
1813 the imap_multi_statemach function but we have no general support for
1814 non-blocking DONE operations, not in the multi state machine and with
1815 Curl_done() invokes on several places in the code!
1818 result = imap_block_statemach(conn);
1821 /* Cleanup our per-request based variables */
1822 Curl_safefree(imap->mailbox);
1823 Curl_safefree(imap->uidvalidity);
1824 Curl_safefree(imap->uid);
1825 Curl_safefree(imap->section);
1826 Curl_safefree(imap->custom);
1827 Curl_safefree(imap->custom_params);
1829 /* Clear the transfer mode for the next request */
1830 imap->transfer = FTPTRANSFER_BODY;
1835 /***********************************************************************
1839 * This is the actual DO function for IMAP. Fetch or append a message, or do
1840 * other things according to the options previously setup.
1842 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1845 /* This is IMAP and no proxy */
1846 CURLcode result = CURLE_OK;
1847 struct SessionHandle *data = conn->data;
1848 struct IMAP *imap = data->state.proto.imap;
1849 struct imap_conn *imapc = &conn->proto.imapc;
1850 bool selected = FALSE;
1852 DEBUGF(infof(conn->data, "DO phase starts\n"));
1854 if(conn->data->set.opt_no_body) {
1855 /* Requested no body means no transfer */
1856 imap->transfer = FTPTRANSFER_INFO;
1859 *dophase_done = FALSE; /* not done yet */
1861 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1862 has already been selected on this connection */
1863 if(imap->mailbox && imapc->mailbox &&
1864 !strcmp(imap->mailbox, imapc->mailbox) &&
1865 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1866 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1869 /* Start the first command in the DO phase */
1870 if(conn->data->set.upload)
1871 /* APPEND can be executed directly */
1872 result = imap_append(conn);
1873 else if(imap->custom && (selected || !imap->mailbox))
1874 /* Custom command using the same mailbox or no mailbox */
1875 result = imap_custom(conn);
1876 else if(!imap->custom && selected && imap->uid)
1877 /* FETCH from the same mailbox */
1878 result = imap_fetch(conn);
1879 else if(imap->mailbox && !selected && (imap->custom || imap->uid))
1880 /* SELECT the mailbox */
1881 result = imap_select(conn);
1884 result = imap_list(conn);
1889 /* Run the state-machine */
1890 result = imap_multi_statemach(conn, dophase_done);
1892 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1895 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1900 /***********************************************************************
1904 * This function is registered as 'curl_do' function. It decodes the path
1905 * parts etc as a wrapper to the actual DO function (imap_perform).
1907 * The input argument is already checked for validity.
1909 static CURLcode imap_do(struct connectdata *conn, bool *done)
1911 CURLcode result = CURLE_OK;
1913 *done = FALSE; /* default to false */
1915 /* Since connections can be re-used between SessionHandles, there might be a
1916 connection already existing but on a fresh SessionHandle struct. As such
1917 we make sure we have a good IMAP struct to play with. For new connections
1918 the IMAP struct is allocated and setup in the imap_connect() function. */
1919 Curl_reset_reqproto(conn);
1920 result = imap_init(conn);
1924 /* Parse the URL path */
1925 result = imap_parse_url_path(conn);
1929 /* Parse the custom request */
1930 result = imap_parse_custom_request(conn);
1934 result = imap_regular_transfer(conn, done);
1939 /***********************************************************************
1943 * This should be called before calling sclose(). We should then wait for the
1944 * response from the server before returning. The calling code should then try
1945 * to close the connection.
1948 static CURLcode imap_logout(struct connectdata *conn)
1950 CURLcode result = CURLE_OK;
1952 /* Send the LOGOUT command */
1953 result = imap_sendf(conn, "LOGOUT", NULL);
1957 state(conn, IMAP_LOGOUT);
1959 result = imap_block_statemach(conn);
1964 /***********************************************************************
1968 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1969 * resources. BLOCKING.
1971 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1973 struct imap_conn *imapc = &conn->proto.imapc;
1975 /* We cannot send quit unconditionally. If this connection is stale or
1976 bad in any way, sending quit and waiting around here will make the
1977 disconnect wait in vain and cause more problems than we need to. */
1979 /* The IMAP session may or may not have been allocated/setup at this
1981 if(!dead_connection && imapc->pp.conn)
1982 (void)imap_logout(conn); /* ignore errors on LOGOUT */
1984 /* Disconnect from the server */
1985 Curl_pp_disconnect(&imapc->pp);
1987 /* Cleanup the SASL module */
1988 Curl_sasl_cleanup(conn, imapc->authused);
1990 /* Cleanup our connection based variables */
1991 Curl_safefree(imapc->mailbox);
1992 Curl_safefree(imapc->mailbox_uidvalidity);
1997 /***********************************************************************
2001 * Portable test of whether the specified char is a "bchar" as defined in the
2002 * grammar of RFC-5092.
2004 static bool imap_is_bchar(char ch)
2008 case ':': case '@': case '/':
2009 /* bchar -> achar */
2011 /* bchar -> achar -> uchar -> unreserved */
2012 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2013 case '7': case '8': case '9':
2014 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2015 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2016 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2017 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2018 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2019 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2020 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2021 case 'v': case 'w': case 'x': case 'y': case 'z':
2022 case '-': case '.': case '_': case '~':
2023 /* bchar -> achar -> uchar -> sub-delims-sh */
2024 case '!': case '$': case '\'': case '(': case ')': case '*':
2026 /* bchar -> achar -> uchar -> pct-encoded */
2027 case '%': /* HEXDIG chars are already included above */
2035 /***********************************************************************
2037 * imap_parse_url_path()
2039 * Parse the URL path into separate path components.
2042 static CURLcode imap_parse_url_path(struct connectdata *conn)
2044 /* The imap struct is already initialised in imap_connect() */
2045 CURLcode result = CURLE_OK;
2046 struct SessionHandle *data = conn->data;
2047 struct IMAP *imap = data->state.proto.imap;
2048 const char *begin = data->state.path;
2049 const char *ptr = begin;
2051 /* See how much of the URL is a valid path and decode it */
2052 while(imap_is_bchar(*ptr))
2056 /* Remove the trailing slash if present */
2057 const char *end = ptr;
2058 if(end > begin && end[-1] == '/')
2061 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2067 imap->mailbox = NULL;
2069 /* There can be any number of parameters in the form ";NAME=VALUE" */
2070 while(*ptr == ';') {
2075 /* Find the length of the name parameter */
2077 while(*ptr && *ptr != '=')
2081 return CURLE_URL_MALFORMAT;
2083 /* Decode the name parameter */
2084 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2088 /* Find the length of the value parameter */
2090 while(imap_is_bchar(*ptr))
2093 /* Decode the value parameter */
2094 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2096 Curl_safefree(name);
2100 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2102 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2103 stripping of the trailing slash character if it is present.
2105 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2106 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2107 if(valuelen > 0 && value[valuelen - 1] == '/')
2108 value[valuelen - 1] = '\0';
2110 imap->uidvalidity = value;
2113 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2114 if(valuelen > 0 && value[valuelen - 1] == '/')
2115 value[valuelen - 1] = '\0';
2120 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2121 if(valuelen > 0 && value[valuelen - 1] == '/')
2122 value[valuelen - 1] = '\0';
2124 imap->section = value;
2128 Curl_safefree(name);
2129 Curl_safefree(value);
2131 return CURLE_URL_MALFORMAT;
2134 Curl_safefree(name);
2135 Curl_safefree(value);
2138 /* Any extra stuff at the end of the URL is an error */
2140 return CURLE_URL_MALFORMAT;
2145 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2147 CURLcode result = CURLE_OK;
2148 struct SessionHandle *data = conn->data;
2149 struct IMAP *imap = data->state.proto.imap;
2150 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2153 /* URL decode the custom request */
2154 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2156 /* Extract the parameters if specified */
2158 const char *params = imap->custom;
2160 while(*params && *params != ' ')
2164 imap->custom_params = strdup(params);
2165 imap->custom[params - imap->custom] = '\0';
2167 if(!imap->custom_params)
2168 result = CURLE_OUT_OF_MEMORY;
2176 /* Call this when the DO phase has completed */
2177 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2179 struct IMAP *imap = conn->data->state.proto.imap;
2183 if(imap->transfer != FTPTRANSFER_BODY)
2184 /* no data to transfer */
2185 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2190 /* Called from multi.c while DOing */
2191 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2193 CURLcode result = imap_multi_statemach(conn, dophase_done);
2196 DEBUGF(infof(conn->data, "DO phase failed\n"));
2199 result = imap_dophase_done(conn, FALSE /* not connected */);
2201 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2208 /***********************************************************************
2210 * imap_regular_transfer()
2212 * The input argument is already checked for validity.
2214 * Performs all commands done before a regular transfer between a local and a
2217 static CURLcode imap_regular_transfer(struct connectdata *conn,
2220 CURLcode result = CURLE_OK;
2221 bool connected = FALSE;
2222 struct SessionHandle *data = conn->data;
2224 /* Make sure size is unknown at this point */
2225 data->req.size = -1;
2227 Curl_pgrsSetUploadCounter(data, 0);
2228 Curl_pgrsSetDownloadCounter(data, 0);
2229 Curl_pgrsSetUploadSize(data, 0);
2230 Curl_pgrsSetDownloadSize(data, 0);
2232 result = imap_perform(conn, &connected, dophase_done);
2236 /* The DO phase has not completed yet */
2239 result = imap_dophase_done(conn, connected);
2245 static CURLcode imap_setup_connection(struct connectdata *conn)
2247 struct SessionHandle *data = conn->data;
2249 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2250 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2251 switch and use HTTP operations only */
2252 #ifndef CURL_DISABLE_HTTP
2253 if(conn->handler == &Curl_handler_imap)
2254 conn->handler = &Curl_handler_imap_proxy;
2257 conn->handler = &Curl_handler_imaps_proxy;
2259 failf(data, "IMAPS not supported!");
2260 return CURLE_UNSUPPORTED_PROTOCOL;
2264 /* We explicitly mark this connection as persistent here as we're doing
2265 IMAP over HTTP and thus we accidentally avoid setting this value
2267 conn->bits.close = FALSE;
2269 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2270 return CURLE_UNSUPPORTED_PROTOCOL;
2274 data->state.path++; /* don't include the initial slash */
2279 #endif /* CURL_DISABLE_IMAP */