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
29 * RFC6749 OAuth 2.0 Authorization Framework
31 ***************************************************************************/
33 #include "curl_setup.h"
35 #ifndef CURL_DISABLE_IMAP
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
44 #include <sys/utsname.h>
54 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
56 #define in_addr_t unsigned long
59 #include <curl/curl.h>
67 #include "http.h" /* for HTTP proxy tunnel stuff */
71 #include "strtoofft.h"
80 #include "curl_sasl.h"
83 #define _MPRINTF_REPLACE /* use our functions only */
84 #include <curl/mprintf.h>
86 #include "curl_memory.h"
87 /* The last #include file should be: */
90 /* Local API functions */
91 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
92 static CURLcode imap_do(struct connectdata *conn, bool *done);
93 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
95 static CURLcode imap_connect(struct connectdata *conn, bool *done);
96 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
97 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
98 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
100 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
101 static CURLcode imap_setup_connection(struct connectdata *conn);
102 static char *imap_atom(const char *str);
103 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
104 static CURLcode imap_parse_url_options(struct connectdata *conn);
105 static CURLcode imap_parse_url_path(struct connectdata *conn);
106 static CURLcode imap_parse_custom_request(struct connectdata *conn);
109 * IMAP protocol handler.
112 const struct Curl_handler Curl_handler_imap = {
114 imap_setup_connection, /* setup_connection */
116 imap_done, /* done */
117 ZERO_NULL, /* do_more */
118 imap_connect, /* connect_it */
119 imap_multi_statemach, /* connecting */
120 imap_doing, /* doing */
121 imap_getsock, /* proto_getsock */
122 imap_getsock, /* doing_getsock */
123 ZERO_NULL, /* domore_getsock */
124 ZERO_NULL, /* perform_getsock */
125 imap_disconnect, /* disconnect */
126 ZERO_NULL, /* readwrite */
127 PORT_IMAP, /* defport */
128 CURLPROTO_IMAP, /* protocol */
129 PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
130 | PROTOPT_NOURLQUERY /* flags */
135 * IMAPS protocol handler.
138 const struct Curl_handler Curl_handler_imaps = {
139 "IMAPS", /* scheme */
140 imap_setup_connection, /* setup_connection */
142 imap_done, /* done */
143 ZERO_NULL, /* do_more */
144 imap_connect, /* connect_it */
145 imap_multi_statemach, /* connecting */
146 imap_doing, /* doing */
147 imap_getsock, /* proto_getsock */
148 imap_getsock, /* doing_getsock */
149 ZERO_NULL, /* domore_getsock */
150 ZERO_NULL, /* perform_getsock */
151 imap_disconnect, /* disconnect */
152 ZERO_NULL, /* readwrite */
153 PORT_IMAPS, /* defport */
154 CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
155 PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
156 | PROTOPT_NOURLQUERY /* flags */
160 #ifndef CURL_DISABLE_HTTP
162 * HTTP-proxyed IMAP protocol handler.
165 static const struct Curl_handler Curl_handler_imap_proxy = {
167 Curl_http_setup_conn, /* setup_connection */
168 Curl_http, /* do_it */
169 Curl_http_done, /* done */
170 ZERO_NULL, /* do_more */
171 ZERO_NULL, /* connect_it */
172 ZERO_NULL, /* connecting */
173 ZERO_NULL, /* doing */
174 ZERO_NULL, /* proto_getsock */
175 ZERO_NULL, /* doing_getsock */
176 ZERO_NULL, /* domore_getsock */
177 ZERO_NULL, /* perform_getsock */
178 ZERO_NULL, /* disconnect */
179 ZERO_NULL, /* readwrite */
180 PORT_IMAP, /* defport */
181 CURLPROTO_HTTP, /* protocol */
182 PROTOPT_NONE /* flags */
187 * HTTP-proxyed IMAPS protocol handler.
190 static const struct Curl_handler Curl_handler_imaps_proxy = {
191 "IMAPS", /* scheme */
192 Curl_http_setup_conn, /* setup_connection */
193 Curl_http, /* do_it */
194 Curl_http_done, /* done */
195 ZERO_NULL, /* do_more */
196 ZERO_NULL, /* connect_it */
197 ZERO_NULL, /* connecting */
198 ZERO_NULL, /* doing */
199 ZERO_NULL, /* proto_getsock */
200 ZERO_NULL, /* doing_getsock */
201 ZERO_NULL, /* domore_getsock */
202 ZERO_NULL, /* perform_getsock */
203 ZERO_NULL, /* disconnect */
204 ZERO_NULL, /* readwrite */
205 PORT_IMAPS, /* defport */
206 CURLPROTO_HTTP, /* protocol */
207 PROTOPT_NONE /* flags */
213 static void imap_to_imaps(struct connectdata *conn)
215 conn->handler = &Curl_handler_imaps;
218 #define imap_to_imaps(x) Curl_nop_stmt
221 /***********************************************************************
225 * Determines whether the untagged response is related to the specified
226 * command by checking if it is in format "* <command-name> ..." or
227 * "* <number> <command-name> ...".
229 * The "* " marker is assumed to have already been checked by the caller.
231 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
233 const char *end = line + len;
234 size_t cmd_len = strlen(cmd);
236 /* Skip the untagged response marker */
239 /* Do we have a number after the marker? */
240 if(line < end && ISDIGIT(*line)) {
241 /* Skip the number */
244 while(line < end && ISDIGIT(*line));
246 /* Do we have the space character? */
247 if(line == end || *line != ' ')
253 /* Does the command name match and is it followed by a space character or at
255 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
256 (line[cmd_len] == ' ' || line + cmd_len == end))
262 /***********************************************************************
266 * Checks whether the given string is a valid tagged, untagged or continuation
267 * response which can be processed by the response handler.
269 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
272 struct IMAP *imap = conn->data->req.protop;
273 struct imap_conn *imapc = &conn->proto.imapc;
274 const char *id = imapc->resptag;
275 size_t id_len = strlen(id);
277 /* Do we have a tagged command response? */
278 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
282 if(len >= 2 && !memcmp(line, "OK", 2))
284 else if(len >= 2 && !memcmp(line, "NO", 2))
286 else if(len >= 3 && !memcmp(line, "BAD", 3))
289 failf(conn->data, "Bad tagged response");
296 /* Do we have an untagged command response? */
297 if(len >= 2 && !memcmp("* ", line, 2)) {
298 switch(imapc->state) {
299 /* States which are interested in untagged responses */
300 case IMAP_CAPABILITY:
301 if(!imap_matchresp(line, len, "CAPABILITY"))
306 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
307 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
308 (strcmp(imap->custom, "STORE") ||
309 !imap_matchresp(line, len, "FETCH")) &&
310 strcmp(imap->custom, "SELECT") &&
311 strcmp(imap->custom, "EXAMINE") &&
312 strcmp(imap->custom, "SEARCH") &&
313 strcmp(imap->custom, "EXPUNGE") &&
314 strcmp(imap->custom, "LSUB") &&
315 strcmp(imap->custom, "UID") &&
316 strcmp(imap->custom, "NOOP")))
321 /* SELECT is special in that its untagged responses do not have a
322 common prefix so accept anything! */
326 if(!imap_matchresp(line, len, "FETCH"))
330 /* Ignore other untagged responses */
339 /* Do we have a continuation response? This should be a + symbol followed by
340 a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
341 APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
342 some e-mail servers ignore this and only send a single + instead. */
343 if((len == 3 && !memcmp("+", line, 1)) ||
344 (len >= 2 && !memcmp("+ ", line, 2))) {
345 switch(imapc->state) {
346 /* States which are interested in continuation responses */
347 case IMAP_AUTHENTICATE_PLAIN:
348 case IMAP_AUTHENTICATE_LOGIN:
349 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
350 case IMAP_AUTHENTICATE_CRAMMD5:
351 case IMAP_AUTHENTICATE_DIGESTMD5:
352 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
353 case IMAP_AUTHENTICATE_NTLM:
354 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
355 case IMAP_AUTHENTICATE_XOAUTH2:
356 case IMAP_AUTHENTICATE_FINAL:
362 failf(conn->data, "Unexpected continuation response");
370 return FALSE; /* Nothing for us */
373 /***********************************************************************
377 * This is the ONLY way to change IMAP state!
379 static void state(struct connectdata *conn, imapstate newstate)
381 struct imap_conn *imapc = &conn->proto.imapc;
382 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
383 /* for debug purposes */
384 static const char * const names[]={
390 "AUTHENTICATE_PLAIN",
391 "AUTHENTICATE_LOGIN",
392 "AUTHENTICATE_LOGIN_PASSWD",
393 "AUTHENTICATE_CRAMMD5",
394 "AUTHENTICATE_DIGESTMD5",
395 "AUTHENTICATE_DIGESTMD5_RESP",
397 "AUTHENTICATE_NTLM_TYPE2MSG",
398 "AUTHENTICATE_XOAUTH2",
399 "AUTHENTICATE_FINAL",
411 if(imapc->state != newstate)
412 infof(conn->data, "IMAP %p state change from %s to %s\n",
413 (void *)imapc, names[imapc->state], names[newstate]);
416 imapc->state = newstate;
419 /***********************************************************************
421 * imap_perform_capability()
423 * Sends the CAPABILITY command in order to obtain a list of server side
424 * supported capabilities.
426 static CURLcode imap_perform_capability(struct connectdata *conn)
428 CURLcode result = CURLE_OK;
429 struct imap_conn *imapc = &conn->proto.imapc;
431 imapc->authmechs = 0; /* No known authentication mechanisms yet */
432 imapc->authused = 0; /* Clear the authentication mechanism used */
433 imapc->tls_supported = FALSE; /* Clear the TLS capability */
435 /* Send the CAPABILITY command */
436 result = imap_sendf(conn, "CAPABILITY");
439 state(conn, IMAP_CAPABILITY);
444 /***********************************************************************
446 * imap_perform_starttls()
448 * Sends the STARTTLS command to start the upgrade to TLS.
450 static CURLcode imap_perform_starttls(struct connectdata *conn)
452 CURLcode result = CURLE_OK;
454 /* Send the STARTTLS command */
455 result = imap_sendf(conn, "STARTTLS");
458 state(conn, IMAP_STARTTLS);
463 /***********************************************************************
465 * imap_perform_upgrade_tls()
467 * Performs the upgrade to TLS.
469 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
471 CURLcode result = CURLE_OK;
472 struct imap_conn *imapc = &conn->proto.imapc;
474 /* Start the SSL connection */
475 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
478 if(imapc->state != IMAP_UPGRADETLS)
479 state(conn, IMAP_UPGRADETLS);
483 result = imap_perform_capability(conn);
490 /***********************************************************************
492 * imap_perform_login()
494 * Sends a clear text LOGIN command to authenticate with.
496 static CURLcode imap_perform_login(struct connectdata *conn)
498 CURLcode result = CURLE_OK;
502 /* Check we have a username and password to authenticate with and end the
503 connect phase if we don't */
504 if(!conn->bits.user_passwd) {
505 state(conn, IMAP_STOP);
510 /* Make sure the username and password are in the correct atom format */
511 user = imap_atom(conn->user);
512 passwd = imap_atom(conn->passwd);
514 /* Send the LOGIN command */
515 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
516 passwd ? passwd : "");
519 Curl_safefree(passwd);
522 state(conn, IMAP_LOGIN);
527 /***********************************************************************
529 * imap_perform_authenticate()
531 * Sends an AUTHENTICATE command allowing the client to login with the
532 * appropriate SASL authentication mechanism.
534 * Additionally, the function will perform fallback to the LOGIN command
535 * should a common mechanism not be available between the client and server.
537 static CURLcode imap_perform_authenticate(struct connectdata *conn)
539 CURLcode result = CURLE_OK;
540 struct SessionHandle *data = conn->data;
541 struct imap_conn *imapc = &conn->proto.imapc;
542 const char *mech = NULL;
543 char *initresp = NULL;
545 imapstate state1 = IMAP_STOP;
546 imapstate state2 = IMAP_STOP;
548 /* Check we have a username and password to authenticate with and end the
549 connect phase if we don't */
550 if(!conn->bits.user_passwd) {
551 state(conn, IMAP_STOP);
556 /* Calculate the supported authentication mechanism by decreasing order of
558 #ifndef CURL_DISABLE_CRYPTO_AUTH
559 if((imapc->authmechs & SASL_MECH_DIGEST_MD5) &&
560 (imapc->prefmech & SASL_MECH_DIGEST_MD5)) {
561 mech = SASL_MECH_STRING_DIGEST_MD5;
562 state1 = IMAP_AUTHENTICATE_DIGESTMD5;
563 imapc->authused = SASL_MECH_DIGEST_MD5;
565 else if((imapc->authmechs & SASL_MECH_CRAM_MD5) &&
566 (imapc->prefmech & SASL_MECH_CRAM_MD5)) {
567 mech = SASL_MECH_STRING_CRAM_MD5;
568 state1 = IMAP_AUTHENTICATE_CRAMMD5;
569 imapc->authused = SASL_MECH_CRAM_MD5;
574 if((imapc->authmechs & SASL_MECH_NTLM) &&
575 (imapc->prefmech & SASL_MECH_NTLM)) {
576 mech = SASL_MECH_STRING_NTLM;
577 state1 = IMAP_AUTHENTICATE_NTLM;
578 state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
579 imapc->authused = SASL_MECH_NTLM;
581 if(imapc->ir_supported || data->set.sasl_ir)
582 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
588 if(((imapc->authmechs & SASL_MECH_XOAUTH2) &&
589 (imapc->prefmech & SASL_MECH_XOAUTH2) &&
590 (imapc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
591 mech = SASL_MECH_STRING_XOAUTH2;
592 state1 = IMAP_AUTHENTICATE_XOAUTH2;
593 state2 = IMAP_AUTHENTICATE_FINAL;
594 imapc->authused = SASL_MECH_XOAUTH2;
596 if(imapc->ir_supported || data->set.sasl_ir)
597 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
598 conn->xoauth2_bearer,
601 else if((imapc->authmechs & SASL_MECH_LOGIN) &&
602 (imapc->prefmech & SASL_MECH_LOGIN)) {
603 mech = SASL_MECH_STRING_LOGIN;
604 state1 = IMAP_AUTHENTICATE_LOGIN;
605 state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
606 imapc->authused = SASL_MECH_LOGIN;
608 if(imapc->ir_supported || data->set.sasl_ir)
609 result = Curl_sasl_create_login_message(conn->data, conn->user,
612 else if((imapc->authmechs & SASL_MECH_PLAIN) &&
613 (imapc->prefmech & SASL_MECH_PLAIN)) {
614 mech = SASL_MECH_STRING_PLAIN;
615 state1 = IMAP_AUTHENTICATE_PLAIN;
616 state2 = IMAP_AUTHENTICATE_FINAL;
617 imapc->authused = SASL_MECH_PLAIN;
619 if(imapc->ir_supported || data->set.sasl_ir)
620 result = Curl_sasl_create_plain_message(conn->data, conn->user,
621 conn->passwd, &initresp, &len);
626 /* Perform SASL based authentication */
628 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
634 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
640 Curl_safefree(initresp);
642 else if(!imapc->login_disabled)
643 /* Perform clear text authentication */
644 result = imap_perform_login(conn);
646 /* Other mechanisms not supported */
647 infof(conn->data, "No known authentication mechanisms supported!\n");
648 result = CURLE_LOGIN_DENIED;
655 /***********************************************************************
657 * imap_perform_list()
659 * Sends a LIST command or an alternative custom request.
661 static CURLcode imap_perform_list(struct connectdata *conn)
663 CURLcode result = CURLE_OK;
664 struct SessionHandle *data = conn->data;
665 struct IMAP *imap = data->req.protop;
669 /* Send the custom request */
670 result = imap_sendf(conn, "%s%s", imap->custom,
671 imap->custom_params ? imap->custom_params : "");
673 /* Make sure the mailbox is in the correct atom format */
674 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
676 return CURLE_OUT_OF_MEMORY;
678 /* Send the LIST command */
679 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
681 Curl_safefree(mailbox);
685 state(conn, IMAP_LIST);
690 /***********************************************************************
692 * imap_perform_select()
694 * Sends a SELECT command to ask the server to change the selected mailbox.
696 static CURLcode imap_perform_select(struct connectdata *conn)
698 CURLcode result = CURLE_OK;
699 struct SessionHandle *data = conn->data;
700 struct IMAP *imap = data->req.protop;
701 struct imap_conn *imapc = &conn->proto.imapc;
704 /* Invalidate old information as we are switching mailboxes */
705 Curl_safefree(imapc->mailbox);
706 Curl_safefree(imapc->mailbox_uidvalidity);
708 /* Check we have a mailbox */
710 failf(conn->data, "Cannot SELECT without a mailbox.");
711 return CURLE_URL_MALFORMAT;
714 /* Make sure the mailbox is in the correct atom format */
715 mailbox = imap_atom(imap->mailbox);
717 return CURLE_OUT_OF_MEMORY;
719 /* Send the SELECT command */
720 result = imap_sendf(conn, "SELECT %s", mailbox);
722 Curl_safefree(mailbox);
725 state(conn, IMAP_SELECT);
730 /***********************************************************************
732 * imap_perform_fetch()
734 * Sends a FETCH command to initiate the download of a message.
736 static CURLcode imap_perform_fetch(struct connectdata *conn)
738 CURLcode result = CURLE_OK;
739 struct IMAP *imap = conn->data->req.protop;
741 /* Check we have a UID */
743 failf(conn->data, "Cannot FETCH without a UID.");
744 return CURLE_URL_MALFORMAT;
747 /* Send the FETCH command */
748 result = imap_sendf(conn, "FETCH %s BODY[%s]",
750 imap->section ? imap->section : "");
753 state(conn, IMAP_FETCH);
758 /***********************************************************************
760 * imap_perform_append()
762 * Sends an APPEND command to initiate the upload of a message.
764 static CURLcode imap_perform_append(struct connectdata *conn)
766 CURLcode result = CURLE_OK;
767 struct IMAP *imap = conn->data->req.protop;
770 /* Check we have a mailbox */
772 failf(conn->data, "Cannot APPEND without a mailbox.");
773 return CURLE_URL_MALFORMAT;
776 /* Check we know the size of the upload */
777 if(conn->data->set.infilesize < 0) {
778 failf(conn->data, "Cannot APPEND with unknown input file size\n");
779 return CURLE_UPLOAD_FAILED;
782 /* Make sure the mailbox is in the correct atom format */
783 mailbox = imap_atom(imap->mailbox);
785 return CURLE_OUT_OF_MEMORY;
787 /* Send the APPEND command */
788 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
789 mailbox, conn->data->set.infilesize);
791 Curl_safefree(mailbox);
794 state(conn, IMAP_APPEND);
799 /***********************************************************************
801 * imap_perform_logout()
803 * Performs the logout action prior to sclose() being called.
805 static CURLcode imap_perform_logout(struct connectdata *conn)
807 CURLcode result = CURLE_OK;
809 /* Send the LOGOUT command */
810 result = imap_sendf(conn, "LOGOUT");
813 state(conn, IMAP_LOGOUT);
818 /* For the initial server greeting */
819 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
823 CURLcode result = CURLE_OK;
824 struct SessionHandle *data = conn->data;
826 (void)instate; /* no use for this yet */
828 if(imapcode != 'O') {
829 failf(data, "Got unexpected imap-server response");
830 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
833 result = imap_perform_capability(conn);
838 /* For CAPABILITY responses */
839 static CURLcode imap_state_capability_resp(struct connectdata *conn,
843 CURLcode result = CURLE_OK;
844 struct SessionHandle *data = conn->data;
845 struct imap_conn *imapc = &conn->proto.imapc;
846 const char *line = data->state.buffer;
849 (void)instate; /* no use for this yet */
851 /* Do we have a untagged response? */
852 if(imapcode == '*') {
855 /* Loop through the data line */
858 (*line == ' ' || *line == '\t' ||
859 *line == '\r' || *line == '\n')) {
867 /* Extract the word */
868 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
869 line[wordlen] != '\t' && line[wordlen] != '\r' &&
870 line[wordlen] != '\n';)
873 /* Does the server support the STARTTLS capability? */
874 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
875 imapc->tls_supported = TRUE;
877 /* Has the server explicitly disabled clear text authentication? */
878 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
879 imapc->login_disabled = TRUE;
881 /* Does the server support the SASL-IR capability? */
882 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
883 imapc->ir_supported = TRUE;
885 /* Do we have a SASL based authentication mechanism? */
886 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
890 /* Test the word for a matching authentication mechanism */
891 if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
892 imapc->authmechs |= SASL_MECH_LOGIN;
893 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
894 imapc->authmechs |= SASL_MECH_PLAIN;
895 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
896 imapc->authmechs |= SASL_MECH_CRAM_MD5;
897 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
898 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
899 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
900 imapc->authmechs |= SASL_MECH_GSSAPI;
901 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
902 imapc->authmechs |= SASL_MECH_EXTERNAL;
903 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
904 imapc->authmechs |= SASL_MECH_NTLM;
905 else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
906 imapc->authmechs |= SASL_MECH_XOAUTH2;
912 else if(imapcode == 'O') {
913 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
914 /* We don't have a SSL/TLS connection yet, but SSL is requested */
915 if(imapc->tls_supported)
916 /* Switch to TLS connection now */
917 result = imap_perform_starttls(conn);
918 else if(data->set.use_ssl == CURLUSESSL_TRY)
919 /* Fallback and carry on with authentication */
920 result = imap_perform_authenticate(conn);
922 failf(data, "STARTTLS not supported.");
923 result = CURLE_USE_SSL_FAILED;
927 result = imap_perform_authenticate(conn);
930 result = imap_perform_login(conn);
935 /* For STARTTLS responses */
936 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
940 CURLcode result = CURLE_OK;
941 struct SessionHandle *data = conn->data;
943 (void)instate; /* no use for this yet */
945 if(imapcode != 'O') {
946 if(data->set.use_ssl != CURLUSESSL_TRY) {
947 failf(data, "STARTTLS denied. %c", imapcode);
948 result = CURLE_USE_SSL_FAILED;
951 result = imap_perform_authenticate(conn);
954 result = imap_perform_upgrade_tls(conn);
959 /* For AUTHENTICATE PLAIN (without initial response) responses */
960 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
964 CURLcode result = CURLE_OK;
965 struct SessionHandle *data = conn->data;
967 char *plainauth = NULL;
969 (void)instate; /* no use for this yet */
971 if(imapcode != '+') {
972 failf(data, "Access denied. %c", imapcode);
973 result = CURLE_LOGIN_DENIED;
976 /* Create the authorisation message */
977 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
980 /* Send the message */
983 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
986 state(conn, IMAP_AUTHENTICATE_FINAL);
989 Curl_safefree(plainauth);
996 /* For AUTHENTICATE LOGIN (without initial response) responses */
997 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1001 CURLcode result = CURLE_OK;
1002 struct SessionHandle *data = conn->data;
1004 char *authuser = NULL;
1006 (void)instate; /* no use for this yet */
1008 if(imapcode != '+') {
1009 failf(data, "Access denied: %d", imapcode);
1010 result = CURLE_LOGIN_DENIED;
1013 /* Create the user message */
1014 result = Curl_sasl_create_login_message(data, conn->user,
1020 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1023 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1026 Curl_safefree(authuser);
1033 /* For AUTHENTICATE LOGIN user entry responses */
1034 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1038 CURLcode result = CURLE_OK;
1039 struct SessionHandle *data = conn->data;
1041 char *authpasswd = NULL;
1043 (void)instate; /* no use for this yet */
1045 if(imapcode != '+') {
1046 failf(data, "Access denied: %d", imapcode);
1047 result = CURLE_LOGIN_DENIED;
1050 /* Create the password message */
1051 result = Curl_sasl_create_login_message(data, conn->passwd,
1054 /* Send the password */
1057 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1060 state(conn, IMAP_AUTHENTICATE_FINAL);
1063 Curl_safefree(authpasswd);
1070 #ifndef CURL_DISABLE_CRYPTO_AUTH
1071 /* For AUTHENTICATE CRAM-MD5 responses */
1072 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1076 CURLcode result = CURLE_OK;
1077 struct SessionHandle *data = conn->data;
1078 char *chlg64 = data->state.buffer;
1080 char *rplyb64 = NULL;
1082 (void)instate; /* no use for this yet */
1084 if(imapcode != '+') {
1085 failf(data, "Access denied: %d", imapcode);
1086 return CURLE_LOGIN_DENIED;
1089 /* Get the challenge */
1090 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1093 /* Terminate the challenge */
1094 if(*chlg64 != '=') {
1095 for(len = strlen(chlg64); len--;)
1096 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
1097 chlg64[len] != '\t')
1105 /* Create the response message */
1106 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
1107 conn->passwd, &rplyb64, &len);
1109 /* Send the response */
1112 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1115 state(conn, IMAP_AUTHENTICATE_FINAL);
1118 Curl_safefree(rplyb64);
1124 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1125 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1129 CURLcode result = CURLE_OK;
1130 struct SessionHandle *data = conn->data;
1131 char *chlg64 = data->state.buffer;
1133 char *rplyb64 = NULL;
1135 (void)instate; /* no use for this yet */
1137 if(imapcode != '+') {
1138 failf(data, "Access denied: %d", imapcode);
1139 return CURLE_LOGIN_DENIED;
1142 /* Get the challenge */
1143 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1146 /* Create the response message */
1147 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
1148 conn->passwd, "imap",
1151 /* Send the response */
1154 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1157 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1160 Curl_safefree(rplyb64);
1166 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1167 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1171 CURLcode result = CURLE_OK;
1172 struct SessionHandle *data = conn->data;
1174 (void)instate; /* no use for this yet */
1176 if(imapcode != '+') {
1177 failf(data, "Authentication failed: %d", imapcode);
1178 result = CURLE_LOGIN_DENIED;
1181 /* Send an empty response */
1182 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1185 state(conn, IMAP_AUTHENTICATE_FINAL);
1193 /* For AUTHENTICATE NTLM (without initial response) responses */
1194 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1198 CURLcode result = CURLE_OK;
1199 struct SessionHandle *data = conn->data;
1201 char *type1msg = NULL;
1203 (void)instate; /* no use for this yet */
1205 if(imapcode != '+') {
1206 failf(data, "Access denied: %d", imapcode);
1207 result = CURLE_LOGIN_DENIED;
1210 /* Create the type-1 message */
1211 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1215 /* Send the message */
1218 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1221 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1224 Curl_safefree(type1msg);
1231 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1232 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1236 CURLcode result = CURLE_OK;
1237 struct SessionHandle *data = conn->data;
1239 char *type3msg = NULL;
1241 (void)instate; /* no use for this yet */
1243 if(imapcode != '+') {
1244 failf(data, "Access denied: %d", imapcode);
1245 result = CURLE_LOGIN_DENIED;
1248 /* Create the type-3 message */
1249 result = Curl_sasl_create_ntlm_type3_message(data,
1250 data->state.buffer + 2,
1251 conn->user, conn->passwd,
1255 /* Send the message */
1258 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1261 state(conn, IMAP_AUTHENTICATE_FINAL);
1264 Curl_safefree(type3msg);
1272 /* For AUTH XOAUTH2 (without initial response) responses */
1273 static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn,
1277 CURLcode result = CURLE_OK;
1278 struct SessionHandle *data = conn->data;
1280 char *xoauth = NULL;
1282 (void)instate; /* no use for this yet */
1284 if(imapcode != '+') {
1285 failf(data, "Access denied: %d", imapcode);
1286 result = CURLE_LOGIN_DENIED;
1289 /* Create the authorisation message */
1290 result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1291 conn->xoauth2_bearer,
1294 /* Send the message */
1297 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth);
1300 state(conn, IMAP_AUTHENTICATE_FINAL);
1303 Curl_safefree(xoauth);
1310 /* For final responses to the AUTHENTICATE sequence */
1311 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1315 CURLcode result = CURLE_OK;
1316 struct SessionHandle *data = conn->data;
1318 (void)instate; /* no use for this yet */
1320 if(imapcode != 'O') {
1321 failf(data, "Authentication failed: %d", imapcode);
1322 result = CURLE_LOGIN_DENIED;
1325 /* End of connect phase */
1326 state(conn, IMAP_STOP);
1331 /* For LOGIN responses */
1332 static CURLcode imap_state_login_resp(struct connectdata *conn,
1336 CURLcode result = CURLE_OK;
1337 struct SessionHandle *data = conn->data;
1339 (void)instate; /* no use for this yet */
1341 if(imapcode != 'O') {
1342 failf(data, "Access denied. %c", imapcode);
1343 result = CURLE_LOGIN_DENIED;
1346 /* End of connect phase */
1347 state(conn, IMAP_STOP);
1352 /* For LIST responses */
1353 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1356 CURLcode result = CURLE_OK;
1357 char *line = conn->data->state.buffer;
1358 size_t len = strlen(line);
1360 (void)instate; /* No use for this yet */
1362 if(imapcode == '*') {
1363 /* Temporarily add the LF character back and send as body to the client */
1365 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1368 else if(imapcode != 'O')
1369 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1371 /* End of DO phase */
1372 state(conn, IMAP_STOP);
1377 /* For SELECT responses */
1378 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1381 CURLcode result = CURLE_OK;
1382 struct SessionHandle *data = conn->data;
1383 struct IMAP *imap = conn->data->req.protop;
1384 struct imap_conn *imapc = &conn->proto.imapc;
1385 const char *line = data->state.buffer;
1388 (void)instate; /* no use for this yet */
1390 if(imapcode == '*') {
1391 /* See if this is an UIDVALIDITY response */
1392 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1393 Curl_safefree(imapc->mailbox_uidvalidity);
1394 imapc->mailbox_uidvalidity = strdup(tmp);
1397 else if(imapcode == 'O') {
1398 /* Check if the UIDVALIDITY has been specified and matches */
1399 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1400 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1401 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1402 result = CURLE_REMOTE_FILE_NOT_FOUND;
1405 /* Note the currently opened mailbox on this connection */
1406 imapc->mailbox = strdup(imap->mailbox);
1409 result = imap_perform_list(conn);
1411 result = imap_perform_fetch(conn);
1415 failf(data, "Select failed");
1416 result = CURLE_LOGIN_DENIED;
1422 /* For the (first line of the) FETCH responses */
1423 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1426 CURLcode result = CURLE_OK;
1427 struct SessionHandle *data = conn->data;
1428 struct imap_conn *imapc = &conn->proto.imapc;
1429 struct pingpong *pp = &imapc->pp;
1430 const char *ptr = data->state.buffer;
1431 bool parsed = FALSE;
1434 (void)instate; /* no use for this yet */
1436 if(imapcode != '*') {
1437 Curl_pgrsSetDownloadSize(data, 0);
1438 state(conn, IMAP_STOP);
1439 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1442 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1443 the continuation data contained within the curly brackets */
1444 while(*ptr && (*ptr != '{'))
1449 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1450 if(endptr - ptr > 1 && endptr[0] == '}' &&
1451 endptr[1] == '\r' && endptr[2] == '\0')
1456 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1457 Curl_pgrsSetDownloadSize(data, size);
1460 /* At this point there is a bunch of data in the header "cache" that is
1461 actually body content, send it as body and then skip it. Do note
1462 that there may even be additional "headers" after the body. */
1463 size_t chunk = pp->cache_size;
1465 if(chunk > (size_t)size)
1466 /* The conversion from curl_off_t to size_t is always fine here */
1467 chunk = (size_t)size;
1469 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1473 data->req.bytecount += chunk;
1475 infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1476 " bytes are left for transfer\n", (curl_off_t)chunk,
1479 /* Have we used the entire cache or just part of it?*/
1480 if(pp->cache_size > chunk) {
1481 /* Only part of it so shrink the cache to fit the trailing data */
1482 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1483 pp->cache_size -= chunk;
1486 /* Free the cache */
1487 Curl_safefree(pp->cache);
1489 /* Reset the cache size */
1494 if(data->req.bytecount == size)
1495 /* The entire data is already transferred! */
1496 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1499 data->req.maxdownload = size;
1500 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1504 /* We don't know how to parse this line */
1505 failf(pp->conn->data, "Failed to parse FETCH response.");
1506 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1509 /* End of DO phase */
1510 state(conn, IMAP_STOP);
1515 /* For final FETCH responses performed after the download */
1516 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1520 CURLcode result = CURLE_OK;
1522 (void)instate; /* No use for this yet */
1525 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1527 /* End of DONE phase */
1528 state(conn, IMAP_STOP);
1533 /* For APPEND responses */
1534 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1537 CURLcode result = CURLE_OK;
1538 struct SessionHandle *data = conn->data;
1540 (void)instate; /* No use for this yet */
1542 if(imapcode != '+') {
1543 result = CURLE_UPLOAD_FAILED;
1546 /* Set the progress upload size */
1547 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1550 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1552 /* End of DO phase */
1553 state(conn, IMAP_STOP);
1559 /* For final APPEND responses performed after the upload */
1560 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1564 CURLcode result = CURLE_OK;
1566 (void)instate; /* No use for this yet */
1569 result = CURLE_UPLOAD_FAILED;
1571 /* End of DONE phase */
1572 state(conn, IMAP_STOP);
1577 static CURLcode imap_statemach_act(struct connectdata *conn)
1579 CURLcode result = CURLE_OK;
1580 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1582 struct imap_conn *imapc = &conn->proto.imapc;
1583 struct pingpong *pp = &imapc->pp;
1586 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1587 if(imapc->state == IMAP_UPGRADETLS)
1588 return imap_perform_upgrade_tls(conn);
1590 /* Flush any data that needs to be sent */
1592 return Curl_pp_flushsend(pp);
1595 /* Read the response from the server */
1596 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1600 /* Was there an error parsing the response line? */
1602 return CURLE_FTP_WEIRD_SERVER_REPLY;
1607 /* We have now received a full IMAP server response */
1608 switch(imapc->state) {
1609 case IMAP_SERVERGREET:
1610 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1613 case IMAP_CAPABILITY:
1614 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1618 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1621 case IMAP_AUTHENTICATE_PLAIN:
1622 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1625 case IMAP_AUTHENTICATE_LOGIN:
1626 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1629 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1630 result = imap_state_auth_login_password_resp(conn, imapcode,
1634 #ifndef CURL_DISABLE_CRYPTO_AUTH
1635 case IMAP_AUTHENTICATE_CRAMMD5:
1636 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1639 case IMAP_AUTHENTICATE_DIGESTMD5:
1640 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1643 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1644 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1649 case IMAP_AUTHENTICATE_NTLM:
1650 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1653 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1654 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1659 case IMAP_AUTHENTICATE_XOAUTH2:
1660 result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state);
1663 case IMAP_AUTHENTICATE_FINAL:
1664 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1668 result = imap_state_login_resp(conn, imapcode, imapc->state);
1672 result = imap_state_list_resp(conn, imapcode, imapc->state);
1676 result = imap_state_select_resp(conn, imapcode, imapc->state);
1680 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1683 case IMAP_FETCH_FINAL:
1684 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1688 result = imap_state_append_resp(conn, imapcode, imapc->state);
1691 case IMAP_APPEND_FINAL:
1692 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1696 /* fallthrough, just stop! */
1698 /* internal error */
1699 state(conn, IMAP_STOP);
1702 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1707 /* Called repeatedly until done from multi.c */
1708 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1710 CURLcode result = CURLE_OK;
1711 struct imap_conn *imapc = &conn->proto.imapc;
1713 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1714 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1715 if(result || !imapc->ssldone)
1719 result = Curl_pp_statemach(&imapc->pp, FALSE);
1720 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1725 static CURLcode imap_block_statemach(struct connectdata *conn)
1727 CURLcode result = CURLE_OK;
1728 struct imap_conn *imapc = &conn->proto.imapc;
1730 while(imapc->state != IMAP_STOP && !result)
1731 result = Curl_pp_statemach(&imapc->pp, TRUE);
1736 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1738 static CURLcode imap_init(struct connectdata *conn)
1740 CURLcode result = CURLE_OK;
1741 struct SessionHandle *data = conn->data;
1744 imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1746 result = CURLE_OUT_OF_MEMORY;
1751 /* For the IMAP "protocol connect" and "doing" phases only */
1752 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1755 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1758 /***********************************************************************
1762 * This function should do everything that is to be considered a part of the
1765 * The variable 'done' points to will be TRUE if the protocol-layer connect
1766 * phase is done when this function returns, or FALSE if not.
1768 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1770 CURLcode result = CURLE_OK;
1771 struct imap_conn *imapc = &conn->proto.imapc;
1772 struct pingpong *pp = &imapc->pp;
1774 *done = FALSE; /* default to not done yet */
1776 /* We always support persistent connections in IMAP */
1777 conn->bits.close = FALSE;
1779 /* Set the default response time-out */
1780 pp->response_time = RESP_TIMEOUT;
1781 pp->statemach_act = imap_statemach_act;
1782 pp->endofresp = imap_endofresp;
1785 /* Set the default preferred authentication mechanism */
1786 imapc->prefmech = SASL_AUTH_ANY;
1788 /* Initialise the pingpong layer */
1791 /* Parse the URL options */
1792 result = imap_parse_url_options(conn);
1796 /* Start off waiting for the server greeting response */
1797 state(conn, IMAP_SERVERGREET);
1799 /* Start off with an response id of '*' */
1800 strcpy(imapc->resptag, "*");
1802 result = imap_multi_statemach(conn, done);
1807 /***********************************************************************
1811 * The DONE function. This does what needs to be done after a single DO has
1814 * Input argument is already checked for validity.
1816 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1819 CURLcode result = CURLE_OK;
1820 struct SessionHandle *data = conn->data;
1821 struct IMAP *imap = data->req.protop;
1826 /* When the easy handle is removed from the multi interface while libcurl
1827 is still trying to resolve the host name, the IMAP struct is not yet
1828 initialized. However, the removal action calls Curl_done() which in
1829 turn calls this function, so we simply return success. */
1833 conn->bits.close = TRUE; /* marked for closure */
1834 result = status; /* use the already set error code */
1836 else if(!data->set.connect_only && !imap->custom &&
1837 (imap->uid || data->set.upload)) {
1838 /* Handle responses after FETCH or APPEND transfer has finished */
1839 if(!data->set.upload)
1840 state(conn, IMAP_FETCH_FINAL);
1842 /* End the APPEND command first by sending an empty line */
1843 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1845 state(conn, IMAP_APPEND_FINAL);
1848 /* Run the state-machine
1850 TODO: when the multi interface is used, this _really_ should be using
1851 the imap_multi_statemach function but we have no general support for
1852 non-blocking DONE operations, not in the multi state machine and with
1853 Curl_done() invokes on several places in the code!
1856 result = imap_block_statemach(conn);
1859 /* Cleanup our per-request based variables */
1860 Curl_safefree(imap->mailbox);
1861 Curl_safefree(imap->uidvalidity);
1862 Curl_safefree(imap->uid);
1863 Curl_safefree(imap->section);
1864 Curl_safefree(imap->custom);
1865 Curl_safefree(imap->custom_params);
1867 /* Clear the transfer mode for the next request */
1868 imap->transfer = FTPTRANSFER_BODY;
1873 /***********************************************************************
1877 * This is the actual DO function for IMAP. Fetch or append a message, or do
1878 * other things according to the options previously setup.
1880 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1883 /* This is IMAP and no proxy */
1884 CURLcode result = CURLE_OK;
1885 struct SessionHandle *data = conn->data;
1886 struct IMAP *imap = data->req.protop;
1887 struct imap_conn *imapc = &conn->proto.imapc;
1888 bool selected = FALSE;
1890 DEBUGF(infof(conn->data, "DO phase starts\n"));
1892 if(conn->data->set.opt_no_body) {
1893 /* Requested no body means no transfer */
1894 imap->transfer = FTPTRANSFER_INFO;
1897 *dophase_done = FALSE; /* not done yet */
1899 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1900 has already been selected on this connection */
1901 if(imap->mailbox && imapc->mailbox &&
1902 !strcmp(imap->mailbox, imapc->mailbox) &&
1903 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1904 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1907 /* Start the first command in the DO phase */
1908 if(conn->data->set.upload)
1909 /* APPEND can be executed directly */
1910 result = imap_perform_append(conn);
1911 else if(imap->custom && (selected || !imap->mailbox))
1912 /* Custom command using the same mailbox or no mailbox */
1913 result = imap_perform_list(conn);
1914 else if(!imap->custom && selected && imap->uid)
1915 /* FETCH from the same mailbox */
1916 result = imap_perform_fetch(conn);
1917 else if(imap->mailbox && !selected && (imap->custom || imap->uid))
1918 /* SELECT the mailbox */
1919 result = imap_perform_select(conn);
1922 result = imap_perform_list(conn);
1927 /* Run the state-machine */
1928 result = imap_multi_statemach(conn, dophase_done);
1930 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1933 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1938 /***********************************************************************
1942 * This function is registered as 'curl_do' function. It decodes the path
1943 * parts etc as a wrapper to the actual DO function (imap_perform).
1945 * The input argument is already checked for validity.
1947 static CURLcode imap_do(struct connectdata *conn, bool *done)
1949 CURLcode result = CURLE_OK;
1951 *done = FALSE; /* default to false */
1953 /* Parse the URL path */
1954 result = imap_parse_url_path(conn);
1958 /* Parse the custom request */
1959 result = imap_parse_custom_request(conn);
1963 result = imap_regular_transfer(conn, done);
1968 /***********************************************************************
1972 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1973 * resources. BLOCKING.
1975 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1977 struct imap_conn *imapc = &conn->proto.imapc;
1979 /* We cannot send quit unconditionally. If this connection is stale or
1980 bad in any way, sending quit and waiting around here will make the
1981 disconnect wait in vain and cause more problems than we need to. */
1983 /* The IMAP session may or may not have been allocated/setup at this
1985 if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
1986 if(!imap_perform_logout(conn))
1987 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1989 /* Disconnect from the server */
1990 Curl_pp_disconnect(&imapc->pp);
1992 /* Cleanup the SASL module */
1993 Curl_sasl_cleanup(conn, imapc->authused);
1995 /* Cleanup our connection based variables */
1996 Curl_safefree(imapc->mailbox);
1997 Curl_safefree(imapc->mailbox_uidvalidity);
2002 /* Call this when the DO phase has completed */
2003 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2005 struct IMAP *imap = conn->data->req.protop;
2009 if(imap->transfer != FTPTRANSFER_BODY)
2010 /* no data to transfer */
2011 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2016 /* Called from multi.c while DOing */
2017 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2019 CURLcode result = imap_multi_statemach(conn, dophase_done);
2022 DEBUGF(infof(conn->data, "DO phase failed\n"));
2023 else if(*dophase_done) {
2024 result = imap_dophase_done(conn, FALSE /* not connected */);
2026 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2032 /***********************************************************************
2034 * imap_regular_transfer()
2036 * The input argument is already checked for validity.
2038 * Performs all commands done before a regular transfer between a local and a
2041 static CURLcode imap_regular_transfer(struct connectdata *conn,
2044 CURLcode result = CURLE_OK;
2045 bool connected = FALSE;
2046 struct SessionHandle *data = conn->data;
2048 /* Make sure size is unknown at this point */
2049 data->req.size = -1;
2051 /* Set the progress data */
2052 Curl_pgrsSetUploadCounter(data, 0);
2053 Curl_pgrsSetDownloadCounter(data, 0);
2054 Curl_pgrsSetUploadSize(data, 0);
2055 Curl_pgrsSetDownloadSize(data, 0);
2057 /* Carry out the perform */
2058 result = imap_perform(conn, &connected, dophase_done);
2060 /* Perform post DO phase operations if necessary */
2061 if(!result && *dophase_done)
2062 result = imap_dophase_done(conn, connected);
2067 static CURLcode imap_setup_connection(struct connectdata *conn)
2069 struct SessionHandle *data = conn->data;
2071 /* Initialise the IMAP layer */
2072 CURLcode result = imap_init(conn);
2076 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2077 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2078 switch and use HTTP operations only */
2079 #ifndef CURL_DISABLE_HTTP
2080 if(conn->handler == &Curl_handler_imap)
2081 conn->handler = &Curl_handler_imap_proxy;
2084 conn->handler = &Curl_handler_imaps_proxy;
2086 failf(data, "IMAPS not supported!");
2087 return CURLE_UNSUPPORTED_PROTOCOL;
2091 /* set it up as an HTTP connection instead */
2092 return conn->handler->setup_connection(conn);
2094 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2095 return CURLE_UNSUPPORTED_PROTOCOL;
2099 data->state.path++; /* don't include the initial slash */
2104 /***********************************************************************
2108 * Sends the formated string as an IMAP command to the server.
2110 * Designed to never block.
2112 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
2114 CURLcode result = CURLE_OK;
2115 struct imap_conn *imapc = &conn->proto.imapc;
2121 /* Calculate the next command ID wrapping at 3 digits */
2122 imapc->cmdid = (imapc->cmdid + 1) % 1000;
2124 /* Calculate the tag based on the connection ID and command ID */
2125 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
2126 'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
2128 /* Prefix the format with the tag */
2129 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
2131 return CURLE_OUT_OF_MEMORY;
2133 /* Send the data with the tag */
2135 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
2138 Curl_safefree(taggedfmt);
2143 /***********************************************************************
2147 * Checks the input string for characters that need escaping and returns an
2148 * atom ready for sending to the server.
2150 * The returned string needs to be freed.
2153 static char *imap_atom(const char *str)
2157 size_t backsp_count = 0;
2158 size_t quote_count = 0;
2159 bool space_exists = FALSE;
2161 char *newstr = NULL;
2166 /* Count any unescapped characters */
2174 space_exists = TRUE;
2179 /* Does the input contain any unescapped characters? */
2180 if(!backsp_count && !quote_count && !space_exists)
2183 /* Calculate the new string length */
2184 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
2186 /* Allocate the new string */
2187 newstr = (char *) malloc((newlen + 1) * sizeof(char));
2191 /* Surround the string in quotes if necessary */
2195 newstr[newlen - 1] = '"';
2199 /* Copy the string, escaping backslash and quote characters along the way */
2202 if(*p1 == '\\' || *p1 == '"') {
2213 /* Terminate the string */
2214 newstr[newlen] = '\0';
2219 /***********************************************************************
2223 * Portable test of whether the specified char is a "bchar" as defined in the
2224 * grammar of RFC-5092.
2226 static bool imap_is_bchar(char ch)
2230 case ':': case '@': case '/':
2231 /* bchar -> achar */
2233 /* bchar -> achar -> uchar -> unreserved */
2234 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2235 case '7': case '8': case '9':
2236 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2237 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2238 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2239 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2240 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2241 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2242 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2243 case 'v': case 'w': case 'x': case 'y': case 'z':
2244 case '-': case '.': case '_': case '~':
2245 /* bchar -> achar -> uchar -> sub-delims-sh */
2246 case '!': case '$': case '\'': case '(': case ')': case '*':
2248 /* bchar -> achar -> uchar -> pct-encoded */
2249 case '%': /* HEXDIG chars are already included above */
2257 /***********************************************************************
2259 * imap_parse_url_options()
2261 * Parse the URL login options.
2263 static CURLcode imap_parse_url_options(struct connectdata *conn)
2265 CURLcode result = CURLE_OK;
2266 struct imap_conn *imapc = &conn->proto.imapc;
2267 const char *options = conn->options;
2268 const char *ptr = options;
2271 const char *key = ptr;
2273 while(*ptr && *ptr != '=')
2276 if(strnequal(key, "AUTH", 4)) {
2277 const char *value = ptr + 1;
2279 if(strequal(value, "*"))
2280 imapc->prefmech = SASL_AUTH_ANY;
2281 else if(strequal(value, SASL_MECH_STRING_LOGIN))
2282 imapc->prefmech = SASL_MECH_LOGIN;
2283 else if(strequal(value, SASL_MECH_STRING_PLAIN))
2284 imapc->prefmech = SASL_MECH_PLAIN;
2285 else if(strequal(value, SASL_MECH_STRING_CRAM_MD5))
2286 imapc->prefmech = SASL_MECH_CRAM_MD5;
2287 else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5))
2288 imapc->prefmech = SASL_MECH_DIGEST_MD5;
2289 else if(strequal(value, SASL_MECH_STRING_GSSAPI))
2290 imapc->prefmech = SASL_MECH_GSSAPI;
2291 else if(strequal(value, SASL_MECH_STRING_NTLM))
2292 imapc->prefmech = SASL_MECH_NTLM;
2293 else if(strequal(value, SASL_MECH_STRING_XOAUTH2))
2294 imapc->prefmech = SASL_MECH_XOAUTH2;
2296 imapc->prefmech = SASL_AUTH_NONE;
2299 result = CURLE_URL_MALFORMAT;
2305 /***********************************************************************
2307 * imap_parse_url_path()
2309 * Parse the URL path into separate path components.
2312 static CURLcode imap_parse_url_path(struct connectdata *conn)
2314 /* The imap struct is already initialised in imap_connect() */
2315 CURLcode result = CURLE_OK;
2316 struct SessionHandle *data = conn->data;
2317 struct IMAP *imap = data->req.protop;
2318 const char *begin = data->state.path;
2319 const char *ptr = begin;
2321 /* See how much of the URL is a valid path and decode it */
2322 while(imap_is_bchar(*ptr))
2326 /* Remove the trailing slash if present */
2327 const char *end = ptr;
2328 if(end > begin && end[-1] == '/')
2331 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2337 imap->mailbox = NULL;
2339 /* There can be any number of parameters in the form ";NAME=VALUE" */
2340 while(*ptr == ';') {
2345 /* Find the length of the name parameter */
2347 while(*ptr && *ptr != '=')
2351 return CURLE_URL_MALFORMAT;
2353 /* Decode the name parameter */
2354 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2358 /* Find the length of the value parameter */
2360 while(imap_is_bchar(*ptr))
2363 /* Decode the value parameter */
2364 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2366 Curl_safefree(name);
2370 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2372 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2373 stripping of the trailing slash character if it is present.
2375 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2376 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2377 if(valuelen > 0 && value[valuelen - 1] == '/')
2378 value[valuelen - 1] = '\0';
2380 imap->uidvalidity = value;
2383 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2384 if(valuelen > 0 && value[valuelen - 1] == '/')
2385 value[valuelen - 1] = '\0';
2390 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2391 if(valuelen > 0 && value[valuelen - 1] == '/')
2392 value[valuelen - 1] = '\0';
2394 imap->section = value;
2398 Curl_safefree(name);
2399 Curl_safefree(value);
2401 return CURLE_URL_MALFORMAT;
2404 Curl_safefree(name);
2405 Curl_safefree(value);
2408 /* Any extra stuff at the end of the URL is an error */
2410 return CURLE_URL_MALFORMAT;
2415 /***********************************************************************
2417 * imap_parse_custom_request()
2419 * Parse the custom request.
2421 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2423 CURLcode result = CURLE_OK;
2424 struct SessionHandle *data = conn->data;
2425 struct IMAP *imap = data->req.protop;
2426 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2429 /* URL decode the custom request */
2430 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2432 /* Extract the parameters if specified */
2434 const char *params = imap->custom;
2436 while(*params && *params != ' ')
2440 imap->custom_params = strdup(params);
2441 imap->custom[params - imap->custom] = '\0';
2443 if(!imap->custom_params)
2444 result = CURLE_OUT_OF_MEMORY;
2452 #endif /* CURL_DISABLE_IMAP */