1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC2195 CRAM-MD5 authentication
22 * RFC2595 Using TLS with IMAP, POP3 and ACAP
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3501 IMAPv4 protocol
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4959 IMAP Extension for SASL Initial Client Response
28 * RFC5092 IMAP URL Scheme
30 ***************************************************************************/
32 #include "curl_setup.h"
34 #ifndef CURL_DISABLE_IMAP
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
43 #include <sys/utsname.h>
53 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
55 #define in_addr_t unsigned long
58 #include <curl/curl.h>
66 #include "http.h" /* for HTTP proxy tunnel stuff */
70 #include "strtoofft.h"
79 #include "curl_sasl.h"
81 #define _MPRINTF_REPLACE /* use our functions only */
82 #include <curl/mprintf.h>
84 #include "curl_memory.h"
85 /* The last #include file should be: */
88 /* Local API functions */
89 static CURLcode imap_parse_url_path(struct connectdata *conn);
90 static CURLcode imap_parse_custom_request(struct connectdata *conn);
91 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
92 static CURLcode imap_do(struct connectdata *conn, bool *done);
93 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
95 static CURLcode imap_connect(struct connectdata *conn, bool *done);
96 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
97 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
98 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
100 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
101 static CURLcode imap_setup_connection(struct connectdata *conn);
104 * IMAP protocol handler.
107 const struct Curl_handler Curl_handler_imap = {
109 imap_setup_connection, /* setup_connection */
111 imap_done, /* done */
112 ZERO_NULL, /* do_more */
113 imap_connect, /* connect_it */
114 imap_multi_statemach, /* connecting */
115 imap_doing, /* doing */
116 imap_getsock, /* proto_getsock */
117 imap_getsock, /* doing_getsock */
118 ZERO_NULL, /* domore_getsock */
119 ZERO_NULL, /* perform_getsock */
120 imap_disconnect, /* disconnect */
121 ZERO_NULL, /* readwrite */
122 PORT_IMAP, /* defport */
123 CURLPROTO_IMAP, /* protocol */
124 PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
125 | PROTOPT_NOURLQUERY /* flags */
130 * IMAPS protocol handler.
133 const struct Curl_handler Curl_handler_imaps = {
134 "IMAPS", /* scheme */
135 imap_setup_connection, /* setup_connection */
137 imap_done, /* done */
138 ZERO_NULL, /* do_more */
139 imap_connect, /* connect_it */
140 imap_multi_statemach, /* connecting */
141 imap_doing, /* doing */
142 imap_getsock, /* proto_getsock */
143 imap_getsock, /* doing_getsock */
144 ZERO_NULL, /* domore_getsock */
145 ZERO_NULL, /* perform_getsock */
146 imap_disconnect, /* disconnect */
147 ZERO_NULL, /* readwrite */
148 PORT_IMAPS, /* defport */
149 CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
150 PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
151 | PROTOPT_NOURLQUERY /* flags */
155 #ifndef CURL_DISABLE_HTTP
157 * HTTP-proxyed IMAP protocol handler.
160 static const struct Curl_handler Curl_handler_imap_proxy = {
162 ZERO_NULL, /* setup_connection */
163 Curl_http, /* do_it */
164 Curl_http_done, /* done */
165 ZERO_NULL, /* do_more */
166 ZERO_NULL, /* connect_it */
167 ZERO_NULL, /* connecting */
168 ZERO_NULL, /* doing */
169 ZERO_NULL, /* proto_getsock */
170 ZERO_NULL, /* doing_getsock */
171 ZERO_NULL, /* domore_getsock */
172 ZERO_NULL, /* perform_getsock */
173 ZERO_NULL, /* disconnect */
174 ZERO_NULL, /* readwrite */
175 PORT_IMAP, /* defport */
176 CURLPROTO_HTTP, /* protocol */
177 PROTOPT_NONE /* flags */
182 * HTTP-proxyed IMAPS protocol handler.
185 static const struct Curl_handler Curl_handler_imaps_proxy = {
186 "IMAPS", /* scheme */
187 ZERO_NULL, /* setup_connection */
188 Curl_http, /* do_it */
189 Curl_http_done, /* done */
190 ZERO_NULL, /* do_more */
191 ZERO_NULL, /* connect_it */
192 ZERO_NULL, /* connecting */
193 ZERO_NULL, /* doing */
194 ZERO_NULL, /* proto_getsock */
195 ZERO_NULL, /* doing_getsock */
196 ZERO_NULL, /* domore_getsock */
197 ZERO_NULL, /* perform_getsock */
198 ZERO_NULL, /* disconnect */
199 ZERO_NULL, /* readwrite */
200 PORT_IMAPS, /* defport */
201 CURLPROTO_HTTP, /* protocol */
202 PROTOPT_NONE /* flags */
208 static void imap_to_imaps(struct connectdata *conn)
210 conn->handler = &Curl_handler_imaps;
213 #define imap_to_imaps(x) Curl_nop_stmt
216 /***********************************************************************
220 * Sends the formated string as an IMAP command to the server.
222 * Designed to never block.
224 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
226 CURLcode result = CURLE_OK;
227 struct imap_conn *imapc = &conn->proto.imapc;
232 /* Calculate the next command ID wrapping at 3 digits */
233 imapc->cmdid = (imapc->cmdid + 1) % 1000;
235 /* Calculate the tag based on the connection ID and command ID */
236 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
237 'A' + (conn->connection_id % 26), imapc->cmdid);
239 /* Prefix the format with the tag */
240 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
242 return CURLE_OUT_OF_MEMORY;
244 /* Send the data with the tag */
245 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
247 Curl_safefree(taggedfmt);
253 /***********************************************************************
257 * Checks the input string for characters that need escaping and returns an
258 * atom ready for sending to the server.
260 * The returned string needs to be freed.
263 static char *imap_atom(const char *str)
267 size_t backsp_count = 0;
268 size_t quote_count = 0;
269 bool space_exists = FALSE;
276 /* Count any unescapped characters */
289 /* Does the input contain any unescapped characters? */
290 if(!backsp_count && !quote_count && !space_exists)
293 /* Calculate the new string length */
294 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
296 /* Allocate the new string */
297 newstr = (char *) malloc((newlen + 1) * sizeof(char));
301 /* Surround the string in quotes if necessary */
305 newstr[newlen - 1] = '"';
309 /* Copy the string, escaping backslash and quote characters along the way */
312 if(*p1 == '\\' || *p1 == '"') {
323 /* Terminate the string */
324 newstr[newlen] = '\0';
329 /***********************************************************************
333 * Determines whether the untagged response is related to the specified
334 * command by checking if it is in format "* <command-name> ..." or
335 * "* <number> <command-name> ...".
337 * The "* " marker is assumed to have already been checked by the caller.
339 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
341 const char *end = line + len;
342 size_t cmd_len = strlen(cmd);
344 /* Skip the untagged response marker */
347 /* Do we have a number after the marker? */
348 if(line < end && ISDIGIT(*line)) {
349 /* Skip the number */
352 while(line < end && ISDIGIT(*line));
354 /* Do we have the space character? */
355 if(line == end || *line != ' ')
361 /* Does the command name match and is it followed by a space character or at
363 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
364 (line[cmd_len] == ' ' || line + cmd_len == end))
370 /***********************************************************************
374 * Checks whether the given string is a valid tagged, untagged or continuation
375 * response which can be processed by the response handler.
377 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
380 struct IMAP *imap = conn->data->state.proto.imap;
381 struct imap_conn *imapc = &conn->proto.imapc;
382 const char *id = imapc->resptag;
383 size_t id_len = strlen(id);
385 /* Do we have a tagged command response? */
386 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
390 if(len >= 2 && !memcmp(line, "OK", 2))
392 else if(len >= 2 && !memcmp(line, "NO", 2))
394 else if(len >= 3 && !memcmp(line, "BAD", 3))
397 failf(conn->data, "Bad tagged response");
404 /* Do we have an untagged command response? */
405 if(len >= 2 && !memcmp("* ", line, 2)) {
406 switch(imapc->state) {
407 /* States which are interested in untagged responses */
408 case IMAP_CAPABILITY:
409 if(!imap_matchresp(line, len, "CAPABILITY"))
414 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
415 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
416 (strcmp(imap->custom, "STORE") ||
417 !imap_matchresp(line, len, "FETCH")) &&
418 strcmp(imap->custom, "SELECT") &&
419 strcmp(imap->custom, "EXAMINE")))
424 /* SELECT is special in that its untagged responses do not have a
425 common prefix so accept anything! */
429 if(!imap_matchresp(line, len, "FETCH"))
433 /* Ignore other untagged responses */
442 /* Do we have a continuation response? */
443 if((len == 3 && !memcmp("+", line, 1)) ||
444 (len >= 2 && !memcmp("+ ", line, 2))) {
445 switch(imapc->state) {
446 /* States which are interested in continuation responses */
447 case IMAP_AUTHENTICATE_PLAIN:
448 case IMAP_AUTHENTICATE_LOGIN:
449 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
450 case IMAP_AUTHENTICATE_CRAMMD5:
451 case IMAP_AUTHENTICATE_DIGESTMD5:
452 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
453 case IMAP_AUTHENTICATE_NTLM:
454 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
455 case IMAP_AUTHENTICATE_FINAL:
461 failf(conn->data, "Unexpected continuation response");
469 return FALSE; /* Nothing for us */
472 /***********************************************************************
476 * This is the ONLY way to change IMAP state!
478 static void state(struct connectdata *conn, imapstate newstate)
480 struct imap_conn *imapc = &conn->proto.imapc;
481 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
482 /* for debug purposes */
483 static const char * const names[]={
489 "AUTHENTICATE_PLAIN",
490 "AUTHENTICATE_LOGIN",
491 "AUTHENTICATE_LOGIN_PASSWD",
492 "AUTHENTICATE_CRAMMD5",
493 "AUTHENTICATE_DIGESTMD5",
494 "AUTHENTICATE_DIGESTMD5_RESP",
496 "AUTHENTICATE_NTLM_TYPE2MSG",
497 "AUTHENTICATE_FINAL",
509 if(imapc->state != newstate)
510 infof(conn->data, "IMAP %p state change from %s to %s\n",
511 imapc, names[imapc->state], names[newstate]);
514 imapc->state = newstate;
517 /***********************************************************************
519 * imap_perform_capability()
521 * Sends the CAPABILITY command in order to obtain a list of server side
522 * supported capabilities.
524 static CURLcode imap_perform_capability(struct connectdata *conn)
526 CURLcode result = CURLE_OK;
527 struct imap_conn *imapc = &conn->proto.imapc;
529 imapc->authmechs = 0; /* No known authentication mechanisms yet */
530 imapc->authused = 0; /* Clear the authentication mechanism used */
531 imapc->tls_supported = FALSE; /* Clear the TLS capability */
533 /* Send the CAPABILITY command */
534 result = imap_sendf(conn, "CAPABILITY");
537 state(conn, IMAP_CAPABILITY);
542 /***********************************************************************
544 * imap_perform_starttls()
546 * Sends the STARTTLS command to start the upgrade to TLS.
548 static CURLcode imap_perform_starttls(struct connectdata *conn)
550 CURLcode result = CURLE_OK;
552 /* Send the STARTTLS command */
553 result = imap_sendf(conn, "STARTTLS");
556 state(conn, IMAP_STARTTLS);
561 /***********************************************************************
563 * imap_perform_upgrade_tls()
565 * Performs the upgrade to TLS.
567 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
569 CURLcode result = CURLE_OK;
570 struct imap_conn *imapc = &conn->proto.imapc;
572 /* Start the SSL connection */
573 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
576 if(imapc->state != IMAP_UPGRADETLS)
577 state(conn, IMAP_UPGRADETLS);
581 result = imap_perform_capability(conn);
588 /***********************************************************************
590 * imap_perform_login()
592 * Sends a clear text LOGIN command to authenticate with.
594 static CURLcode imap_perform_login(struct connectdata *conn)
596 CURLcode result = CURLE_OK;
600 /* Check we have a username and password to authenticate with and end the
601 connect phase if we don't */
602 if(!conn->bits.user_passwd) {
603 state(conn, IMAP_STOP);
608 /* Make sure the username and password are in the correct atom format */
609 user = imap_atom(conn->user);
610 passwd = imap_atom(conn->passwd);
612 /* Send the LOGIN command */
613 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
614 passwd ? passwd : "");
617 Curl_safefree(passwd);
620 state(conn, IMAP_LOGIN);
625 /***********************************************************************
627 * imap_perform_authenticate()
629 * Sends an AUTHENTICATE command allowing the client to login with the
630 * appropriate SASL authentication mechanism.
632 * Additionally, the function will perform fallback to the LOGIN command
633 * should a common mechanism not be available between the client and server.
635 static CURLcode imap_perform_authenticate(struct connectdata *conn)
637 CURLcode result = CURLE_OK;
638 struct imap_conn *imapc = &conn->proto.imapc;
639 const char *mech = NULL;
640 char *initresp = NULL;
642 imapstate state1 = IMAP_STOP;
643 imapstate state2 = IMAP_STOP;
645 /* Check we have a username and password to authenticate with and end the
646 connect phase if we don't */
647 if(!conn->bits.user_passwd) {
648 state(conn, IMAP_STOP);
653 /* Calculate the supported authentication mechanism by decreasing order of
655 #ifndef CURL_DISABLE_CRYPTO_AUTH
656 if(imapc->authmechs & SASL_MECH_DIGEST_MD5) {
658 state1 = IMAP_AUTHENTICATE_DIGESTMD5;
659 imapc->authused = SASL_MECH_DIGEST_MD5;
661 else if(imapc->authmechs & SASL_MECH_CRAM_MD5) {
663 state1 = IMAP_AUTHENTICATE_CRAMMD5;
664 imapc->authused = SASL_MECH_CRAM_MD5;
669 if(imapc->authmechs & SASL_MECH_NTLM) {
671 state1 = IMAP_AUTHENTICATE_NTLM;
672 state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
673 imapc->authused = SASL_MECH_NTLM;
675 if(imapc->ir_supported)
676 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
682 if(imapc->authmechs & SASL_MECH_LOGIN) {
684 state1 = IMAP_AUTHENTICATE_LOGIN;
685 state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
686 imapc->authused = SASL_MECH_LOGIN;
688 if(imapc->ir_supported)
689 result = Curl_sasl_create_login_message(conn->data, conn->user,
692 else if(imapc->authmechs & SASL_MECH_PLAIN) {
694 state1 = IMAP_AUTHENTICATE_PLAIN;
695 state2 = IMAP_AUTHENTICATE_FINAL;
696 imapc->authused = SASL_MECH_PLAIN;
698 if(imapc->ir_supported)
699 result = Curl_sasl_create_plain_message(conn->data, conn->user,
700 conn->passwd, &initresp, &len);
707 /* Perform SASL based authentication */
709 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
715 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
721 Curl_safefree(initresp);
723 else if(!imapc->login_disabled)
724 /* Perform clear text authentication */
725 result = imap_perform_login(conn);
727 /* Other mechanisms not supported */
728 infof(conn->data, "No known authentication mechanisms supported!\n");
729 result = CURLE_LOGIN_DENIED;
735 /***********************************************************************
737 * imap_perform_list()
739 * Sends a LIST command or an alternative custom request.
741 static CURLcode imap_perform_list(struct connectdata *conn)
743 CURLcode result = CURLE_OK;
744 struct SessionHandle *data = conn->data;
745 struct IMAP *imap = data->state.proto.imap;
749 /* Send the custom request */
750 result = imap_sendf(conn, "%s%s", imap->custom,
751 imap->custom_params ? imap->custom_params : "");
753 /* Make sure the mailbox is in the correct atom format */
754 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
756 return CURLE_OUT_OF_MEMORY;
758 /* Send the LIST command */
759 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
761 Curl_safefree(mailbox);
765 state(conn, IMAP_LIST);
770 /***********************************************************************
772 * imap_perform_select()
774 * Sends a SELECT command to ask the server to change the selected mailbox.
776 static CURLcode imap_perform_select(struct connectdata *conn)
778 CURLcode result = CURLE_OK;
779 struct SessionHandle *data = conn->data;
780 struct IMAP *imap = data->state.proto.imap;
781 struct imap_conn *imapc = &conn->proto.imapc;
784 /* Invalidate old information as we are switching mailboxes */
785 Curl_safefree(imapc->mailbox);
786 Curl_safefree(imapc->mailbox_uidvalidity);
788 /* Check we have a mailbox */
790 failf(conn->data, "Cannot SELECT without a mailbox.");
791 return CURLE_URL_MALFORMAT;
794 /* Make sure the mailbox is in the correct atom format */
795 mailbox = imap_atom(imap->mailbox);
797 return CURLE_OUT_OF_MEMORY;
799 /* Send the SELECT command */
800 result = imap_sendf(conn, "SELECT %s", mailbox);
802 Curl_safefree(mailbox);
805 state(conn, IMAP_SELECT);
810 /***********************************************************************
812 * imap_perform_fetch()
814 * Sends a FETCH command to initiate the download of a message.
816 static CURLcode imap_perform_fetch(struct connectdata *conn)
818 CURLcode result = CURLE_OK;
819 struct IMAP *imap = conn->data->state.proto.imap;
821 /* Check we have a UID */
823 failf(conn->data, "Cannot FETCH without a UID.");
824 return CURLE_URL_MALFORMAT;
827 /* Send the FETCH command */
828 result = imap_sendf(conn, "FETCH %s BODY[%s]",
830 imap->section ? imap->section : "");
833 state(conn, IMAP_FETCH);
838 /***********************************************************************
840 * imap_perform_append()
842 * Sends an APPEND command to initiate the upload of a message.
844 static CURLcode imap_perform_append(struct connectdata *conn)
846 CURLcode result = CURLE_OK;
847 struct IMAP *imap = conn->data->state.proto.imap;
850 /* Check we have a mailbox */
852 failf(conn->data, "Cannot APPEND without a mailbox.");
853 return CURLE_URL_MALFORMAT;
856 /* Check we know the size of the upload */
857 if(conn->data->set.infilesize < 0) {
858 failf(conn->data, "Cannot APPEND with unknown input file size\n");
859 return CURLE_UPLOAD_FAILED;
862 /* Make sure the mailbox is in the correct atom format */
863 mailbox = imap_atom(imap->mailbox);
865 return CURLE_OUT_OF_MEMORY;
867 /* Send the APPEND command */
868 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
869 mailbox, conn->data->set.infilesize);
871 Curl_safefree(mailbox);
874 state(conn, IMAP_APPEND);
879 /***********************************************************************
881 * imap_perform_logout()
883 * Performs the logout action prior to sclose() being called.
885 static CURLcode imap_perform_logout(struct connectdata *conn)
887 CURLcode result = CURLE_OK;
889 /* Send the LOGOUT command */
890 result = imap_sendf(conn, "LOGOUT");
893 state(conn, IMAP_LOGOUT);
898 /* For the initial server greeting */
899 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
903 CURLcode result = CURLE_OK;
904 struct SessionHandle *data = conn->data;
906 (void)instate; /* no use for this yet */
908 if(imapcode != 'O') {
909 failf(data, "Got unexpected imap-server response");
910 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
913 result = imap_perform_capability(conn);
918 /* For CAPABILITY responses */
919 static CURLcode imap_state_capability_resp(struct connectdata *conn,
923 CURLcode result = CURLE_OK;
924 struct SessionHandle *data = conn->data;
925 struct imap_conn *imapc = &conn->proto.imapc;
926 const char *line = data->state.buffer;
929 (void)instate; /* no use for this yet */
931 /* Do we have a untagged response? */
932 if(imapcode == '*') {
935 /* Loop through the data line */
938 (*line == ' ' || *line == '\t' ||
939 *line == '\r' || *line == '\n')) {
947 /* Extract the word */
948 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
949 line[wordlen] != '\t' && line[wordlen] != '\r' &&
950 line[wordlen] != '\n';)
953 /* Does the server support the STARTTLS capability? */
954 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
955 imapc->tls_supported = TRUE;
957 /* Has the server explicitly disabled clear text authentication? */
958 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
959 imapc->login_disabled = TRUE;
961 /* Does the server support the SASL-IR capability? */
962 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
963 imapc->ir_supported = TRUE;
965 /* Do we have a SASL based authentication mechanism? */
966 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
970 /* Test the word for a matching authentication mechanism */
971 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
972 imapc->authmechs |= SASL_MECH_LOGIN;
973 if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
974 imapc->authmechs |= SASL_MECH_PLAIN;
975 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
976 imapc->authmechs |= SASL_MECH_CRAM_MD5;
977 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
978 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
979 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
980 imapc->authmechs |= SASL_MECH_GSSAPI;
981 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
982 imapc->authmechs |= SASL_MECH_EXTERNAL;
983 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
984 imapc->authmechs |= SASL_MECH_NTLM;
990 else if(imapcode == 'O') {
991 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
992 /* We don't have a SSL/TLS connection yet, but SSL is requested */
993 if(imapc->tls_supported)
994 /* Switch to TLS connection now */
995 result = imap_perform_starttls(conn);
996 else if(data->set.use_ssl == CURLUSESSL_TRY)
997 /* Fallback and carry on with authentication */
998 result = imap_perform_authenticate(conn);
1000 failf(data, "STARTTLS not supported.");
1001 result = CURLE_USE_SSL_FAILED;
1005 result = imap_perform_authenticate(conn);
1008 result = imap_perform_login(conn);
1013 /* For STARTTLS responses */
1014 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
1018 CURLcode result = CURLE_OK;
1019 struct SessionHandle *data = conn->data;
1021 (void)instate; /* no use for this yet */
1023 if(imapcode != 'O') {
1024 if(data->set.use_ssl != CURLUSESSL_TRY) {
1025 failf(data, "STARTTLS denied. %c", imapcode);
1026 result = CURLE_USE_SSL_FAILED;
1029 result = imap_perform_authenticate(conn);
1032 result = imap_perform_upgrade_tls(conn);
1037 /* For AUTHENTICATE PLAIN (without initial response) responses */
1038 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
1042 CURLcode result = CURLE_OK;
1043 struct SessionHandle *data = conn->data;
1045 char *plainauth = NULL;
1047 (void)instate; /* no use for this yet */
1049 if(imapcode != '+') {
1050 failf(data, "Access denied. %c", imapcode);
1051 result = CURLE_LOGIN_DENIED;
1054 /* Create the authorisation message */
1055 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
1058 /* Send the message */
1061 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
1064 state(conn, IMAP_AUTHENTICATE_FINAL);
1067 Curl_safefree(plainauth);
1074 /* For AUTHENTICATE LOGIN (without initial response) responses */
1075 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1079 CURLcode result = CURLE_OK;
1080 struct SessionHandle *data = conn->data;
1082 char *authuser = NULL;
1084 (void)instate; /* no use for this yet */
1086 if(imapcode != '+') {
1087 failf(data, "Access denied: %d", imapcode);
1088 result = CURLE_LOGIN_DENIED;
1091 /* Create the user message */
1092 result = Curl_sasl_create_login_message(data, conn->user,
1098 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1101 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1104 Curl_safefree(authuser);
1111 /* For AUTHENTICATE LOGIN user entry responses */
1112 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1116 CURLcode result = CURLE_OK;
1117 struct SessionHandle *data = conn->data;
1119 char *authpasswd = NULL;
1121 (void)instate; /* no use for this yet */
1123 if(imapcode != '+') {
1124 failf(data, "Access denied: %d", imapcode);
1125 result = CURLE_LOGIN_DENIED;
1128 /* Create the password message */
1129 result = Curl_sasl_create_login_message(data, conn->passwd,
1132 /* Send the password */
1135 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1138 state(conn, IMAP_AUTHENTICATE_FINAL);
1141 Curl_safefree(authpasswd);
1148 #ifndef CURL_DISABLE_CRYPTO_AUTH
1149 /* For AUTHENTICATE CRAM-MD5 responses */
1150 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1154 CURLcode result = CURLE_OK;
1155 struct SessionHandle *data = conn->data;
1156 char *chlg64 = data->state.buffer;
1158 char *rplyb64 = NULL;
1160 (void)instate; /* no use for this yet */
1162 if(imapcode != '+') {
1163 failf(data, "Access denied: %d", imapcode);
1164 return CURLE_LOGIN_DENIED;
1167 /* Get the challenge */
1168 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1171 /* Terminate the challenge */
1172 if(*chlg64 != '=') {
1173 for(len = strlen(chlg64); len--;)
1174 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
1175 chlg64[len] != '\t')
1183 /* Create the response message */
1184 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
1185 conn->passwd, &rplyb64, &len);
1187 /* Send the response */
1190 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1193 state(conn, IMAP_AUTHENTICATE_FINAL);
1196 Curl_safefree(rplyb64);
1202 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1203 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1207 CURLcode result = CURLE_OK;
1208 struct SessionHandle *data = conn->data;
1209 char *chlg64 = data->state.buffer;
1211 char *rplyb64 = NULL;
1213 (void)instate; /* no use for this yet */
1215 if(imapcode != '+') {
1216 failf(data, "Access denied: %d", imapcode);
1217 return CURLE_LOGIN_DENIED;
1220 /* Get the challenge */
1221 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1224 /* Create the response message */
1225 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
1226 conn->passwd, "imap",
1229 /* Send the response */
1232 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1235 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1238 Curl_safefree(rplyb64);
1244 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1245 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1249 CURLcode result = CURLE_OK;
1250 struct SessionHandle *data = conn->data;
1252 (void)instate; /* no use for this yet */
1254 if(imapcode != '+') {
1255 failf(data, "Authentication failed: %d", imapcode);
1256 result = CURLE_LOGIN_DENIED;
1259 /* Send an empty response */
1260 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1263 state(conn, IMAP_AUTHENTICATE_FINAL);
1271 /* For AUTHENTICATE NTLM (without initial response) responses */
1272 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1276 CURLcode result = CURLE_OK;
1277 struct SessionHandle *data = conn->data;
1279 char *type1msg = NULL;
1281 (void)instate; /* no use for this yet */
1283 if(imapcode != '+') {
1284 failf(data, "Access denied: %d", imapcode);
1285 result = CURLE_LOGIN_DENIED;
1288 /* Create the type-1 message */
1289 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1293 /* Send the message */
1296 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1299 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1302 Curl_safefree(type1msg);
1309 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1310 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1314 CURLcode result = CURLE_OK;
1315 struct SessionHandle *data = conn->data;
1317 char *type3msg = NULL;
1319 (void)instate; /* no use for this yet */
1321 if(imapcode != '+') {
1322 failf(data, "Access denied: %d", imapcode);
1323 result = CURLE_LOGIN_DENIED;
1326 /* Create the type-3 message */
1327 result = Curl_sasl_create_ntlm_type3_message(data,
1328 data->state.buffer + 2,
1329 conn->user, conn->passwd,
1333 /* Send the message */
1336 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1339 state(conn, IMAP_AUTHENTICATE_FINAL);
1342 Curl_safefree(type3msg);
1350 /* For final responses to the AUTHENTICATE sequence */
1351 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1355 CURLcode result = CURLE_OK;
1356 struct SessionHandle *data = conn->data;
1358 (void)instate; /* no use for this yet */
1360 if(imapcode != 'O') {
1361 failf(data, "Authentication failed: %d", imapcode);
1362 result = CURLE_LOGIN_DENIED;
1365 /* End of connect phase */
1366 state(conn, IMAP_STOP);
1371 /* For LOGIN responses */
1372 static CURLcode imap_state_login_resp(struct connectdata *conn,
1376 CURLcode result = CURLE_OK;
1377 struct SessionHandle *data = conn->data;
1379 (void)instate; /* no use for this yet */
1381 if(imapcode != 'O') {
1382 failf(data, "Access denied. %c", imapcode);
1383 result = CURLE_LOGIN_DENIED;
1386 /* End of connect phase */
1387 state(conn, IMAP_STOP);
1392 /* For LIST responses */
1393 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1396 CURLcode result = CURLE_OK;
1397 char *line = conn->data->state.buffer;
1398 size_t len = strlen(line);
1400 (void)instate; /* No use for this yet */
1402 if(imapcode == '*') {
1403 /* Temporarily add the LF character back and send as body to the client */
1405 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1408 else if(imapcode != 'O')
1409 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1411 /* End of DO phase */
1412 state(conn, IMAP_STOP);
1417 /* For SELECT responses */
1418 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1421 CURLcode result = CURLE_OK;
1422 struct SessionHandle *data = conn->data;
1423 struct IMAP *imap = conn->data->state.proto.imap;
1424 struct imap_conn *imapc = &conn->proto.imapc;
1425 const char *line = data->state.buffer;
1428 (void)instate; /* no use for this yet */
1430 if(imapcode == '*') {
1431 /* See if this is an UIDVALIDITY response */
1432 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1433 Curl_safefree(imapc->mailbox_uidvalidity);
1434 imapc->mailbox_uidvalidity = strdup(tmp);
1437 else if(imapcode == 'O') {
1438 /* Check if the UIDVALIDITY has been specified and matches */
1439 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1440 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1441 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1442 result = CURLE_REMOTE_FILE_NOT_FOUND;
1445 /* Note the currently opened mailbox on this connection */
1446 imapc->mailbox = strdup(imap->mailbox);
1449 result = imap_perform_list(conn);
1451 result = imap_perform_fetch(conn);
1455 failf(data, "Select failed");
1456 result = CURLE_LOGIN_DENIED;
1462 /* For the (first line of the) FETCH responses */
1463 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1466 CURLcode result = CURLE_OK;
1467 struct SessionHandle *data = conn->data;
1468 struct imap_conn *imapc = &conn->proto.imapc;
1469 struct pingpong *pp = &imapc->pp;
1470 const char *ptr = data->state.buffer;
1471 bool parsed = FALSE;
1474 (void)instate; /* no use for this yet */
1476 if(imapcode != '*') {
1477 Curl_pgrsSetDownloadSize(data, 0);
1478 state(conn, IMAP_STOP);
1479 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1482 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1483 the continuation data contained within the curly brackets */
1484 while(*ptr && (*ptr != '{'))
1489 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1490 if(endptr - ptr > 1 && endptr[0] == '}' &&
1491 endptr[1] == '\r' && endptr[2] == '\0')
1496 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1497 Curl_pgrsSetDownloadSize(data, size);
1500 /* At this point there is a bunch of data in the header "cache" that is
1501 actually body content, send it as body and then skip it. Do note
1502 that there may even be additional "headers" after the body. */
1503 size_t chunk = pp->cache_size;
1505 if(chunk > (size_t)size)
1506 /* The conversion from curl_off_t to size_t is always fine here */
1507 chunk = (size_t)size;
1509 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1513 data->req.bytecount += chunk;
1516 infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1517 " bytes are left for transfer\n", (curl_off_t)chunk, size);
1519 /* Have we used the entire cache or just part of it?*/
1520 if(pp->cache_size > chunk) {
1521 /* Only part of it so shrink the cache to fit the trailing data */
1522 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1523 pp->cache_size -= chunk;
1526 /* Free the cache */
1527 Curl_safefree(pp->cache);
1529 /* Reset the cache size */
1535 /* The entire data is already transferred! */
1536 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1539 data->req.maxdownload = size;
1540 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1544 /* We don't know how to parse this line */
1545 failf(pp->conn->data, "Failed to parse FETCH response.");
1546 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1549 /* End of DO phase */
1550 state(conn, IMAP_STOP);
1555 /* For final FETCH responses performed after the download */
1556 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1560 CURLcode result = CURLE_OK;
1562 (void)instate; /* No use for this yet */
1565 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1567 /* End of DONE phase */
1568 state(conn, IMAP_STOP);
1573 /* For APPEND responses */
1574 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1577 CURLcode result = CURLE_OK;
1578 struct SessionHandle *data = conn->data;
1580 (void)instate; /* No use for this yet */
1582 if(imapcode != '+') {
1583 result = CURLE_UPLOAD_FAILED;
1586 /* Set the progress upload size */
1587 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1590 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1592 /* End of DO phase */
1593 state(conn, IMAP_STOP);
1599 /* For final APPEND responses performed after the upload */
1600 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1604 CURLcode result = CURLE_OK;
1606 (void)instate; /* No use for this yet */
1609 result = CURLE_UPLOAD_FAILED;
1611 /* End of DONE phase */
1612 state(conn, IMAP_STOP);
1617 static CURLcode imap_statemach_act(struct connectdata *conn)
1619 CURLcode result = CURLE_OK;
1620 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1622 struct imap_conn *imapc = &conn->proto.imapc;
1623 struct pingpong *pp = &imapc->pp;
1626 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1627 if(imapc->state == IMAP_UPGRADETLS)
1628 return imap_perform_upgrade_tls(conn);
1630 /* Flush any data that needs to be sent */
1632 return Curl_pp_flushsend(pp);
1635 /* Read the response from the server */
1636 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1640 /* Was there an error parsing the response line? */
1642 return CURLE_FTP_WEIRD_SERVER_REPLY;
1647 /* We have now received a full IMAP server response */
1648 switch(imapc->state) {
1649 case IMAP_SERVERGREET:
1650 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1653 case IMAP_CAPABILITY:
1654 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1658 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1661 case IMAP_AUTHENTICATE_PLAIN:
1662 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1665 case IMAP_AUTHENTICATE_LOGIN:
1666 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1669 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1670 result = imap_state_auth_login_password_resp(conn, imapcode,
1674 #ifndef CURL_DISABLE_CRYPTO_AUTH
1675 case IMAP_AUTHENTICATE_CRAMMD5:
1676 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1679 case IMAP_AUTHENTICATE_DIGESTMD5:
1680 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1683 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1684 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1689 case IMAP_AUTHENTICATE_NTLM:
1690 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1693 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1694 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1699 case IMAP_AUTHENTICATE_FINAL:
1700 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1704 result = imap_state_login_resp(conn, imapcode, imapc->state);
1708 result = imap_state_list_resp(conn, imapcode, imapc->state);
1712 result = imap_state_select_resp(conn, imapcode, imapc->state);
1716 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1719 case IMAP_FETCH_FINAL:
1720 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1724 result = imap_state_append_resp(conn, imapcode, imapc->state);
1727 case IMAP_APPEND_FINAL:
1728 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1732 /* fallthrough, just stop! */
1734 /* internal error */
1735 state(conn, IMAP_STOP);
1738 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1743 /* Called repeatedly until done from multi.c */
1744 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1746 CURLcode result = CURLE_OK;
1747 struct imap_conn *imapc = &conn->proto.imapc;
1749 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1750 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1752 result = Curl_pp_statemach(&imapc->pp, FALSE);
1754 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1759 static CURLcode imap_block_statemach(struct connectdata *conn)
1761 CURLcode result = CURLE_OK;
1762 struct imap_conn *imapc = &conn->proto.imapc;
1764 while(imapc->state != IMAP_STOP && !result)
1765 result = Curl_pp_statemach(&imapc->pp, TRUE);
1770 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1772 static CURLcode imap_init(struct connectdata *conn)
1774 CURLcode result = CURLE_OK;
1775 struct SessionHandle *data = conn->data;
1776 struct IMAP *imap = data->state.proto.imap;
1779 imap = data->state.proto.imap = calloc(sizeof(struct IMAP), 1);
1781 result = CURLE_OUT_OF_MEMORY;
1787 /* For the IMAP "protocol connect" and "doing" phases only */
1788 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1791 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1794 /***********************************************************************
1798 * This function should do everything that is to be considered a part of the
1801 * The variable 'done' points to will be TRUE if the protocol-layer connect
1802 * phase is done when this function returns, or FALSE is not. When called as
1803 * a part of the easy interface, it will always be TRUE.
1805 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1807 CURLcode result = CURLE_OK;
1808 struct imap_conn *imapc = &conn->proto.imapc;
1809 struct pingpong *pp = &imapc->pp;
1811 *done = FALSE; /* default to not done yet */
1813 /* If there already is a protocol-specific struct allocated for this
1814 sessionhandle, deal with it */
1815 Curl_reset_reqproto(conn);
1817 /* Initialise the IMAP layer */
1818 result = imap_init(conn);
1822 /* We always support persistent connections in IMAP */
1823 conn->bits.close = FALSE;
1825 /* Set the default response time-out */
1826 pp->response_time = RESP_TIMEOUT;
1827 pp->statemach_act = imap_statemach_act;
1828 pp->endofresp = imap_endofresp;
1831 /* Initialise the pingpong layer */
1834 /* Start off waiting for the server greeting response */
1835 state(conn, IMAP_SERVERGREET);
1837 /* Start off with an response id of '*' */
1838 strcpy(imapc->resptag, "*");
1840 result = imap_multi_statemach(conn, done);
1845 /***********************************************************************
1849 * The DONE function. This does what needs to be done after a single DO has
1852 * Input argument is already checked for validity.
1854 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1857 CURLcode result = CURLE_OK;
1858 struct SessionHandle *data = conn->data;
1859 struct IMAP *imap = data->state.proto.imap;
1864 /* When the easy handle is removed from the multi interface while libcurl
1865 is still trying to resolve the host name, the IMAP struct is not yet
1866 initialized. However, the removal action calls Curl_done() which in
1867 turn calls this function, so we simply return success. */
1871 conn->bits.close = TRUE; /* marked for closure */
1872 result = status; /* use the already set error code */
1874 else if(!data->set.connect_only && !imap->custom &&
1875 (imap->uid || data->set.upload)) {
1876 /* Handle responses after FETCH or APPEND transfer has finished */
1877 if(!data->set.upload)
1878 state(conn, IMAP_FETCH_FINAL);
1880 /* End the APPEND command first by sending an empty line */
1881 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1883 state(conn, IMAP_APPEND_FINAL);
1886 /* Run the state-machine
1888 TODO: when the multi interface is used, this _really_ should be using
1889 the imap_multi_statemach function but we have no general support for
1890 non-blocking DONE operations, not in the multi state machine and with
1891 Curl_done() invokes on several places in the code!
1894 result = imap_block_statemach(conn);
1897 /* Cleanup our per-request based variables */
1898 Curl_safefree(imap->mailbox);
1899 Curl_safefree(imap->uidvalidity);
1900 Curl_safefree(imap->uid);
1901 Curl_safefree(imap->section);
1902 Curl_safefree(imap->custom);
1903 Curl_safefree(imap->custom_params);
1905 /* Clear the transfer mode for the next request */
1906 imap->transfer = FTPTRANSFER_BODY;
1911 /***********************************************************************
1915 * This is the actual DO function for IMAP. Fetch or append a message, or do
1916 * other things according to the options previously setup.
1918 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1921 /* This is IMAP and no proxy */
1922 CURLcode result = CURLE_OK;
1923 struct SessionHandle *data = conn->data;
1924 struct IMAP *imap = data->state.proto.imap;
1925 struct imap_conn *imapc = &conn->proto.imapc;
1926 bool selected = FALSE;
1928 DEBUGF(infof(conn->data, "DO phase starts\n"));
1930 if(conn->data->set.opt_no_body) {
1931 /* Requested no body means no transfer */
1932 imap->transfer = FTPTRANSFER_INFO;
1935 *dophase_done = FALSE; /* not done yet */
1937 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1938 has already been selected on this connection */
1939 if(imap->mailbox && imapc->mailbox &&
1940 !strcmp(imap->mailbox, imapc->mailbox) &&
1941 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1942 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1945 /* Start the first command in the DO phase */
1946 if(conn->data->set.upload)
1947 /* APPEND can be executed directly */
1948 result = imap_perform_append(conn);
1949 else if(imap->custom && (selected || !imap->mailbox))
1950 /* Custom command using the same mailbox or no mailbox */
1951 result = imap_perform_list(conn);
1952 else if(!imap->custom && selected && imap->uid)
1953 /* FETCH from the same mailbox */
1954 result = imap_perform_fetch(conn);
1955 else if(imap->mailbox && !selected && (imap->custom || imap->uid))
1956 /* SELECT the mailbox */
1957 result = imap_perform_select(conn);
1960 result = imap_perform_list(conn);
1965 /* Run the state-machine */
1966 result = imap_multi_statemach(conn, dophase_done);
1968 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1971 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1976 /***********************************************************************
1980 * This function is registered as 'curl_do' function. It decodes the path
1981 * parts etc as a wrapper to the actual DO function (imap_perform).
1983 * The input argument is already checked for validity.
1985 static CURLcode imap_do(struct connectdata *conn, bool *done)
1987 CURLcode result = CURLE_OK;
1989 *done = FALSE; /* default to false */
1991 /* Since connections can be re-used between SessionHandles, there might be a
1992 connection already existing but on a fresh SessionHandle struct. As such
1993 we make sure we have a good IMAP struct to play with. For new connections
1994 the IMAP struct is allocated and setup in the imap_connect() function. */
1995 Curl_reset_reqproto(conn);
1996 result = imap_init(conn);
2000 /* Parse the URL path */
2001 result = imap_parse_url_path(conn);
2005 /* Parse the custom request */
2006 result = imap_parse_custom_request(conn);
2010 result = imap_regular_transfer(conn, done);
2015 /***********************************************************************
2019 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
2020 * resources. BLOCKING.
2022 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2024 struct imap_conn *imapc = &conn->proto.imapc;
2026 /* We cannot send quit unconditionally. If this connection is stale or
2027 bad in any way, sending quit and waiting around here will make the
2028 disconnect wait in vain and cause more problems than we need to. */
2030 /* The IMAP session may or may not have been allocated/setup at this
2032 if(!dead_connection && imapc->pp.conn)
2033 if(!imap_perform_logout(conn))
2034 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
2036 /* Disconnect from the server */
2037 Curl_pp_disconnect(&imapc->pp);
2039 /* Cleanup the SASL module */
2040 Curl_sasl_cleanup(conn, imapc->authused);
2042 /* Cleanup our connection based variables */
2043 Curl_safefree(imapc->mailbox);
2044 Curl_safefree(imapc->mailbox_uidvalidity);
2049 /***********************************************************************
2053 * Portable test of whether the specified char is a "bchar" as defined in the
2054 * grammar of RFC-5092.
2056 static bool imap_is_bchar(char ch)
2060 case ':': case '@': case '/':
2061 /* bchar -> achar */
2063 /* bchar -> achar -> uchar -> unreserved */
2064 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2065 case '7': case '8': case '9':
2066 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2067 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2068 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2069 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2070 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2071 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2072 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2073 case 'v': case 'w': case 'x': case 'y': case 'z':
2074 case '-': case '.': case '_': case '~':
2075 /* bchar -> achar -> uchar -> sub-delims-sh */
2076 case '!': case '$': case '\'': case '(': case ')': case '*':
2078 /* bchar -> achar -> uchar -> pct-encoded */
2079 case '%': /* HEXDIG chars are already included above */
2087 /***********************************************************************
2089 * imap_parse_url_path()
2091 * Parse the URL path into separate path components.
2094 static CURLcode imap_parse_url_path(struct connectdata *conn)
2096 /* The imap struct is already initialised in imap_connect() */
2097 CURLcode result = CURLE_OK;
2098 struct SessionHandle *data = conn->data;
2099 struct IMAP *imap = data->state.proto.imap;
2100 const char *begin = data->state.path;
2101 const char *ptr = begin;
2103 /* See how much of the URL is a valid path and decode it */
2104 while(imap_is_bchar(*ptr))
2108 /* Remove the trailing slash if present */
2109 const char *end = ptr;
2110 if(end > begin && end[-1] == '/')
2113 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2119 imap->mailbox = NULL;
2121 /* There can be any number of parameters in the form ";NAME=VALUE" */
2122 while(*ptr == ';') {
2127 /* Find the length of the name parameter */
2129 while(*ptr && *ptr != '=')
2133 return CURLE_URL_MALFORMAT;
2135 /* Decode the name parameter */
2136 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2140 /* Find the length of the value parameter */
2142 while(imap_is_bchar(*ptr))
2145 /* Decode the value parameter */
2146 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2148 Curl_safefree(name);
2152 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2154 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2155 stripping of the trailing slash character if it is present.
2157 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2158 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2159 if(valuelen > 0 && value[valuelen - 1] == '/')
2160 value[valuelen - 1] = '\0';
2162 imap->uidvalidity = value;
2165 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2166 if(valuelen > 0 && value[valuelen - 1] == '/')
2167 value[valuelen - 1] = '\0';
2172 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2173 if(valuelen > 0 && value[valuelen - 1] == '/')
2174 value[valuelen - 1] = '\0';
2176 imap->section = value;
2180 Curl_safefree(name);
2181 Curl_safefree(value);
2183 return CURLE_URL_MALFORMAT;
2186 Curl_safefree(name);
2187 Curl_safefree(value);
2190 /* Any extra stuff at the end of the URL is an error */
2192 return CURLE_URL_MALFORMAT;
2197 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2199 CURLcode result = CURLE_OK;
2200 struct SessionHandle *data = conn->data;
2201 struct IMAP *imap = data->state.proto.imap;
2202 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2205 /* URL decode the custom request */
2206 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2208 /* Extract the parameters if specified */
2210 const char *params = imap->custom;
2212 while(*params && *params != ' ')
2216 imap->custom_params = strdup(params);
2217 imap->custom[params - imap->custom] = '\0';
2219 if(!imap->custom_params)
2220 result = CURLE_OUT_OF_MEMORY;
2228 /* Call this when the DO phase has completed */
2229 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2231 struct IMAP *imap = conn->data->state.proto.imap;
2235 if(imap->transfer != FTPTRANSFER_BODY)
2236 /* no data to transfer */
2237 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2242 /* Called from multi.c while DOing */
2243 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2245 CURLcode result = imap_multi_statemach(conn, dophase_done);
2248 DEBUGF(infof(conn->data, "DO phase failed\n"));
2249 else if(*dophase_done) {
2250 result = imap_dophase_done(conn, FALSE /* not connected */);
2252 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2258 /***********************************************************************
2260 * imap_regular_transfer()
2262 * The input argument is already checked for validity.
2264 * Performs all commands done before a regular transfer between a local and a
2267 static CURLcode imap_regular_transfer(struct connectdata *conn,
2270 CURLcode result = CURLE_OK;
2271 bool connected = FALSE;
2272 struct SessionHandle *data = conn->data;
2274 /* Make sure size is unknown at this point */
2275 data->req.size = -1;
2277 /* Set the progress data */
2278 Curl_pgrsSetUploadCounter(data, 0);
2279 Curl_pgrsSetDownloadCounter(data, 0);
2280 Curl_pgrsSetUploadSize(data, 0);
2281 Curl_pgrsSetDownloadSize(data, 0);
2283 /* Carry out the perform */
2284 result = imap_perform(conn, &connected, dophase_done);
2286 /* Perform post DO phase operations if necessary */
2287 if(!result && *dophase_done)
2288 result = imap_dophase_done(conn, connected);
2293 static CURLcode imap_setup_connection(struct connectdata *conn)
2295 struct SessionHandle *data = conn->data;
2297 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2298 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2299 switch and use HTTP operations only */
2300 #ifndef CURL_DISABLE_HTTP
2301 if(conn->handler == &Curl_handler_imap)
2302 conn->handler = &Curl_handler_imap_proxy;
2305 conn->handler = &Curl_handler_imaps_proxy;
2307 failf(data, "IMAPS not supported!");
2308 return CURLE_UNSUPPORTED_PROTOCOL;
2312 /* We explicitly mark this connection as persistent here as we're doing
2313 IMAP over HTTP and thus we accidentally avoid setting this value
2315 conn->bits.close = FALSE;
2317 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2318 return CURLE_UNSUPPORTED_PROTOCOL;
2322 data->state.path++; /* don't include the initial slash */
2327 #endif /* CURL_DISABLE_IMAP */