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_regular_transfer(struct connectdata *conn, bool *done);
90 static CURLcode imap_do(struct connectdata *conn, bool *done);
91 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
93 static CURLcode imap_connect(struct connectdata *conn, bool *done);
94 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
95 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
96 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
98 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
99 static CURLcode imap_setup_connection(struct connectdata *conn);
100 static char *imap_atom(const char *str);
101 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
102 static CURLcode imap_parse_url_options(struct connectdata *conn);
103 static CURLcode imap_parse_url_path(struct connectdata *conn);
104 static CURLcode imap_parse_custom_request(struct connectdata *conn);
107 * IMAP protocol handler.
110 const struct Curl_handler Curl_handler_imap = {
112 imap_setup_connection, /* setup_connection */
114 imap_done, /* done */
115 ZERO_NULL, /* do_more */
116 imap_connect, /* connect_it */
117 imap_multi_statemach, /* connecting */
118 imap_doing, /* doing */
119 imap_getsock, /* proto_getsock */
120 imap_getsock, /* doing_getsock */
121 ZERO_NULL, /* domore_getsock */
122 ZERO_NULL, /* perform_getsock */
123 imap_disconnect, /* disconnect */
124 ZERO_NULL, /* readwrite */
125 PORT_IMAP, /* defport */
126 CURLPROTO_IMAP, /* protocol */
127 PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
128 | PROTOPT_NOURLQUERY /* flags */
133 * IMAPS protocol handler.
136 const struct Curl_handler Curl_handler_imaps = {
137 "IMAPS", /* scheme */
138 imap_setup_connection, /* setup_connection */
140 imap_done, /* done */
141 ZERO_NULL, /* do_more */
142 imap_connect, /* connect_it */
143 imap_multi_statemach, /* connecting */
144 imap_doing, /* doing */
145 imap_getsock, /* proto_getsock */
146 imap_getsock, /* doing_getsock */
147 ZERO_NULL, /* domore_getsock */
148 ZERO_NULL, /* perform_getsock */
149 imap_disconnect, /* disconnect */
150 ZERO_NULL, /* readwrite */
151 PORT_IMAPS, /* defport */
152 CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
153 PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
154 | PROTOPT_NOURLQUERY /* flags */
158 #ifndef CURL_DISABLE_HTTP
160 * HTTP-proxyed IMAP protocol handler.
163 static const struct Curl_handler Curl_handler_imap_proxy = {
165 ZERO_NULL, /* setup_connection */
166 Curl_http, /* do_it */
167 Curl_http_done, /* done */
168 ZERO_NULL, /* do_more */
169 ZERO_NULL, /* connect_it */
170 ZERO_NULL, /* connecting */
171 ZERO_NULL, /* doing */
172 ZERO_NULL, /* proto_getsock */
173 ZERO_NULL, /* doing_getsock */
174 ZERO_NULL, /* domore_getsock */
175 ZERO_NULL, /* perform_getsock */
176 ZERO_NULL, /* disconnect */
177 ZERO_NULL, /* readwrite */
178 PORT_IMAP, /* defport */
179 CURLPROTO_HTTP, /* protocol */
180 PROTOPT_NONE /* flags */
185 * HTTP-proxyed IMAPS protocol handler.
188 static const struct Curl_handler Curl_handler_imaps_proxy = {
189 "IMAPS", /* scheme */
190 ZERO_NULL, /* setup_connection */
191 Curl_http, /* do_it */
192 Curl_http_done, /* done */
193 ZERO_NULL, /* do_more */
194 ZERO_NULL, /* connect_it */
195 ZERO_NULL, /* connecting */
196 ZERO_NULL, /* doing */
197 ZERO_NULL, /* proto_getsock */
198 ZERO_NULL, /* doing_getsock */
199 ZERO_NULL, /* domore_getsock */
200 ZERO_NULL, /* perform_getsock */
201 ZERO_NULL, /* disconnect */
202 ZERO_NULL, /* readwrite */
203 PORT_IMAPS, /* defport */
204 CURLPROTO_HTTP, /* protocol */
205 PROTOPT_NONE /* flags */
211 static void imap_to_imaps(struct connectdata *conn)
213 conn->handler = &Curl_handler_imaps;
216 #define imap_to_imaps(x) Curl_nop_stmt
219 /***********************************************************************
223 * Determines whether the untagged response is related to the specified
224 * command by checking if it is in format "* <command-name> ..." or
225 * "* <number> <command-name> ...".
227 * The "* " marker is assumed to have already been checked by the caller.
229 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
231 const char *end = line + len;
232 size_t cmd_len = strlen(cmd);
234 /* Skip the untagged response marker */
237 /* Do we have a number after the marker? */
238 if(line < end && ISDIGIT(*line)) {
239 /* Skip the number */
242 while(line < end && ISDIGIT(*line));
244 /* Do we have the space character? */
245 if(line == end || *line != ' ')
251 /* Does the command name match and is it followed by a space character or at
253 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
254 (line[cmd_len] == ' ' || line + cmd_len == end))
260 /***********************************************************************
264 * Checks whether the given string is a valid tagged, untagged or continuation
265 * response which can be processed by the response handler.
267 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
270 struct IMAP *imap = conn->data->state.proto.imap;
271 struct imap_conn *imapc = &conn->proto.imapc;
272 const char *id = imapc->resptag;
273 size_t id_len = strlen(id);
275 /* Do we have a tagged command response? */
276 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
280 if(len >= 2 && !memcmp(line, "OK", 2))
282 else if(len >= 2 && !memcmp(line, "NO", 2))
284 else if(len >= 3 && !memcmp(line, "BAD", 3))
287 failf(conn->data, "Bad tagged response");
294 /* Do we have an untagged command response? */
295 if(len >= 2 && !memcmp("* ", line, 2)) {
296 switch(imapc->state) {
297 /* States which are interested in untagged responses */
298 case IMAP_CAPABILITY:
299 if(!imap_matchresp(line, len, "CAPABILITY"))
304 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
305 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
306 (strcmp(imap->custom, "STORE") ||
307 !imap_matchresp(line, len, "FETCH")) &&
308 strcmp(imap->custom, "SELECT") &&
309 strcmp(imap->custom, "EXAMINE")))
314 /* SELECT is special in that its untagged responses do not have a
315 common prefix so accept anything! */
319 if(!imap_matchresp(line, len, "FETCH"))
323 /* Ignore other untagged responses */
332 /* Do we have a continuation response? */
333 if((len == 3 && !memcmp("+", line, 1)) ||
334 (len >= 2 && !memcmp("+ ", line, 2))) {
335 switch(imapc->state) {
336 /* States which are interested in continuation responses */
337 case IMAP_AUTHENTICATE_PLAIN:
338 case IMAP_AUTHENTICATE_LOGIN:
339 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
340 case IMAP_AUTHENTICATE_CRAMMD5:
341 case IMAP_AUTHENTICATE_DIGESTMD5:
342 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
343 case IMAP_AUTHENTICATE_NTLM:
344 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
345 case IMAP_AUTHENTICATE_FINAL:
351 failf(conn->data, "Unexpected continuation response");
359 return FALSE; /* Nothing for us */
362 /***********************************************************************
366 * This is the ONLY way to change IMAP state!
368 static void state(struct connectdata *conn, imapstate newstate)
370 struct imap_conn *imapc = &conn->proto.imapc;
371 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
372 /* for debug purposes */
373 static const char * const names[]={
379 "AUTHENTICATE_PLAIN",
380 "AUTHENTICATE_LOGIN",
381 "AUTHENTICATE_LOGIN_PASSWD",
382 "AUTHENTICATE_CRAMMD5",
383 "AUTHENTICATE_DIGESTMD5",
384 "AUTHENTICATE_DIGESTMD5_RESP",
386 "AUTHENTICATE_NTLM_TYPE2MSG",
387 "AUTHENTICATE_FINAL",
399 if(imapc->state != newstate)
400 infof(conn->data, "IMAP %p state change from %s to %s\n",
401 imapc, names[imapc->state], names[newstate]);
404 imapc->state = newstate;
407 /***********************************************************************
409 * imap_perform_capability()
411 * Sends the CAPABILITY command in order to obtain a list of server side
412 * supported capabilities.
414 static CURLcode imap_perform_capability(struct connectdata *conn)
416 CURLcode result = CURLE_OK;
417 struct imap_conn *imapc = &conn->proto.imapc;
419 imapc->authmechs = 0; /* No known authentication mechanisms yet */
420 imapc->authused = 0; /* Clear the authentication mechanism used */
421 imapc->tls_supported = FALSE; /* Clear the TLS capability */
423 /* Send the CAPABILITY command */
424 result = imap_sendf(conn, "CAPABILITY");
427 state(conn, IMAP_CAPABILITY);
432 /***********************************************************************
434 * imap_perform_starttls()
436 * Sends the STARTTLS command to start the upgrade to TLS.
438 static CURLcode imap_perform_starttls(struct connectdata *conn)
440 CURLcode result = CURLE_OK;
442 /* Send the STARTTLS command */
443 result = imap_sendf(conn, "STARTTLS");
446 state(conn, IMAP_STARTTLS);
451 /***********************************************************************
453 * imap_perform_upgrade_tls()
455 * Performs the upgrade to TLS.
457 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
459 CURLcode result = CURLE_OK;
460 struct imap_conn *imapc = &conn->proto.imapc;
462 /* Start the SSL connection */
463 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
466 if(imapc->state != IMAP_UPGRADETLS)
467 state(conn, IMAP_UPGRADETLS);
471 result = imap_perform_capability(conn);
478 /***********************************************************************
480 * imap_perform_login()
482 * Sends a clear text LOGIN command to authenticate with.
484 static CURLcode imap_perform_login(struct connectdata *conn)
486 CURLcode result = CURLE_OK;
490 /* Check we have a username and password to authenticate with and end the
491 connect phase if we don't */
492 if(!conn->bits.user_passwd) {
493 state(conn, IMAP_STOP);
498 /* Make sure the username and password are in the correct atom format */
499 user = imap_atom(conn->user);
500 passwd = imap_atom(conn->passwd);
502 /* Send the LOGIN command */
503 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
504 passwd ? passwd : "");
507 Curl_safefree(passwd);
510 state(conn, IMAP_LOGIN);
515 /***********************************************************************
517 * imap_perform_authenticate()
519 * Sends an AUTHENTICATE command allowing the client to login with the
520 * appropriate SASL authentication mechanism.
522 * Additionally, the function will perform fallback to the LOGIN command
523 * should a common mechanism not be available between the client and server.
525 static CURLcode imap_perform_authenticate(struct connectdata *conn)
527 CURLcode result = CURLE_OK;
528 struct SessionHandle *data = conn->data;
529 struct imap_conn *imapc = &conn->proto.imapc;
530 const char *mech = NULL;
531 char *initresp = NULL;
533 imapstate state1 = IMAP_STOP;
534 imapstate state2 = IMAP_STOP;
536 /* Check we have a username and password to authenticate with and end the
537 connect phase if we don't */
538 if(!conn->bits.user_passwd) {
539 state(conn, IMAP_STOP);
544 /* Calculate the supported authentication mechanism by decreasing order of
546 #ifndef CURL_DISABLE_CRYPTO_AUTH
547 if((imapc->authmechs & SASL_MECH_DIGEST_MD5) &&
548 (imapc->prefmech & SASL_MECH_DIGEST_MD5)) {
550 state1 = IMAP_AUTHENTICATE_DIGESTMD5;
551 imapc->authused = SASL_MECH_DIGEST_MD5;
553 else if((imapc->authmechs & SASL_MECH_CRAM_MD5) &&
554 (imapc->prefmech & SASL_MECH_CRAM_MD5)) {
556 state1 = IMAP_AUTHENTICATE_CRAMMD5;
557 imapc->authused = SASL_MECH_CRAM_MD5;
562 if((imapc->authmechs & SASL_MECH_NTLM) &&
563 (imapc->prefmech & SASL_MECH_NTLM)) {
565 state1 = IMAP_AUTHENTICATE_NTLM;
566 state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
567 imapc->authused = SASL_MECH_NTLM;
569 if(imapc->ir_supported || data->set.sasl_ir)
570 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
576 if((imapc->authmechs & SASL_MECH_LOGIN) &&
577 (imapc->prefmech & SASL_MECH_LOGIN)) {
579 state1 = IMAP_AUTHENTICATE_LOGIN;
580 state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
581 imapc->authused = SASL_MECH_LOGIN;
583 if(imapc->ir_supported || data->set.sasl_ir)
584 result = Curl_sasl_create_login_message(conn->data, conn->user,
587 else if((imapc->authmechs & SASL_MECH_PLAIN) &&
588 (imapc->prefmech & SASL_MECH_PLAIN)) {
590 state1 = IMAP_AUTHENTICATE_PLAIN;
591 state2 = IMAP_AUTHENTICATE_FINAL;
592 imapc->authused = SASL_MECH_PLAIN;
594 if(imapc->ir_supported || data->set.sasl_ir)
595 result = Curl_sasl_create_plain_message(conn->data, conn->user,
596 conn->passwd, &initresp, &len);
603 /* Perform SASL based authentication */
605 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
611 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
617 Curl_safefree(initresp);
619 else if(!imapc->login_disabled)
620 /* Perform clear text authentication */
621 result = imap_perform_login(conn);
623 /* Other mechanisms not supported */
624 infof(conn->data, "No known authentication mechanisms supported!\n");
625 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, "");
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 is not. When called as
1699 * a part of the easy interface, it will always be TRUE.
1701 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1703 CURLcode result = CURLE_OK;
1704 struct imap_conn *imapc = &conn->proto.imapc;
1705 struct pingpong *pp = &imapc->pp;
1707 *done = FALSE; /* default to not done yet */
1709 /* If there already is a protocol-specific struct allocated for this
1710 sessionhandle, deal with it */
1711 Curl_reset_reqproto(conn);
1713 /* Initialise the IMAP layer */
1714 result = imap_init(conn);
1718 /* We always support persistent connections in IMAP */
1719 conn->bits.close = FALSE;
1721 /* Set the default response time-out */
1722 pp->response_time = RESP_TIMEOUT;
1723 pp->statemach_act = imap_statemach_act;
1724 pp->endofresp = imap_endofresp;
1727 /* Set the default preferred authentication mechanism */
1728 imapc->prefmech = SASL_AUTH_ANY;
1730 /* Initialise the pingpong layer */
1733 /* Parse the URL options */
1734 result = imap_parse_url_options(conn);
1738 /* Start off waiting for the server greeting response */
1739 state(conn, IMAP_SERVERGREET);
1741 /* Start off with an response id of '*' */
1742 strcpy(imapc->resptag, "*");
1744 result = imap_multi_statemach(conn, done);
1749 /***********************************************************************
1753 * The DONE function. This does what needs to be done after a single DO has
1756 * Input argument is already checked for validity.
1758 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1761 CURLcode result = CURLE_OK;
1762 struct SessionHandle *data = conn->data;
1763 struct IMAP *imap = data->state.proto.imap;
1768 /* When the easy handle is removed from the multi interface while libcurl
1769 is still trying to resolve the host name, the IMAP struct is not yet
1770 initialized. However, the removal action calls Curl_done() which in
1771 turn calls this function, so we simply return success. */
1775 conn->bits.close = TRUE; /* marked for closure */
1776 result = status; /* use the already set error code */
1778 else if(!data->set.connect_only && !imap->custom &&
1779 (imap->uid || data->set.upload)) {
1780 /* Handle responses after FETCH or APPEND transfer has finished */
1781 if(!data->set.upload)
1782 state(conn, IMAP_FETCH_FINAL);
1784 /* End the APPEND command first by sending an empty line */
1785 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1787 state(conn, IMAP_APPEND_FINAL);
1790 /* Run the state-machine
1792 TODO: when the multi interface is used, this _really_ should be using
1793 the imap_multi_statemach function but we have no general support for
1794 non-blocking DONE operations, not in the multi state machine and with
1795 Curl_done() invokes on several places in the code!
1798 result = imap_block_statemach(conn);
1801 /* Cleanup our per-request based variables */
1802 Curl_safefree(imap->mailbox);
1803 Curl_safefree(imap->uidvalidity);
1804 Curl_safefree(imap->uid);
1805 Curl_safefree(imap->section);
1806 Curl_safefree(imap->custom);
1807 Curl_safefree(imap->custom_params);
1809 /* Clear the transfer mode for the next request */
1810 imap->transfer = FTPTRANSFER_BODY;
1815 /***********************************************************************
1819 * This is the actual DO function for IMAP. Fetch or append a message, or do
1820 * other things according to the options previously setup.
1822 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1825 /* This is IMAP and no proxy */
1826 CURLcode result = CURLE_OK;
1827 struct SessionHandle *data = conn->data;
1828 struct IMAP *imap = data->state.proto.imap;
1829 struct imap_conn *imapc = &conn->proto.imapc;
1830 bool selected = FALSE;
1832 DEBUGF(infof(conn->data, "DO phase starts\n"));
1834 if(conn->data->set.opt_no_body) {
1835 /* Requested no body means no transfer */
1836 imap->transfer = FTPTRANSFER_INFO;
1839 *dophase_done = FALSE; /* not done yet */
1841 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1842 has already been selected on this connection */
1843 if(imap->mailbox && imapc->mailbox &&
1844 !strcmp(imap->mailbox, imapc->mailbox) &&
1845 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1846 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1849 /* Start the first command in the DO phase */
1850 if(conn->data->set.upload)
1851 /* APPEND can be executed directly */
1852 result = imap_perform_append(conn);
1853 else if(imap->custom && (selected || !imap->mailbox))
1854 /* Custom command using the same mailbox or no mailbox */
1855 result = imap_perform_list(conn);
1856 else if(!imap->custom && selected && imap->uid)
1857 /* FETCH from the same mailbox */
1858 result = imap_perform_fetch(conn);
1859 else if(imap->mailbox && !selected && (imap->custom || imap->uid))
1860 /* SELECT the mailbox */
1861 result = imap_perform_select(conn);
1864 result = imap_perform_list(conn);
1869 /* Run the state-machine */
1870 result = imap_multi_statemach(conn, dophase_done);
1872 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1875 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1880 /***********************************************************************
1884 * This function is registered as 'curl_do' function. It decodes the path
1885 * parts etc as a wrapper to the actual DO function (imap_perform).
1887 * The input argument is already checked for validity.
1889 static CURLcode imap_do(struct connectdata *conn, bool *done)
1891 CURLcode result = CURLE_OK;
1893 *done = FALSE; /* default to false */
1895 /* Since connections can be re-used between SessionHandles, there might be a
1896 connection already existing but on a fresh SessionHandle struct. As such
1897 we make sure we have a good IMAP struct to play with. For new connections
1898 the IMAP struct is allocated and setup in the imap_connect() function. */
1899 Curl_reset_reqproto(conn);
1900 result = imap_init(conn);
1904 /* Parse the URL path */
1905 result = imap_parse_url_path(conn);
1909 /* Parse the custom request */
1910 result = imap_parse_custom_request(conn);
1914 result = imap_regular_transfer(conn, done);
1919 /***********************************************************************
1923 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1924 * resources. BLOCKING.
1926 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1928 struct imap_conn *imapc = &conn->proto.imapc;
1930 /* We cannot send quit unconditionally. If this connection is stale or
1931 bad in any way, sending quit and waiting around here will make the
1932 disconnect wait in vain and cause more problems than we need to. */
1934 /* The IMAP session may or may not have been allocated/setup at this
1936 if(!dead_connection && imapc->pp.conn)
1937 if(!imap_perform_logout(conn))
1938 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1940 /* Disconnect from the server */
1941 Curl_pp_disconnect(&imapc->pp);
1943 /* Cleanup the SASL module */
1944 Curl_sasl_cleanup(conn, imapc->authused);
1946 /* Cleanup our connection based variables */
1947 Curl_safefree(imapc->mailbox);
1948 Curl_safefree(imapc->mailbox_uidvalidity);
1953 /* Call this when the DO phase has completed */
1954 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1956 struct IMAP *imap = conn->data->state.proto.imap;
1960 if(imap->transfer != FTPTRANSFER_BODY)
1961 /* no data to transfer */
1962 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1967 /* Called from multi.c while DOing */
1968 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1970 CURLcode result = imap_multi_statemach(conn, dophase_done);
1973 DEBUGF(infof(conn->data, "DO phase failed\n"));
1974 else if(*dophase_done) {
1975 result = imap_dophase_done(conn, FALSE /* not connected */);
1977 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1983 /***********************************************************************
1985 * imap_regular_transfer()
1987 * The input argument is already checked for validity.
1989 * Performs all commands done before a regular transfer between a local and a
1992 static CURLcode imap_regular_transfer(struct connectdata *conn,
1995 CURLcode result = CURLE_OK;
1996 bool connected = FALSE;
1997 struct SessionHandle *data = conn->data;
1999 /* Make sure size is unknown at this point */
2000 data->req.size = -1;
2002 /* Set the progress data */
2003 Curl_pgrsSetUploadCounter(data, 0);
2004 Curl_pgrsSetDownloadCounter(data, 0);
2005 Curl_pgrsSetUploadSize(data, 0);
2006 Curl_pgrsSetDownloadSize(data, 0);
2008 /* Carry out the perform */
2009 result = imap_perform(conn, &connected, dophase_done);
2011 /* Perform post DO phase operations if necessary */
2012 if(!result && *dophase_done)
2013 result = imap_dophase_done(conn, connected);
2018 static CURLcode imap_setup_connection(struct connectdata *conn)
2020 struct SessionHandle *data = conn->data;
2022 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2023 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2024 switch and use HTTP operations only */
2025 #ifndef CURL_DISABLE_HTTP
2026 if(conn->handler == &Curl_handler_imap)
2027 conn->handler = &Curl_handler_imap_proxy;
2030 conn->handler = &Curl_handler_imaps_proxy;
2032 failf(data, "IMAPS not supported!");
2033 return CURLE_UNSUPPORTED_PROTOCOL;
2037 /* We explicitly mark this connection as persistent here as we're doing
2038 IMAP over HTTP and thus we accidentally avoid setting this value
2040 conn->bits.close = FALSE;
2042 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2043 return CURLE_UNSUPPORTED_PROTOCOL;
2047 data->state.path++; /* don't include the initial slash */
2052 /***********************************************************************
2056 * Sends the formated string as an IMAP command to the server.
2058 * Designed to never block.
2060 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
2062 CURLcode result = CURLE_OK;
2063 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' + (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 */
2081 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
2083 Curl_safefree(taggedfmt);
2089 /***********************************************************************
2093 * Checks the input string for characters that need escaping and returns an
2094 * atom ready for sending to the server.
2096 * The returned string needs to be freed.
2099 static char *imap_atom(const char *str)
2103 size_t backsp_count = 0;
2104 size_t quote_count = 0;
2105 bool space_exists = FALSE;
2107 char *newstr = NULL;
2112 /* Count any unescapped characters */
2120 space_exists = TRUE;
2125 /* Does the input contain any unescapped characters? */
2126 if(!backsp_count && !quote_count && !space_exists)
2129 /* Calculate the new string length */
2130 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
2132 /* Allocate the new string */
2133 newstr = (char *) malloc((newlen + 1) * sizeof(char));
2137 /* Surround the string in quotes if necessary */
2141 newstr[newlen - 1] = '"';
2145 /* Copy the string, escaping backslash and quote characters along the way */
2148 if(*p1 == '\\' || *p1 == '"') {
2159 /* Terminate the string */
2160 newstr[newlen] = '\0';
2165 /***********************************************************************
2169 * Portable test of whether the specified char is a "bchar" as defined in the
2170 * grammar of RFC-5092.
2172 static bool imap_is_bchar(char ch)
2176 case ':': case '@': case '/':
2177 /* bchar -> achar */
2179 /* bchar -> achar -> uchar -> unreserved */
2180 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2181 case '7': case '8': case '9':
2182 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2183 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2184 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2185 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2186 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2187 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2188 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2189 case 'v': case 'w': case 'x': case 'y': case 'z':
2190 case '-': case '.': case '_': case '~':
2191 /* bchar -> achar -> uchar -> sub-delims-sh */
2192 case '!': case '$': case '\'': case '(': case ')': case '*':
2194 /* bchar -> achar -> uchar -> pct-encoded */
2195 case '%': /* HEXDIG chars are already included above */
2203 /***********************************************************************
2205 * imap_parse_url_options()
2207 * Parse the URL login options.
2209 static CURLcode imap_parse_url_options(struct connectdata *conn)
2211 CURLcode result = CURLE_OK;
2212 struct imap_conn *imapc = &conn->proto.imapc;
2213 const char *options = conn->options;
2214 const char *ptr = options;
2217 const char *key = ptr;
2219 while(*ptr && *ptr != '=')
2222 if(strnequal(key, "AUTH", 4)) {
2223 const char *value = ptr + 1;
2225 if(strequal(value, "*"))
2226 imapc->prefmech = SASL_AUTH_ANY;
2227 else if(strequal(value, "LOGIN"))
2228 imapc->prefmech = SASL_MECH_LOGIN;
2229 else if(strequal(value, "PLAIN"))
2230 imapc->prefmech = SASL_MECH_PLAIN;
2231 else if(strequal(value, "CRAM-MD5"))
2232 imapc->prefmech = SASL_MECH_CRAM_MD5;
2233 else if(strequal(value, "DIGEST-MD5"))
2234 imapc->prefmech = SASL_MECH_DIGEST_MD5;
2235 else if(strequal(value, "GSSAPI"))
2236 imapc->prefmech = SASL_MECH_GSSAPI;
2237 else if(strequal(value, "NTLM"))
2238 imapc->prefmech = SASL_MECH_NTLM;
2240 imapc->prefmech = SASL_AUTH_NONE;
2243 result = CURLE_URL_MALFORMAT;
2249 /***********************************************************************
2251 * imap_parse_url_path()
2253 * Parse the URL path into separate path components.
2256 static CURLcode imap_parse_url_path(struct connectdata *conn)
2258 /* The imap struct is already initialised in imap_connect() */
2259 CURLcode result = CURLE_OK;
2260 struct SessionHandle *data = conn->data;
2261 struct IMAP *imap = data->state.proto.imap;
2262 const char *begin = data->state.path;
2263 const char *ptr = begin;
2265 /* See how much of the URL is a valid path and decode it */
2266 while(imap_is_bchar(*ptr))
2270 /* Remove the trailing slash if present */
2271 const char *end = ptr;
2272 if(end > begin && end[-1] == '/')
2275 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2281 imap->mailbox = NULL;
2283 /* There can be any number of parameters in the form ";NAME=VALUE" */
2284 while(*ptr == ';') {
2289 /* Find the length of the name parameter */
2291 while(*ptr && *ptr != '=')
2295 return CURLE_URL_MALFORMAT;
2297 /* Decode the name parameter */
2298 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2302 /* Find the length of the value parameter */
2304 while(imap_is_bchar(*ptr))
2307 /* Decode the value parameter */
2308 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2310 Curl_safefree(name);
2314 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2316 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2317 stripping of the trailing slash character if it is present.
2319 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2320 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2321 if(valuelen > 0 && value[valuelen - 1] == '/')
2322 value[valuelen - 1] = '\0';
2324 imap->uidvalidity = value;
2327 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2328 if(valuelen > 0 && value[valuelen - 1] == '/')
2329 value[valuelen - 1] = '\0';
2334 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2335 if(valuelen > 0 && value[valuelen - 1] == '/')
2336 value[valuelen - 1] = '\0';
2338 imap->section = value;
2342 Curl_safefree(name);
2343 Curl_safefree(value);
2345 return CURLE_URL_MALFORMAT;
2348 Curl_safefree(name);
2349 Curl_safefree(value);
2352 /* Any extra stuff at the end of the URL is an error */
2354 return CURLE_URL_MALFORMAT;
2359 /***********************************************************************
2361 * imap_parse_custom_request()
2363 * Parse the custom request.
2365 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2367 CURLcode result = CURLE_OK;
2368 struct SessionHandle *data = conn->data;
2369 struct IMAP *imap = data->state.proto.imap;
2370 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2373 /* URL decode the custom request */
2374 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2376 /* Extract the parameters if specified */
2378 const char *params = imap->custom;
2380 while(*params && *params != ' ')
2384 imap->custom_params = strdup(params);
2385 imap->custom[params - imap->custom] = '\0';
2387 if(!imap->custom_params)
2388 result = CURLE_OUT_OF_MEMORY;
2396 #endif /* CURL_DISABLE_IMAP */