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"
82 #define _MPRINTF_REPLACE /* use our functions only */
83 #include <curl/mprintf.h>
85 #include "curl_memory.h"
86 /* The last #include file should be: */
89 /* Local API functions */
90 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
91 static CURLcode imap_do(struct connectdata *conn, bool *done);
92 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
94 static CURLcode imap_connect(struct connectdata *conn, bool *done);
95 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
96 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
97 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
99 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode imap_setup_connection(struct connectdata *conn);
101 static char *imap_atom(const char *str);
102 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
103 static CURLcode imap_parse_url_options(struct connectdata *conn);
104 static CURLcode imap_parse_url_path(struct connectdata *conn);
105 static CURLcode imap_parse_custom_request(struct connectdata *conn);
108 * IMAP protocol handler.
111 const struct Curl_handler Curl_handler_imap = {
113 imap_setup_connection, /* setup_connection */
115 imap_done, /* done */
116 ZERO_NULL, /* do_more */
117 imap_connect, /* connect_it */
118 imap_multi_statemach, /* connecting */
119 imap_doing, /* doing */
120 imap_getsock, /* proto_getsock */
121 imap_getsock, /* doing_getsock */
122 ZERO_NULL, /* domore_getsock */
123 ZERO_NULL, /* perform_getsock */
124 imap_disconnect, /* disconnect */
125 ZERO_NULL, /* readwrite */
126 PORT_IMAP, /* defport */
127 CURLPROTO_IMAP, /* protocol */
128 PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
129 | PROTOPT_NOURLQUERY /* flags */
134 * IMAPS protocol handler.
137 const struct Curl_handler Curl_handler_imaps = {
138 "IMAPS", /* scheme */
139 imap_setup_connection, /* setup_connection */
141 imap_done, /* done */
142 ZERO_NULL, /* do_more */
143 imap_connect, /* connect_it */
144 imap_multi_statemach, /* connecting */
145 imap_doing, /* doing */
146 imap_getsock, /* proto_getsock */
147 imap_getsock, /* doing_getsock */
148 ZERO_NULL, /* domore_getsock */
149 ZERO_NULL, /* perform_getsock */
150 imap_disconnect, /* disconnect */
151 ZERO_NULL, /* readwrite */
152 PORT_IMAPS, /* defport */
153 CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
154 PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
155 | PROTOPT_NOURLQUERY /* flags */
159 #ifndef CURL_DISABLE_HTTP
161 * HTTP-proxyed IMAP protocol handler.
164 static const struct Curl_handler Curl_handler_imap_proxy = {
166 ZERO_NULL, /* setup_connection */
167 Curl_http, /* do_it */
168 Curl_http_done, /* done */
169 ZERO_NULL, /* do_more */
170 ZERO_NULL, /* connect_it */
171 ZERO_NULL, /* connecting */
172 ZERO_NULL, /* doing */
173 ZERO_NULL, /* proto_getsock */
174 ZERO_NULL, /* doing_getsock */
175 ZERO_NULL, /* domore_getsock */
176 ZERO_NULL, /* perform_getsock */
177 ZERO_NULL, /* disconnect */
178 ZERO_NULL, /* readwrite */
179 PORT_IMAP, /* defport */
180 CURLPROTO_HTTP, /* protocol */
181 PROTOPT_NONE /* flags */
186 * HTTP-proxyed IMAPS protocol handler.
189 static const struct Curl_handler Curl_handler_imaps_proxy = {
190 "IMAPS", /* scheme */
191 ZERO_NULL, /* setup_connection */
192 Curl_http, /* do_it */
193 Curl_http_done, /* done */
194 ZERO_NULL, /* do_more */
195 ZERO_NULL, /* connect_it */
196 ZERO_NULL, /* connecting */
197 ZERO_NULL, /* doing */
198 ZERO_NULL, /* proto_getsock */
199 ZERO_NULL, /* doing_getsock */
200 ZERO_NULL, /* domore_getsock */
201 ZERO_NULL, /* perform_getsock */
202 ZERO_NULL, /* disconnect */
203 ZERO_NULL, /* readwrite */
204 PORT_IMAPS, /* defport */
205 CURLPROTO_HTTP, /* protocol */
206 PROTOPT_NONE /* flags */
212 static void imap_to_imaps(struct connectdata *conn)
214 conn->handler = &Curl_handler_imaps;
217 #define imap_to_imaps(x) Curl_nop_stmt
220 /***********************************************************************
224 * Determines whether the untagged response is related to the specified
225 * command by checking if it is in format "* <command-name> ..." or
226 * "* <number> <command-name> ...".
228 * The "* " marker is assumed to have already been checked by the caller.
230 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
232 const char *end = line + len;
233 size_t cmd_len = strlen(cmd);
235 /* Skip the untagged response marker */
238 /* Do we have a number after the marker? */
239 if(line < end && ISDIGIT(*line)) {
240 /* Skip the number */
243 while(line < end && ISDIGIT(*line));
245 /* Do we have the space character? */
246 if(line == end || *line != ' ')
252 /* Does the command name match and is it followed by a space character or at
254 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
255 (line[cmd_len] == ' ' || line + cmd_len == end))
261 /***********************************************************************
265 * Checks whether the given string is a valid tagged, untagged or continuation
266 * response which can be processed by the response handler.
268 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
271 struct IMAP *imap = conn->data->state.proto.imap;
272 struct imap_conn *imapc = &conn->proto.imapc;
273 const char *id = imapc->resptag;
274 size_t id_len = strlen(id);
276 /* Do we have a tagged command response? */
277 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
281 if(len >= 2 && !memcmp(line, "OK", 2))
283 else if(len >= 2 && !memcmp(line, "NO", 2))
285 else if(len >= 3 && !memcmp(line, "BAD", 3))
288 failf(conn->data, "Bad tagged response");
295 /* Do we have an untagged command response? */
296 if(len >= 2 && !memcmp("* ", line, 2)) {
297 switch(imapc->state) {
298 /* States which are interested in untagged responses */
299 case IMAP_CAPABILITY:
300 if(!imap_matchresp(line, len, "CAPABILITY"))
305 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
306 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
307 (strcmp(imap->custom, "STORE") ||
308 !imap_matchresp(line, len, "FETCH")) &&
309 strcmp(imap->custom, "SELECT") &&
310 strcmp(imap->custom, "EXAMINE")))
315 /* SELECT is special in that its untagged responses do not have a
316 common prefix so accept anything! */
320 if(!imap_matchresp(line, len, "FETCH"))
324 /* Ignore other untagged responses */
333 /* Do we have a continuation response? */
334 if((len == 3 && !memcmp("+", line, 1)) ||
335 (len >= 2 && !memcmp("+ ", line, 2))) {
336 switch(imapc->state) {
337 /* States which are interested in continuation responses */
338 case IMAP_AUTHENTICATE_PLAIN:
339 case IMAP_AUTHENTICATE_LOGIN:
340 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
341 case IMAP_AUTHENTICATE_CRAMMD5:
342 case IMAP_AUTHENTICATE_DIGESTMD5:
343 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
344 case IMAP_AUTHENTICATE_NTLM:
345 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
346 case IMAP_AUTHENTICATE_FINAL:
352 failf(conn->data, "Unexpected continuation response");
360 return FALSE; /* Nothing for us */
363 /***********************************************************************
367 * This is the ONLY way to change IMAP state!
369 static void state(struct connectdata *conn, imapstate newstate)
371 struct imap_conn *imapc = &conn->proto.imapc;
372 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
373 /* for debug purposes */
374 static const char * const names[]={
380 "AUTHENTICATE_PLAIN",
381 "AUTHENTICATE_LOGIN",
382 "AUTHENTICATE_LOGIN_PASSWD",
383 "AUTHENTICATE_CRAMMD5",
384 "AUTHENTICATE_DIGESTMD5",
385 "AUTHENTICATE_DIGESTMD5_RESP",
387 "AUTHENTICATE_NTLM_TYPE2MSG",
388 "AUTHENTICATE_FINAL",
400 if(imapc->state != newstate)
401 infof(conn->data, "IMAP %p state change from %s to %s\n",
402 (void *)imapc, names[imapc->state], names[newstate]);
405 imapc->state = newstate;
408 /***********************************************************************
410 * imap_perform_capability()
412 * Sends the CAPABILITY command in order to obtain a list of server side
413 * supported capabilities.
415 static CURLcode imap_perform_capability(struct connectdata *conn)
417 CURLcode result = CURLE_OK;
418 struct imap_conn *imapc = &conn->proto.imapc;
420 imapc->authmechs = 0; /* No known authentication mechanisms yet */
421 imapc->authused = 0; /* Clear the authentication mechanism used */
422 imapc->tls_supported = FALSE; /* Clear the TLS capability */
424 /* Send the CAPABILITY command */
425 result = imap_sendf(conn, "CAPABILITY");
428 state(conn, IMAP_CAPABILITY);
433 /***********************************************************************
435 * imap_perform_starttls()
437 * Sends the STARTTLS command to start the upgrade to TLS.
439 static CURLcode imap_perform_starttls(struct connectdata *conn)
441 CURLcode result = CURLE_OK;
443 /* Send the STARTTLS command */
444 result = imap_sendf(conn, "STARTTLS");
447 state(conn, IMAP_STARTTLS);
452 /***********************************************************************
454 * imap_perform_upgrade_tls()
456 * Performs the upgrade to TLS.
458 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
460 CURLcode result = CURLE_OK;
461 struct imap_conn *imapc = &conn->proto.imapc;
463 /* Start the SSL connection */
464 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
467 if(imapc->state != IMAP_UPGRADETLS)
468 state(conn, IMAP_UPGRADETLS);
472 result = imap_perform_capability(conn);
479 /***********************************************************************
481 * imap_perform_login()
483 * Sends a clear text LOGIN command to authenticate with.
485 static CURLcode imap_perform_login(struct connectdata *conn)
487 CURLcode result = CURLE_OK;
491 /* Check we have a username and password to authenticate with and end the
492 connect phase if we don't */
493 if(!conn->bits.user_passwd) {
494 state(conn, IMAP_STOP);
499 /* Make sure the username and password are in the correct atom format */
500 user = imap_atom(conn->user);
501 passwd = imap_atom(conn->passwd);
503 /* Send the LOGIN command */
504 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
505 passwd ? passwd : "");
508 Curl_safefree(passwd);
511 state(conn, IMAP_LOGIN);
516 /***********************************************************************
518 * imap_perform_authenticate()
520 * Sends an AUTHENTICATE command allowing the client to login with the
521 * appropriate SASL authentication mechanism.
523 * Additionally, the function will perform fallback to the LOGIN command
524 * should a common mechanism not be available between the client and server.
526 static CURLcode imap_perform_authenticate(struct connectdata *conn)
528 CURLcode result = CURLE_OK;
529 struct SessionHandle *data = conn->data;
530 struct imap_conn *imapc = &conn->proto.imapc;
531 const char *mech = NULL;
532 char *initresp = NULL;
534 imapstate state1 = IMAP_STOP;
535 imapstate state2 = IMAP_STOP;
537 /* Check we have a username and password to authenticate with and end the
538 connect phase if we don't */
539 if(!conn->bits.user_passwd) {
540 state(conn, IMAP_STOP);
545 /* Calculate the supported authentication mechanism by decreasing order of
547 #ifndef CURL_DISABLE_CRYPTO_AUTH
548 if((imapc->authmechs & SASL_MECH_DIGEST_MD5) &&
549 (imapc->prefmech & SASL_MECH_DIGEST_MD5)) {
551 state1 = IMAP_AUTHENTICATE_DIGESTMD5;
552 imapc->authused = SASL_MECH_DIGEST_MD5;
554 else if((imapc->authmechs & SASL_MECH_CRAM_MD5) &&
555 (imapc->prefmech & SASL_MECH_CRAM_MD5)) {
557 state1 = IMAP_AUTHENTICATE_CRAMMD5;
558 imapc->authused = SASL_MECH_CRAM_MD5;
563 if((imapc->authmechs & SASL_MECH_NTLM) &&
564 (imapc->prefmech & SASL_MECH_NTLM)) {
566 state1 = IMAP_AUTHENTICATE_NTLM;
567 state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
568 imapc->authused = SASL_MECH_NTLM;
570 if(imapc->ir_supported || data->set.sasl_ir)
571 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
577 if((imapc->authmechs & SASL_MECH_LOGIN) &&
578 (imapc->prefmech & SASL_MECH_LOGIN)) {
580 state1 = IMAP_AUTHENTICATE_LOGIN;
581 state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
582 imapc->authused = SASL_MECH_LOGIN;
584 if(imapc->ir_supported || data->set.sasl_ir)
585 result = Curl_sasl_create_login_message(conn->data, conn->user,
588 else if((imapc->authmechs & SASL_MECH_PLAIN) &&
589 (imapc->prefmech & SASL_MECH_PLAIN)) {
591 state1 = IMAP_AUTHENTICATE_PLAIN;
592 state2 = IMAP_AUTHENTICATE_FINAL;
593 imapc->authused = SASL_MECH_PLAIN;
595 if(imapc->ir_supported || data->set.sasl_ir)
596 result = Curl_sasl_create_plain_message(conn->data, conn->user,
597 conn->passwd, &initresp, &len);
602 /* Perform SASL based authentication */
604 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
610 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
616 Curl_safefree(initresp);
618 else if(!imapc->login_disabled)
619 /* Perform clear text authentication */
620 result = imap_perform_login(conn);
622 /* Other mechanisms not supported */
623 infof(conn->data, "No known authentication mechanisms supported!\n");
624 result = CURLE_LOGIN_DENIED;
631 /***********************************************************************
633 * imap_perform_list()
635 * Sends a LIST command or an alternative custom request.
637 static CURLcode imap_perform_list(struct connectdata *conn)
639 CURLcode result = CURLE_OK;
640 struct SessionHandle *data = conn->data;
641 struct IMAP *imap = data->state.proto.imap;
645 /* Send the custom request */
646 result = imap_sendf(conn, "%s%s", imap->custom,
647 imap->custom_params ? imap->custom_params : "");
649 /* Make sure the mailbox is in the correct atom format */
650 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
652 return CURLE_OUT_OF_MEMORY;
654 /* Send the LIST command */
655 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
657 Curl_safefree(mailbox);
661 state(conn, IMAP_LIST);
666 /***********************************************************************
668 * imap_perform_select()
670 * Sends a SELECT command to ask the server to change the selected mailbox.
672 static CURLcode imap_perform_select(struct connectdata *conn)
674 CURLcode result = CURLE_OK;
675 struct SessionHandle *data = conn->data;
676 struct IMAP *imap = data->state.proto.imap;
677 struct imap_conn *imapc = &conn->proto.imapc;
680 /* Invalidate old information as we are switching mailboxes */
681 Curl_safefree(imapc->mailbox);
682 Curl_safefree(imapc->mailbox_uidvalidity);
684 /* Check we have a mailbox */
686 failf(conn->data, "Cannot SELECT without a mailbox.");
687 return CURLE_URL_MALFORMAT;
690 /* Make sure the mailbox is in the correct atom format */
691 mailbox = imap_atom(imap->mailbox);
693 return CURLE_OUT_OF_MEMORY;
695 /* Send the SELECT command */
696 result = imap_sendf(conn, "SELECT %s", mailbox);
698 Curl_safefree(mailbox);
701 state(conn, IMAP_SELECT);
706 /***********************************************************************
708 * imap_perform_fetch()
710 * Sends a FETCH command to initiate the download of a message.
712 static CURLcode imap_perform_fetch(struct connectdata *conn)
714 CURLcode result = CURLE_OK;
715 struct IMAP *imap = conn->data->state.proto.imap;
717 /* Check we have a UID */
719 failf(conn->data, "Cannot FETCH without a UID.");
720 return CURLE_URL_MALFORMAT;
723 /* Send the FETCH command */
724 result = imap_sendf(conn, "FETCH %s BODY[%s]",
726 imap->section ? imap->section : "");
729 state(conn, IMAP_FETCH);
734 /***********************************************************************
736 * imap_perform_append()
738 * Sends an APPEND command to initiate the upload of a message.
740 static CURLcode imap_perform_append(struct connectdata *conn)
742 CURLcode result = CURLE_OK;
743 struct IMAP *imap = conn->data->state.proto.imap;
746 /* Check we have a mailbox */
748 failf(conn->data, "Cannot APPEND without a mailbox.");
749 return CURLE_URL_MALFORMAT;
752 /* Check we know the size of the upload */
753 if(conn->data->set.infilesize < 0) {
754 failf(conn->data, "Cannot APPEND with unknown input file size\n");
755 return CURLE_UPLOAD_FAILED;
758 /* Make sure the mailbox is in the correct atom format */
759 mailbox = imap_atom(imap->mailbox);
761 return CURLE_OUT_OF_MEMORY;
763 /* Send the APPEND command */
764 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
765 mailbox, conn->data->set.infilesize);
767 Curl_safefree(mailbox);
770 state(conn, IMAP_APPEND);
775 /***********************************************************************
777 * imap_perform_logout()
779 * Performs the logout action prior to sclose() being called.
781 static CURLcode imap_perform_logout(struct connectdata *conn)
783 CURLcode result = CURLE_OK;
785 /* Send the LOGOUT command */
786 result = imap_sendf(conn, "LOGOUT");
789 state(conn, IMAP_LOGOUT);
794 /* For the initial server greeting */
795 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
799 CURLcode result = CURLE_OK;
800 struct SessionHandle *data = conn->data;
802 (void)instate; /* no use for this yet */
804 if(imapcode != 'O') {
805 failf(data, "Got unexpected imap-server response");
806 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
809 result = imap_perform_capability(conn);
814 /* For CAPABILITY responses */
815 static CURLcode imap_state_capability_resp(struct connectdata *conn,
819 CURLcode result = CURLE_OK;
820 struct SessionHandle *data = conn->data;
821 struct imap_conn *imapc = &conn->proto.imapc;
822 const char *line = data->state.buffer;
825 (void)instate; /* no use for this yet */
827 /* Do we have a untagged response? */
828 if(imapcode == '*') {
831 /* Loop through the data line */
834 (*line == ' ' || *line == '\t' ||
835 *line == '\r' || *line == '\n')) {
843 /* Extract the word */
844 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
845 line[wordlen] != '\t' && line[wordlen] != '\r' &&
846 line[wordlen] != '\n';)
849 /* Does the server support the STARTTLS capability? */
850 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
851 imapc->tls_supported = TRUE;
853 /* Has the server explicitly disabled clear text authentication? */
854 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
855 imapc->login_disabled = TRUE;
857 /* Does the server support the SASL-IR capability? */
858 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
859 imapc->ir_supported = TRUE;
861 /* Do we have a SASL based authentication mechanism? */
862 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
866 /* Test the word for a matching authentication mechanism */
867 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
868 imapc->authmechs |= SASL_MECH_LOGIN;
869 if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
870 imapc->authmechs |= SASL_MECH_PLAIN;
871 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
872 imapc->authmechs |= SASL_MECH_CRAM_MD5;
873 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
874 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
875 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
876 imapc->authmechs |= SASL_MECH_GSSAPI;
877 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
878 imapc->authmechs |= SASL_MECH_EXTERNAL;
879 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
880 imapc->authmechs |= SASL_MECH_NTLM;
886 else if(imapcode == 'O') {
887 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
888 /* We don't have a SSL/TLS connection yet, but SSL is requested */
889 if(imapc->tls_supported)
890 /* Switch to TLS connection now */
891 result = imap_perform_starttls(conn);
892 else if(data->set.use_ssl == CURLUSESSL_TRY)
893 /* Fallback and carry on with authentication */
894 result = imap_perform_authenticate(conn);
896 failf(data, "STARTTLS not supported.");
897 result = CURLE_USE_SSL_FAILED;
901 result = imap_perform_authenticate(conn);
904 result = imap_perform_login(conn);
909 /* For STARTTLS responses */
910 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
914 CURLcode result = CURLE_OK;
915 struct SessionHandle *data = conn->data;
917 (void)instate; /* no use for this yet */
919 if(imapcode != 'O') {
920 if(data->set.use_ssl != CURLUSESSL_TRY) {
921 failf(data, "STARTTLS denied. %c", imapcode);
922 result = CURLE_USE_SSL_FAILED;
925 result = imap_perform_authenticate(conn);
928 result = imap_perform_upgrade_tls(conn);
933 /* For AUTHENTICATE PLAIN (without initial response) responses */
934 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
938 CURLcode result = CURLE_OK;
939 struct SessionHandle *data = conn->data;
941 char *plainauth = NULL;
943 (void)instate; /* no use for this yet */
945 if(imapcode != '+') {
946 failf(data, "Access denied. %c", imapcode);
947 result = CURLE_LOGIN_DENIED;
950 /* Create the authorisation message */
951 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
954 /* Send the message */
957 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
960 state(conn, IMAP_AUTHENTICATE_FINAL);
963 Curl_safefree(plainauth);
970 /* For AUTHENTICATE LOGIN (without initial response) responses */
971 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
975 CURLcode result = CURLE_OK;
976 struct SessionHandle *data = conn->data;
978 char *authuser = NULL;
980 (void)instate; /* no use for this yet */
982 if(imapcode != '+') {
983 failf(data, "Access denied: %d", imapcode);
984 result = CURLE_LOGIN_DENIED;
987 /* Create the user message */
988 result = Curl_sasl_create_login_message(data, conn->user,
994 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
997 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1000 Curl_safefree(authuser);
1007 /* For AUTHENTICATE LOGIN user entry responses */
1008 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1012 CURLcode result = CURLE_OK;
1013 struct SessionHandle *data = conn->data;
1015 char *authpasswd = NULL;
1017 (void)instate; /* no use for this yet */
1019 if(imapcode != '+') {
1020 failf(data, "Access denied: %d", imapcode);
1021 result = CURLE_LOGIN_DENIED;
1024 /* Create the password message */
1025 result = Curl_sasl_create_login_message(data, conn->passwd,
1028 /* Send the password */
1031 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1034 state(conn, IMAP_AUTHENTICATE_FINAL);
1037 Curl_safefree(authpasswd);
1044 #ifndef CURL_DISABLE_CRYPTO_AUTH
1045 /* For AUTHENTICATE CRAM-MD5 responses */
1046 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1050 CURLcode result = CURLE_OK;
1051 struct SessionHandle *data = conn->data;
1052 char *chlg64 = data->state.buffer;
1054 char *rplyb64 = NULL;
1056 (void)instate; /* no use for this yet */
1058 if(imapcode != '+') {
1059 failf(data, "Access denied: %d", imapcode);
1060 return CURLE_LOGIN_DENIED;
1063 /* Get the challenge */
1064 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1067 /* Terminate the challenge */
1068 if(*chlg64 != '=') {
1069 for(len = strlen(chlg64); len--;)
1070 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
1071 chlg64[len] != '\t')
1079 /* Create the response message */
1080 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
1081 conn->passwd, &rplyb64, &len);
1083 /* Send the response */
1086 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1089 state(conn, IMAP_AUTHENTICATE_FINAL);
1092 Curl_safefree(rplyb64);
1098 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1099 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1103 CURLcode result = CURLE_OK;
1104 struct SessionHandle *data = conn->data;
1105 char *chlg64 = data->state.buffer;
1107 char *rplyb64 = NULL;
1109 (void)instate; /* no use for this yet */
1111 if(imapcode != '+') {
1112 failf(data, "Access denied: %d", imapcode);
1113 return CURLE_LOGIN_DENIED;
1116 /* Get the challenge */
1117 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1120 /* Create the response message */
1121 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
1122 conn->passwd, "imap",
1125 /* Send the response */
1128 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1131 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1134 Curl_safefree(rplyb64);
1140 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1141 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1145 CURLcode result = CURLE_OK;
1146 struct SessionHandle *data = conn->data;
1148 (void)instate; /* no use for this yet */
1150 if(imapcode != '+') {
1151 failf(data, "Authentication failed: %d", imapcode);
1152 result = CURLE_LOGIN_DENIED;
1155 /* Send an empty response */
1156 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1159 state(conn, IMAP_AUTHENTICATE_FINAL);
1167 /* For AUTHENTICATE NTLM (without initial response) responses */
1168 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1172 CURLcode result = CURLE_OK;
1173 struct SessionHandle *data = conn->data;
1175 char *type1msg = NULL;
1177 (void)instate; /* no use for this yet */
1179 if(imapcode != '+') {
1180 failf(data, "Access denied: %d", imapcode);
1181 result = CURLE_LOGIN_DENIED;
1184 /* Create the type-1 message */
1185 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1189 /* Send the message */
1192 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1195 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1198 Curl_safefree(type1msg);
1205 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1206 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1210 CURLcode result = CURLE_OK;
1211 struct SessionHandle *data = conn->data;
1213 char *type3msg = NULL;
1215 (void)instate; /* no use for this yet */
1217 if(imapcode != '+') {
1218 failf(data, "Access denied: %d", imapcode);
1219 result = CURLE_LOGIN_DENIED;
1222 /* Create the type-3 message */
1223 result = Curl_sasl_create_ntlm_type3_message(data,
1224 data->state.buffer + 2,
1225 conn->user, conn->passwd,
1229 /* Send the message */
1232 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1235 state(conn, IMAP_AUTHENTICATE_FINAL);
1238 Curl_safefree(type3msg);
1246 /* For final responses to the AUTHENTICATE sequence */
1247 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1251 CURLcode result = CURLE_OK;
1252 struct SessionHandle *data = conn->data;
1254 (void)instate; /* no use for this yet */
1256 if(imapcode != 'O') {
1257 failf(data, "Authentication failed: %d", imapcode);
1258 result = CURLE_LOGIN_DENIED;
1261 /* End of connect phase */
1262 state(conn, IMAP_STOP);
1267 /* For LOGIN responses */
1268 static CURLcode imap_state_login_resp(struct connectdata *conn,
1272 CURLcode result = CURLE_OK;
1273 struct SessionHandle *data = conn->data;
1275 (void)instate; /* no use for this yet */
1277 if(imapcode != 'O') {
1278 failf(data, "Access denied. %c", imapcode);
1279 result = CURLE_LOGIN_DENIED;
1282 /* End of connect phase */
1283 state(conn, IMAP_STOP);
1288 /* For LIST responses */
1289 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1292 CURLcode result = CURLE_OK;
1293 char *line = conn->data->state.buffer;
1294 size_t len = strlen(line);
1296 (void)instate; /* No use for this yet */
1298 if(imapcode == '*') {
1299 /* Temporarily add the LF character back and send as body to the client */
1301 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1304 else if(imapcode != 'O')
1305 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1307 /* End of DO phase */
1308 state(conn, IMAP_STOP);
1313 /* For SELECT responses */
1314 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1317 CURLcode result = CURLE_OK;
1318 struct SessionHandle *data = conn->data;
1319 struct IMAP *imap = conn->data->state.proto.imap;
1320 struct imap_conn *imapc = &conn->proto.imapc;
1321 const char *line = data->state.buffer;
1324 (void)instate; /* no use for this yet */
1326 if(imapcode == '*') {
1327 /* See if this is an UIDVALIDITY response */
1328 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1329 Curl_safefree(imapc->mailbox_uidvalidity);
1330 imapc->mailbox_uidvalidity = strdup(tmp);
1333 else if(imapcode == 'O') {
1334 /* Check if the UIDVALIDITY has been specified and matches */
1335 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1336 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1337 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1338 result = CURLE_REMOTE_FILE_NOT_FOUND;
1341 /* Note the currently opened mailbox on this connection */
1342 imapc->mailbox = strdup(imap->mailbox);
1345 result = imap_perform_list(conn);
1347 result = imap_perform_fetch(conn);
1351 failf(data, "Select failed");
1352 result = CURLE_LOGIN_DENIED;
1358 /* For the (first line of the) FETCH responses */
1359 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1362 CURLcode result = CURLE_OK;
1363 struct SessionHandle *data = conn->data;
1364 struct imap_conn *imapc = &conn->proto.imapc;
1365 struct pingpong *pp = &imapc->pp;
1366 const char *ptr = data->state.buffer;
1367 bool parsed = FALSE;
1370 (void)instate; /* no use for this yet */
1372 if(imapcode != '*') {
1373 Curl_pgrsSetDownloadSize(data, 0);
1374 state(conn, IMAP_STOP);
1375 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1378 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1379 the continuation data contained within the curly brackets */
1380 while(*ptr && (*ptr != '{'))
1385 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1386 if(endptr - ptr > 1 && endptr[0] == '}' &&
1387 endptr[1] == '\r' && endptr[2] == '\0')
1392 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1393 Curl_pgrsSetDownloadSize(data, size);
1396 /* At this point there is a bunch of data in the header "cache" that is
1397 actually body content, send it as body and then skip it. Do note
1398 that there may even be additional "headers" after the body. */
1399 size_t chunk = pp->cache_size;
1401 if(chunk > (size_t)size)
1402 /* The conversion from curl_off_t to size_t is always fine here */
1403 chunk = (size_t)size;
1405 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1409 data->req.bytecount += chunk;
1412 infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1413 " bytes are left for transfer\n", (curl_off_t)chunk, size);
1415 /* Have we used the entire cache or just part of it?*/
1416 if(pp->cache_size > chunk) {
1417 /* Only part of it so shrink the cache to fit the trailing data */
1418 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1419 pp->cache_size -= chunk;
1422 /* Free the cache */
1423 Curl_safefree(pp->cache);
1425 /* Reset the cache size */
1431 /* The entire data is already transferred! */
1432 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1435 data->req.maxdownload = size;
1436 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1440 /* We don't know how to parse this line */
1441 failf(pp->conn->data, "Failed to parse FETCH response.");
1442 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1445 /* End of DO phase */
1446 state(conn, IMAP_STOP);
1451 /* For final FETCH responses performed after the download */
1452 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1456 CURLcode result = CURLE_OK;
1458 (void)instate; /* No use for this yet */
1461 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1463 /* End of DONE phase */
1464 state(conn, IMAP_STOP);
1469 /* For APPEND responses */
1470 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1473 CURLcode result = CURLE_OK;
1474 struct SessionHandle *data = conn->data;
1476 (void)instate; /* No use for this yet */
1478 if(imapcode != '+') {
1479 result = CURLE_UPLOAD_FAILED;
1482 /* Set the progress upload size */
1483 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1486 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1488 /* End of DO phase */
1489 state(conn, IMAP_STOP);
1495 /* For final APPEND responses performed after the upload */
1496 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1500 CURLcode result = CURLE_OK;
1502 (void)instate; /* No use for this yet */
1505 result = CURLE_UPLOAD_FAILED;
1507 /* End of DONE phase */
1508 state(conn, IMAP_STOP);
1513 static CURLcode imap_statemach_act(struct connectdata *conn)
1515 CURLcode result = CURLE_OK;
1516 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1518 struct imap_conn *imapc = &conn->proto.imapc;
1519 struct pingpong *pp = &imapc->pp;
1522 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1523 if(imapc->state == IMAP_UPGRADETLS)
1524 return imap_perform_upgrade_tls(conn);
1526 /* Flush any data that needs to be sent */
1528 return Curl_pp_flushsend(pp);
1531 /* Read the response from the server */
1532 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1536 /* Was there an error parsing the response line? */
1538 return CURLE_FTP_WEIRD_SERVER_REPLY;
1543 /* We have now received a full IMAP server response */
1544 switch(imapc->state) {
1545 case IMAP_SERVERGREET:
1546 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1549 case IMAP_CAPABILITY:
1550 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1554 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1557 case IMAP_AUTHENTICATE_PLAIN:
1558 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1561 case IMAP_AUTHENTICATE_LOGIN:
1562 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1565 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1566 result = imap_state_auth_login_password_resp(conn, imapcode,
1570 #ifndef CURL_DISABLE_CRYPTO_AUTH
1571 case IMAP_AUTHENTICATE_CRAMMD5:
1572 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1575 case IMAP_AUTHENTICATE_DIGESTMD5:
1576 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1579 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1580 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1585 case IMAP_AUTHENTICATE_NTLM:
1586 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1589 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1590 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1595 case IMAP_AUTHENTICATE_FINAL:
1596 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1600 result = imap_state_login_resp(conn, imapcode, imapc->state);
1604 result = imap_state_list_resp(conn, imapcode, imapc->state);
1608 result = imap_state_select_resp(conn, imapcode, imapc->state);
1612 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1615 case IMAP_FETCH_FINAL:
1616 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1620 result = imap_state_append_resp(conn, imapcode, imapc->state);
1623 case IMAP_APPEND_FINAL:
1624 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1628 /* fallthrough, just stop! */
1630 /* internal error */
1631 state(conn, IMAP_STOP);
1634 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1639 /* Called repeatedly until done from multi.c */
1640 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1642 CURLcode result = CURLE_OK;
1643 struct imap_conn *imapc = &conn->proto.imapc;
1645 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1646 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1648 result = Curl_pp_statemach(&imapc->pp, FALSE);
1650 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1655 static CURLcode imap_block_statemach(struct connectdata *conn)
1657 CURLcode result = CURLE_OK;
1658 struct imap_conn *imapc = &conn->proto.imapc;
1660 while(imapc->state != IMAP_STOP && !result)
1661 result = Curl_pp_statemach(&imapc->pp, TRUE);
1666 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1668 static CURLcode imap_init(struct connectdata *conn)
1670 CURLcode result = CURLE_OK;
1671 struct SessionHandle *data = conn->data;
1672 struct IMAP *imap = data->state.proto.imap;
1675 imap = data->state.proto.imap = calloc(sizeof(struct IMAP), 1);
1677 result = CURLE_OUT_OF_MEMORY;
1683 /* For the IMAP "protocol connect" and "doing" phases only */
1684 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1687 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1690 /***********************************************************************
1694 * This function should do everything that is to be considered a part of the
1697 * The variable 'done' points to will be TRUE if the protocol-layer connect
1698 * phase is done when this function returns, or FALSE if not.
1700 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1702 CURLcode result = CURLE_OK;
1703 struct imap_conn *imapc = &conn->proto.imapc;
1704 struct pingpong *pp = &imapc->pp;
1706 *done = FALSE; /* default to not done yet */
1708 /* If there already is a protocol-specific struct allocated for this
1709 sessionhandle, deal with it */
1710 Curl_reset_reqproto(conn);
1712 /* Initialise the IMAP layer */
1713 result = imap_init(conn);
1717 /* We always support persistent connections in IMAP */
1718 conn->bits.close = FALSE;
1720 /* Set the default response time-out */
1721 pp->response_time = RESP_TIMEOUT;
1722 pp->statemach_act = imap_statemach_act;
1723 pp->endofresp = imap_endofresp;
1726 /* Set the default preferred authentication mechanism */
1727 imapc->prefmech = SASL_AUTH_ANY;
1729 /* Initialise the pingpong layer */
1732 /* Parse the URL options */
1733 result = imap_parse_url_options(conn);
1737 /* Start off waiting for the server greeting response */
1738 state(conn, IMAP_SERVERGREET);
1740 /* Start off with an response id of '*' */
1741 strcpy(imapc->resptag, "*");
1743 result = imap_multi_statemach(conn, done);
1748 /***********************************************************************
1752 * The DONE function. This does what needs to be done after a single DO has
1755 * Input argument is already checked for validity.
1757 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1760 CURLcode result = CURLE_OK;
1761 struct SessionHandle *data = conn->data;
1762 struct IMAP *imap = data->state.proto.imap;
1767 /* When the easy handle is removed from the multi interface while libcurl
1768 is still trying to resolve the host name, the IMAP struct is not yet
1769 initialized. However, the removal action calls Curl_done() which in
1770 turn calls this function, so we simply return success. */
1774 conn->bits.close = TRUE; /* marked for closure */
1775 result = status; /* use the already set error code */
1777 else if(!data->set.connect_only && !imap->custom &&
1778 (imap->uid || data->set.upload)) {
1779 /* Handle responses after FETCH or APPEND transfer has finished */
1780 if(!data->set.upload)
1781 state(conn, IMAP_FETCH_FINAL);
1783 /* End the APPEND command first by sending an empty line */
1784 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1786 state(conn, IMAP_APPEND_FINAL);
1789 /* Run the state-machine
1791 TODO: when the multi interface is used, this _really_ should be using
1792 the imap_multi_statemach function but we have no general support for
1793 non-blocking DONE operations, not in the multi state machine and with
1794 Curl_done() invokes on several places in the code!
1797 result = imap_block_statemach(conn);
1800 /* Cleanup our per-request based variables */
1801 Curl_safefree(imap->mailbox);
1802 Curl_safefree(imap->uidvalidity);
1803 Curl_safefree(imap->uid);
1804 Curl_safefree(imap->section);
1805 Curl_safefree(imap->custom);
1806 Curl_safefree(imap->custom_params);
1808 /* Clear the transfer mode for the next request */
1809 imap->transfer = FTPTRANSFER_BODY;
1814 /***********************************************************************
1818 * This is the actual DO function for IMAP. Fetch or append a message, or do
1819 * other things according to the options previously setup.
1821 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1824 /* This is IMAP and no proxy */
1825 CURLcode result = CURLE_OK;
1826 struct SessionHandle *data = conn->data;
1827 struct IMAP *imap = data->state.proto.imap;
1828 struct imap_conn *imapc = &conn->proto.imapc;
1829 bool selected = FALSE;
1831 DEBUGF(infof(conn->data, "DO phase starts\n"));
1833 if(conn->data->set.opt_no_body) {
1834 /* Requested no body means no transfer */
1835 imap->transfer = FTPTRANSFER_INFO;
1838 *dophase_done = FALSE; /* not done yet */
1840 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1841 has already been selected on this connection */
1842 if(imap->mailbox && imapc->mailbox &&
1843 !strcmp(imap->mailbox, imapc->mailbox) &&
1844 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1845 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1848 /* Start the first command in the DO phase */
1849 if(conn->data->set.upload)
1850 /* APPEND can be executed directly */
1851 result = imap_perform_append(conn);
1852 else if(imap->custom && (selected || !imap->mailbox))
1853 /* Custom command using the same mailbox or no mailbox */
1854 result = imap_perform_list(conn);
1855 else if(!imap->custom && selected && imap->uid)
1856 /* FETCH from the same mailbox */
1857 result = imap_perform_fetch(conn);
1858 else if(imap->mailbox && !selected && (imap->custom || imap->uid))
1859 /* SELECT the mailbox */
1860 result = imap_perform_select(conn);
1863 result = imap_perform_list(conn);
1868 /* Run the state-machine */
1869 result = imap_multi_statemach(conn, dophase_done);
1871 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1874 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1879 /***********************************************************************
1883 * This function is registered as 'curl_do' function. It decodes the path
1884 * parts etc as a wrapper to the actual DO function (imap_perform).
1886 * The input argument is already checked for validity.
1888 static CURLcode imap_do(struct connectdata *conn, bool *done)
1890 CURLcode result = CURLE_OK;
1892 *done = FALSE; /* default to false */
1894 /* Since connections can be re-used between SessionHandles, there might be a
1895 connection already existing but on a fresh SessionHandle struct. As such
1896 we make sure we have a good IMAP struct to play with. For new connections
1897 the IMAP struct is allocated and setup in the imap_connect() function. */
1898 Curl_reset_reqproto(conn);
1899 result = imap_init(conn);
1903 /* Parse the URL path */
1904 result = imap_parse_url_path(conn);
1908 /* Parse the custom request */
1909 result = imap_parse_custom_request(conn);
1913 result = imap_regular_transfer(conn, done);
1918 /***********************************************************************
1922 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1923 * resources. BLOCKING.
1925 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1927 struct imap_conn *imapc = &conn->proto.imapc;
1929 /* We cannot send quit unconditionally. If this connection is stale or
1930 bad in any way, sending quit and waiting around here will make the
1931 disconnect wait in vain and cause more problems than we need to. */
1933 /* The IMAP session may or may not have been allocated/setup at this
1935 if(!dead_connection && imapc->pp.conn)
1936 if(!imap_perform_logout(conn))
1937 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1939 /* Disconnect from the server */
1940 Curl_pp_disconnect(&imapc->pp);
1942 /* Cleanup the SASL module */
1943 Curl_sasl_cleanup(conn, imapc->authused);
1945 /* Cleanup our connection based variables */
1946 Curl_safefree(imapc->mailbox);
1947 Curl_safefree(imapc->mailbox_uidvalidity);
1952 /* Call this when the DO phase has completed */
1953 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1955 struct IMAP *imap = conn->data->state.proto.imap;
1959 if(imap->transfer != FTPTRANSFER_BODY)
1960 /* no data to transfer */
1961 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1966 /* Called from multi.c while DOing */
1967 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1969 CURLcode result = imap_multi_statemach(conn, dophase_done);
1972 DEBUGF(infof(conn->data, "DO phase failed\n"));
1973 else if(*dophase_done) {
1974 result = imap_dophase_done(conn, FALSE /* not connected */);
1976 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1982 /***********************************************************************
1984 * imap_regular_transfer()
1986 * The input argument is already checked for validity.
1988 * Performs all commands done before a regular transfer between a local and a
1991 static CURLcode imap_regular_transfer(struct connectdata *conn,
1994 CURLcode result = CURLE_OK;
1995 bool connected = FALSE;
1996 struct SessionHandle *data = conn->data;
1998 /* Make sure size is unknown at this point */
1999 data->req.size = -1;
2001 /* Set the progress data */
2002 Curl_pgrsSetUploadCounter(data, 0);
2003 Curl_pgrsSetDownloadCounter(data, 0);
2004 Curl_pgrsSetUploadSize(data, 0);
2005 Curl_pgrsSetDownloadSize(data, 0);
2007 /* Carry out the perform */
2008 result = imap_perform(conn, &connected, dophase_done);
2010 /* Perform post DO phase operations if necessary */
2011 if(!result && *dophase_done)
2012 result = imap_dophase_done(conn, connected);
2017 static CURLcode imap_setup_connection(struct connectdata *conn)
2019 struct SessionHandle *data = conn->data;
2021 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2022 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2023 switch and use HTTP operations only */
2024 #ifndef CURL_DISABLE_HTTP
2025 if(conn->handler == &Curl_handler_imap)
2026 conn->handler = &Curl_handler_imap_proxy;
2029 conn->handler = &Curl_handler_imaps_proxy;
2031 failf(data, "IMAPS not supported!");
2032 return CURLE_UNSUPPORTED_PROTOCOL;
2036 /* We explicitly mark this connection as persistent here as we're doing
2037 IMAP over HTTP and thus we accidentally avoid setting this value
2039 conn->bits.close = FALSE;
2041 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2042 return CURLE_UNSUPPORTED_PROTOCOL;
2046 data->state.path++; /* don't include the initial slash */
2051 /***********************************************************************
2055 * Sends the formated string as an IMAP command to the server.
2057 * Designed to never block.
2059 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
2061 CURLcode result = CURLE_OK;
2062 struct imap_conn *imapc = &conn->proto.imapc;
2068 /* Calculate the next command ID wrapping at 3 digits */
2069 imapc->cmdid = (imapc->cmdid + 1) % 1000;
2071 /* Calculate the tag based on the connection ID and command ID */
2072 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
2073 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
2075 /* Prefix the format with the tag */
2076 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
2078 return CURLE_OUT_OF_MEMORY;
2080 /* Send the data with the tag */
2082 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
2085 Curl_safefree(taggedfmt);
2090 /***********************************************************************
2094 * Checks the input string for characters that need escaping and returns an
2095 * atom ready for sending to the server.
2097 * The returned string needs to be freed.
2100 static char *imap_atom(const char *str)
2104 size_t backsp_count = 0;
2105 size_t quote_count = 0;
2106 bool space_exists = FALSE;
2108 char *newstr = NULL;
2113 /* Count any unescapped characters */
2121 space_exists = TRUE;
2126 /* Does the input contain any unescapped characters? */
2127 if(!backsp_count && !quote_count && !space_exists)
2130 /* Calculate the new string length */
2131 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
2133 /* Allocate the new string */
2134 newstr = (char *) malloc((newlen + 1) * sizeof(char));
2138 /* Surround the string in quotes if necessary */
2142 newstr[newlen - 1] = '"';
2146 /* Copy the string, escaping backslash and quote characters along the way */
2149 if(*p1 == '\\' || *p1 == '"') {
2160 /* Terminate the string */
2161 newstr[newlen] = '\0';
2166 /***********************************************************************
2170 * Portable test of whether the specified char is a "bchar" as defined in the
2171 * grammar of RFC-5092.
2173 static bool imap_is_bchar(char ch)
2177 case ':': case '@': case '/':
2178 /* bchar -> achar */
2180 /* bchar -> achar -> uchar -> unreserved */
2181 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2182 case '7': case '8': case '9':
2183 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2184 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2185 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2186 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2187 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2188 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2189 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2190 case 'v': case 'w': case 'x': case 'y': case 'z':
2191 case '-': case '.': case '_': case '~':
2192 /* bchar -> achar -> uchar -> sub-delims-sh */
2193 case '!': case '$': case '\'': case '(': case ')': case '*':
2195 /* bchar -> achar -> uchar -> pct-encoded */
2196 case '%': /* HEXDIG chars are already included above */
2204 /***********************************************************************
2206 * imap_parse_url_options()
2208 * Parse the URL login options.
2210 static CURLcode imap_parse_url_options(struct connectdata *conn)
2212 CURLcode result = CURLE_OK;
2213 struct imap_conn *imapc = &conn->proto.imapc;
2214 const char *options = conn->options;
2215 const char *ptr = options;
2218 const char *key = ptr;
2220 while(*ptr && *ptr != '=')
2223 if(strnequal(key, "AUTH", 4)) {
2224 const char *value = ptr + 1;
2226 if(strequal(value, "*"))
2227 imapc->prefmech = SASL_AUTH_ANY;
2228 else if(strequal(value, "LOGIN"))
2229 imapc->prefmech = SASL_MECH_LOGIN;
2230 else if(strequal(value, "PLAIN"))
2231 imapc->prefmech = SASL_MECH_PLAIN;
2232 else if(strequal(value, "CRAM-MD5"))
2233 imapc->prefmech = SASL_MECH_CRAM_MD5;
2234 else if(strequal(value, "DIGEST-MD5"))
2235 imapc->prefmech = SASL_MECH_DIGEST_MD5;
2236 else if(strequal(value, "GSSAPI"))
2237 imapc->prefmech = SASL_MECH_GSSAPI;
2238 else if(strequal(value, "NTLM"))
2239 imapc->prefmech = SASL_MECH_NTLM;
2241 imapc->prefmech = SASL_AUTH_NONE;
2244 result = CURLE_URL_MALFORMAT;
2250 /***********************************************************************
2252 * imap_parse_url_path()
2254 * Parse the URL path into separate path components.
2257 static CURLcode imap_parse_url_path(struct connectdata *conn)
2259 /* The imap struct is already initialised in imap_connect() */
2260 CURLcode result = CURLE_OK;
2261 struct SessionHandle *data = conn->data;
2262 struct IMAP *imap = data->state.proto.imap;
2263 const char *begin = data->state.path;
2264 const char *ptr = begin;
2266 /* See how much of the URL is a valid path and decode it */
2267 while(imap_is_bchar(*ptr))
2271 /* Remove the trailing slash if present */
2272 const char *end = ptr;
2273 if(end > begin && end[-1] == '/')
2276 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2282 imap->mailbox = NULL;
2284 /* There can be any number of parameters in the form ";NAME=VALUE" */
2285 while(*ptr == ';') {
2290 /* Find the length of the name parameter */
2292 while(*ptr && *ptr != '=')
2296 return CURLE_URL_MALFORMAT;
2298 /* Decode the name parameter */
2299 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2303 /* Find the length of the value parameter */
2305 while(imap_is_bchar(*ptr))
2308 /* Decode the value parameter */
2309 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2311 Curl_safefree(name);
2315 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2317 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2318 stripping of the trailing slash character if it is present.
2320 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2321 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2322 if(valuelen > 0 && value[valuelen - 1] == '/')
2323 value[valuelen - 1] = '\0';
2325 imap->uidvalidity = value;
2328 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2329 if(valuelen > 0 && value[valuelen - 1] == '/')
2330 value[valuelen - 1] = '\0';
2335 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2336 if(valuelen > 0 && value[valuelen - 1] == '/')
2337 value[valuelen - 1] = '\0';
2339 imap->section = value;
2343 Curl_safefree(name);
2344 Curl_safefree(value);
2346 return CURLE_URL_MALFORMAT;
2349 Curl_safefree(name);
2350 Curl_safefree(value);
2353 /* Any extra stuff at the end of the URL is an error */
2355 return CURLE_URL_MALFORMAT;
2360 /***********************************************************************
2362 * imap_parse_custom_request()
2364 * Parse the custom request.
2366 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2368 CURLcode result = CURLE_OK;
2369 struct SessionHandle *data = conn->data;
2370 struct IMAP *imap = data->state.proto.imap;
2371 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2374 /* URL decode the custom request */
2375 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2377 /* Extract the parameters if specified */
2379 const char *params = imap->custom;
2381 while(*params && *params != ' ')
2385 imap->custom_params = strdup(params);
2386 imap->custom[params - imap->custom] = '\0';
2388 if(!imap->custom_params)
2389 result = CURLE_OUT_OF_MEMORY;
2397 #endif /* CURL_DISABLE_IMAP */