1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * RFC2195 CRAM-MD5 authentication
22 * RFC2595 Using TLS with IMAP, POP3 and ACAP
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3501 IMAPv4 protocol
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4959 IMAP Extension for SASL Initial Client Response
28 * RFC5092 IMAP URL Scheme
30 ***************************************************************************/
32 #include "curl_setup.h"
34 #ifndef CURL_DISABLE_IMAP
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
43 #include <sys/utsname.h>
53 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
55 #define in_addr_t unsigned long
58 #include <curl/curl.h>
66 #include "http.h" /* for HTTP proxy tunnel stuff */
70 #include "strtoofft.h"
79 #include "curl_sasl.h"
81 #define _MPRINTF_REPLACE /* use our functions only */
82 #include <curl/mprintf.h>
84 #include "curl_memory.h"
85 /* The last #include file should be: */
88 /* Local API functions */
89 static CURLcode imap_parse_url_path(struct connectdata *conn);
90 static CURLcode imap_parse_custom_request(struct connectdata *conn);
91 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
92 static CURLcode imap_do(struct connectdata *conn, bool *done);
93 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
95 static CURLcode imap_connect(struct connectdata *conn, bool *done);
96 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
97 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
98 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
100 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
101 static CURLcode imap_setup_connection(struct connectdata *conn);
104 * IMAP protocol handler.
107 const struct Curl_handler Curl_handler_imap = {
109 imap_setup_connection, /* setup_connection */
111 imap_done, /* done */
112 ZERO_NULL, /* do_more */
113 imap_connect, /* connect_it */
114 imap_multi_statemach, /* connecting */
115 imap_doing, /* doing */
116 imap_getsock, /* proto_getsock */
117 imap_getsock, /* doing_getsock */
118 ZERO_NULL, /* domore_getsock */
119 ZERO_NULL, /* perform_getsock */
120 imap_disconnect, /* disconnect */
121 ZERO_NULL, /* readwrite */
122 PORT_IMAP, /* defport */
123 CURLPROTO_IMAP, /* protocol */
124 PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
125 | PROTOPT_NOURLQUERY /* flags */
130 * IMAPS protocol handler.
133 const struct Curl_handler Curl_handler_imaps = {
134 "IMAPS", /* scheme */
135 imap_setup_connection, /* setup_connection */
137 imap_done, /* done */
138 ZERO_NULL, /* do_more */
139 imap_connect, /* connect_it */
140 imap_multi_statemach, /* connecting */
141 imap_doing, /* doing */
142 imap_getsock, /* proto_getsock */
143 imap_getsock, /* doing_getsock */
144 ZERO_NULL, /* domore_getsock */
145 ZERO_NULL, /* perform_getsock */
146 imap_disconnect, /* disconnect */
147 ZERO_NULL, /* readwrite */
148 PORT_IMAPS, /* defport */
149 CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
150 PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
151 | PROTOPT_NOURLQUERY /* flags */
155 #ifndef CURL_DISABLE_HTTP
157 * HTTP-proxyed IMAP protocol handler.
160 static const struct Curl_handler Curl_handler_imap_proxy = {
162 ZERO_NULL, /* setup_connection */
163 Curl_http, /* do_it */
164 Curl_http_done, /* done */
165 ZERO_NULL, /* do_more */
166 ZERO_NULL, /* connect_it */
167 ZERO_NULL, /* connecting */
168 ZERO_NULL, /* doing */
169 ZERO_NULL, /* proto_getsock */
170 ZERO_NULL, /* doing_getsock */
171 ZERO_NULL, /* domore_getsock */
172 ZERO_NULL, /* perform_getsock */
173 ZERO_NULL, /* disconnect */
174 ZERO_NULL, /* readwrite */
175 PORT_IMAP, /* defport */
176 CURLPROTO_HTTP, /* protocol */
177 PROTOPT_NONE /* flags */
182 * HTTP-proxyed IMAPS protocol handler.
185 static const struct Curl_handler Curl_handler_imaps_proxy = {
186 "IMAPS", /* scheme */
187 ZERO_NULL, /* setup_connection */
188 Curl_http, /* do_it */
189 Curl_http_done, /* done */
190 ZERO_NULL, /* do_more */
191 ZERO_NULL, /* connect_it */
192 ZERO_NULL, /* connecting */
193 ZERO_NULL, /* doing */
194 ZERO_NULL, /* proto_getsock */
195 ZERO_NULL, /* doing_getsock */
196 ZERO_NULL, /* domore_getsock */
197 ZERO_NULL, /* perform_getsock */
198 ZERO_NULL, /* disconnect */
199 ZERO_NULL, /* readwrite */
200 PORT_IMAPS, /* defport */
201 CURLPROTO_HTTP, /* protocol */
202 PROTOPT_NONE /* flags */
208 static void imap_to_imaps(struct connectdata *conn)
210 conn->handler = &Curl_handler_imaps;
213 #define imap_to_imaps(x) Curl_nop_stmt
216 /***********************************************************************
220 * Sends the formated string as an IMAP command to the server.
222 * Designed to never block.
224 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
226 CURLcode result = CURLE_OK;
227 struct imap_conn *imapc = &conn->proto.imapc;
232 /* Calculate the next command ID wrapping at 3 digits */
233 imapc->cmdid = (imapc->cmdid + 1) % 1000;
235 /* Calculate the tag based on the connection ID and command ID */
236 snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
237 'A' + (conn->connection_id % 26), imapc->cmdid);
239 taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
241 return CURLE_OUT_OF_MEMORY;
243 result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
245 Curl_safefree(taggedfmt);
251 /***********************************************************************
255 * Checks the input string for characters that need escaping and returns an
256 * atom ready for sending to the server.
258 * The returned string needs to be freed.
261 static char *imap_atom(const char *str)
265 size_t backsp_count = 0;
266 size_t quote_count = 0;
267 bool space_exists = FALSE;
274 /* Count any unescapped characters */
287 /* Does the input contain any unescapped characters? */
288 if(!backsp_count && !quote_count && !space_exists)
291 /* Calculate the new string length */
292 newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
294 /* Allocate the new string */
295 newstr = (char *) malloc((newlen + 1) * sizeof(char));
299 /* Surround the string in quotes if necessary */
303 newstr[newlen - 1] = '"';
307 /* Copy the string, escaping backslash and quote characters along the way */
310 if(*p1 == '\\' || *p1 == '"') {
321 /* Terminate the string */
322 newstr[newlen] = '\0';
327 /* Determines whether the untagged response is related to a specified
328 command by checking if it is in format "* <command-name> ..." or
329 "* <number> <command-name> ...". The "* " marker is assumed to have
330 already been checked by the caller. */
331 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
333 const char *end = line + len;
334 size_t cmd_len = strlen(cmd);
336 /* Skip the untagged response marker */
339 /* Do we have a number after the marker? */
340 if(line < end && ISDIGIT(*line)) {
341 /* Skip the number */
344 while(line < end && ISDIGIT(*line));
346 /* Do we have the space character? */
347 if(line == end || *line != ' ')
353 /* Does the command name match and is it followed by a space character or at
355 if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
356 (line[cmd_len] == ' ' || line + cmd_len == end))
362 /* Function that checks whether the given string is a valid tagged, untagged
363 or continuation response which can be processed by the response handler. */
364 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
367 struct IMAP *imap = conn->data->state.proto.imap;
368 struct imap_conn *imapc = &conn->proto.imapc;
369 const char *id = imapc->resptag;
370 size_t id_len = strlen(id);
372 /* Do we have a tagged command response? */
373 if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
377 if(len >= 2 && !memcmp(line, "OK", 2))
379 else if(len >= 2 && !memcmp(line, "NO", 2))
381 else if(len >= 3 && !memcmp(line, "BAD", 3))
384 failf(conn->data, "Bad tagged response");
391 /* Do we have an untagged command response? */
392 if(len >= 2 && !memcmp("* ", line, 2)) {
393 switch(imapc->state) {
394 /* States which are interested in untagged responses */
395 case IMAP_CAPABILITY:
396 if(!imap_matchresp(line, len, "CAPABILITY"))
401 if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
402 (imap->custom && !imap_matchresp(line, len, imap->custom) &&
403 (strcmp(imap->custom, "STORE") ||
404 !imap_matchresp(line, len, "FETCH")) &&
405 strcmp(imap->custom, "SELECT") &&
406 strcmp(imap->custom, "EXAMINE")))
411 /* SELECT is special in that its untagged responses do not have a
412 common prefix so accept anything! */
416 if(!imap_matchresp(line, len, "FETCH"))
420 /* Ignore other untagged responses */
429 /* Do we have a continuation response? */
430 if((len == 3 && !memcmp("+", line, 1)) ||
431 (len >= 2 && !memcmp("+ ", line, 2))) {
432 switch(imapc->state) {
433 /* States which are interested in continuation responses */
434 case IMAP_AUTHENTICATE_PLAIN:
435 case IMAP_AUTHENTICATE_LOGIN:
436 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
437 case IMAP_AUTHENTICATE_CRAMMD5:
438 case IMAP_AUTHENTICATE_DIGESTMD5:
439 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
440 case IMAP_AUTHENTICATE_NTLM:
441 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
442 case IMAP_AUTHENTICATE_FINAL:
448 failf(conn->data, "Unexpected continuation response");
456 return FALSE; /* Nothing for us */
459 /* This is the ONLY way to change IMAP state! */
460 static void state(struct connectdata *conn, imapstate newstate)
462 struct imap_conn *imapc = &conn->proto.imapc;
463 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
464 /* for debug purposes */
465 static const char * const names[]={
471 "AUTHENTICATE_PLAIN",
472 "AUTHENTICATE_LOGIN",
473 "AUTHENTICATE_LOGIN_PASSWD",
474 "AUTHENTICATE_CRAMMD5",
475 "AUTHENTICATE_DIGESTMD5",
476 "AUTHENTICATE_DIGESTMD5_RESP",
478 "AUTHENTICATE_NTLM_TYPE2MSG",
479 "AUTHENTICATE_FINAL",
491 if(imapc->state != newstate)
492 infof(conn->data, "IMAP %p state change from %s to %s\n",
493 imapc, names[imapc->state], names[newstate]);
496 imapc->state = newstate;
499 /***********************************************************************
501 * imap_state_capability()
503 * Sends the CAPABILITY command in order to obtain a list of server side
504 * supported capabilities.
506 static CURLcode imap_state_capability(struct connectdata *conn)
508 CURLcode result = CURLE_OK;
509 struct imap_conn *imapc = &conn->proto.imapc;
511 imapc->authmechs = 0; /* No known authentication mechanisms yet */
512 imapc->authused = 0; /* Clear the authentication mechanism used */
513 imapc->tls_supported = FALSE; /* Clear the TLS capability */
515 /* Send the CAPABILITY command */
516 result = imap_sendf(conn, "CAPABILITY");
519 state(conn, IMAP_CAPABILITY);
524 /***********************************************************************
526 * imap_state_starttls()
528 * Sends the STARTTLS command to start the upgrade to TLS.
530 static CURLcode imap_state_starttls(struct connectdata *conn)
532 CURLcode result = CURLE_OK;
534 /* Send the STARTTLS command */
535 result = imap_sendf(conn, "STARTTLS");
538 state(conn, IMAP_STARTTLS);
543 /***********************************************************************
545 * imap_state_upgrade_tls()
547 * Performs the upgrade to TLS.
549 static CURLcode imap_state_upgrade_tls(struct connectdata *conn)
551 CURLcode result = CURLE_OK;
552 struct imap_conn *imapc = &conn->proto.imapc;
554 /* Start the SSL connection */
555 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
558 if(imapc->state != IMAP_UPGRADETLS)
559 state(conn, IMAP_UPGRADETLS);
563 result = imap_state_capability(conn);
570 /***********************************************************************
574 * Sends a clear text LOGIN command to authenticate with.
576 static CURLcode imap_state_login(struct connectdata *conn)
578 CURLcode result = CURLE_OK;
582 /* Check we have a username and password to authenticate with and end the
583 connect phase if we don't */
584 if(!conn->bits.user_passwd) {
585 state(conn, IMAP_STOP);
590 /* Make sure the username and password are in the correct atom format */
591 user = imap_atom(conn->user);
592 passwd = imap_atom(conn->passwd);
594 /* Send the LOGIN command */
595 result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
596 passwd ? passwd : "");
599 Curl_safefree(passwd);
602 state(conn, IMAP_LOGIN);
607 /***********************************************************************
609 * imap_authenticate()
611 * Sends an AUTHENTICATE command allowing the client to login with the
612 * appropriate SASL authentication mechanism.
614 * Additionally, the function will perform fallback to the LOGIN command
615 * should a common mechanism not be available between the client and server.
617 static CURLcode imap_authenticate(struct connectdata *conn)
619 CURLcode result = CURLE_OK;
620 struct imap_conn *imapc = &conn->proto.imapc;
621 const char *mech = NULL;
622 char *initresp = NULL;
624 imapstate state1 = IMAP_STOP;
625 imapstate state2 = IMAP_STOP;
627 /* Check we have a username and password to authenticate with and end the
628 connect phase if we don't */
629 if(!conn->bits.user_passwd) {
630 state(conn, IMAP_STOP);
635 /* Calculate the supported authentication mechanism by decreasing order of
637 #ifndef CURL_DISABLE_CRYPTO_AUTH
638 if(imapc->authmechs & SASL_MECH_DIGEST_MD5) {
640 state1 = IMAP_AUTHENTICATE_DIGESTMD5;
641 imapc->authused = SASL_MECH_DIGEST_MD5;
643 else if(imapc->authmechs & SASL_MECH_CRAM_MD5) {
645 state1 = IMAP_AUTHENTICATE_CRAMMD5;
646 imapc->authused = SASL_MECH_CRAM_MD5;
651 if(imapc->authmechs & SASL_MECH_NTLM) {
653 state1 = IMAP_AUTHENTICATE_NTLM;
654 state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
655 imapc->authused = SASL_MECH_NTLM;
657 if(imapc->ir_supported)
658 result = Curl_sasl_create_login_message(conn->data, conn->user,
663 if(imapc->authmechs & SASL_MECH_LOGIN) {
665 state1 = IMAP_AUTHENTICATE_LOGIN;
666 state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
667 imapc->authused = SASL_MECH_LOGIN;
669 if(imapc->ir_supported)
670 result = Curl_sasl_create_plain_message(conn->data, conn->user,
671 conn->passwd, &initresp, &len);
673 else if(imapc->authmechs & SASL_MECH_PLAIN) {
675 state1 = IMAP_AUTHENTICATE_PLAIN;
676 state2 = IMAP_AUTHENTICATE_FINAL;
677 imapc->authused = SASL_MECH_PLAIN;
679 if(imapc->ir_supported)
680 result = Curl_sasl_create_plain_message(conn->data, conn->user,
681 conn->passwd, &initresp, &len);
689 /* Perform SASL based authentication */
690 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
696 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
702 Curl_safefree(initresp);
704 else if(!imapc->login_disabled)
705 /* Perform clear text authentication */
706 result = imap_state_login(conn);
708 /* Other mechanisms not supported */
709 infof(conn->data, "No known authentication mechanisms supported!\n");
710 result = CURLE_LOGIN_DENIED;
716 /***********************************************************************
720 * Sends a LIST command or an alternative custom request.
722 static CURLcode imap_list(struct connectdata *conn)
724 CURLcode result = CURLE_OK;
725 struct SessionHandle *data = conn->data;
726 struct IMAP *imap = data->state.proto.imap;
730 /* Send the custom request */
731 result = imap_sendf(conn, "%s%s", imap->custom,
732 imap->custom_params ? imap->custom_params : "");
734 /* Make sure the mailbox is in the correct atom format */
735 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
737 return CURLE_OUT_OF_MEMORY;
739 /* Send the LIST command */
740 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
742 Curl_safefree(mailbox);
746 state(conn, IMAP_LIST);
751 /***********************************************************************
755 * Sends a SELECT command to ask the server to change the selected mailbox.
757 static CURLcode imap_select(struct connectdata *conn)
759 CURLcode result = CURLE_OK;
760 struct SessionHandle *data = conn->data;
761 struct IMAP *imap = data->state.proto.imap;
762 struct imap_conn *imapc = &conn->proto.imapc;
765 /* Invalidate old information as we are switching mailboxes */
766 Curl_safefree(imapc->mailbox);
767 Curl_safefree(imapc->mailbox_uidvalidity);
769 /* Check we have a mailbox */
771 failf(conn->data, "Cannot SELECT without a mailbox.");
772 return CURLE_URL_MALFORMAT;
775 /* Make sure the mailbox is in the correct atom format */
776 mailbox = imap_atom(imap->mailbox);
778 return CURLE_OUT_OF_MEMORY;
780 /* Send the SELECT command */
781 result = imap_sendf(conn, "SELECT %s", mailbox);
783 Curl_safefree(mailbox);
786 state(conn, IMAP_SELECT);
791 /***********************************************************************
795 * Sends a FETCH command to initiate the download of a message.
797 static CURLcode imap_fetch(struct connectdata *conn)
799 CURLcode result = CURLE_OK;
800 struct IMAP *imap = conn->data->state.proto.imap;
802 /* Check we have a UID */
804 failf(conn->data, "Cannot FETCH without a UID.");
805 return CURLE_URL_MALFORMAT;
808 /* Send the FETCH command */
809 result = imap_sendf(conn, "FETCH %s BODY[%s]",
811 imap->section ? imap->section : "");
814 state(conn, IMAP_FETCH);
819 /***********************************************************************
823 * Sends an APPEND command to initiate the upload of a message.
825 static CURLcode imap_append(struct connectdata *conn)
827 CURLcode result = CURLE_OK;
828 struct IMAP *imap = conn->data->state.proto.imap;
831 /* Check we have a mailbox */
833 failf(conn->data, "Cannot APPEND without a mailbox.");
834 return CURLE_URL_MALFORMAT;
837 /* Check we know the size of the upload */
838 if(conn->data->set.infilesize < 0) {
839 failf(conn->data, "Cannot APPEND with unknown input file size\n");
840 return CURLE_UPLOAD_FAILED;
843 /* Make sure the mailbox is in the correct atom format */
844 mailbox = imap_atom(imap->mailbox);
846 return CURLE_OUT_OF_MEMORY;
848 /* Send the APPEND command */
849 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
850 mailbox, conn->data->set.infilesize);
852 Curl_safefree(mailbox);
855 state(conn, IMAP_APPEND);
860 /***********************************************************************
864 * Performs the logout action prior to sclose() being called.
866 static CURLcode imap_logout(struct connectdata *conn)
868 CURLcode result = CURLE_OK;
870 /* Send the LOGOUT command */
871 result = imap_sendf(conn, "LOGOUT");
874 state(conn, IMAP_LOGOUT);
879 /* For the initial server greeting */
880 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
884 CURLcode result = CURLE_OK;
885 struct SessionHandle *data = conn->data;
887 (void)instate; /* no use for this yet */
889 if(imapcode != 'O') {
890 failf(data, "Got unexpected imap-server response");
891 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
894 result = imap_state_capability(conn);
899 /* For CAPABILITY responses */
900 static CURLcode imap_state_capability_resp(struct connectdata *conn,
904 CURLcode result = CURLE_OK;
905 struct SessionHandle *data = conn->data;
906 struct imap_conn *imapc = &conn->proto.imapc;
907 const char *line = data->state.buffer;
910 (void)instate; /* no use for this yet */
912 /* Do we have a untagged response? */
913 if(imapcode == '*') {
916 /* Loop through the data line */
919 (*line == ' ' || *line == '\t' ||
920 *line == '\r' || *line == '\n')) {
928 /* Extract the word */
929 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
930 line[wordlen] != '\t' && line[wordlen] != '\r' &&
931 line[wordlen] != '\n';)
934 /* Does the server support the STARTTLS capability? */
935 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
936 imapc->tls_supported = TRUE;
938 /* Has the server explicitly disabled clear text authentication? */
939 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
940 imapc->login_disabled = TRUE;
942 /* Does the server support the SASL-IR capability? */
943 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
944 imapc->ir_supported = TRUE;
946 /* Do we have a SASL based authentication mechanism? */
947 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
951 /* Test the word for a matching authentication mechanism */
952 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
953 imapc->authmechs |= SASL_MECH_LOGIN;
954 if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
955 imapc->authmechs |= SASL_MECH_PLAIN;
956 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
957 imapc->authmechs |= SASL_MECH_CRAM_MD5;
958 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
959 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
960 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
961 imapc->authmechs |= SASL_MECH_GSSAPI;
962 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
963 imapc->authmechs |= SASL_MECH_EXTERNAL;
964 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
965 imapc->authmechs |= SASL_MECH_NTLM;
971 else if(imapcode == 'O') {
972 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
973 /* We don't have a SSL/TLS connection yet, but SSL is requested */
974 if(imapc->tls_supported)
975 /* Switch to TLS connection now */
976 result = imap_state_starttls(conn);
977 else if(data->set.use_ssl == CURLUSESSL_TRY)
978 /* Fallback and carry on with authentication */
979 result = imap_authenticate(conn);
981 failf(data, "STARTTLS not supported.");
982 result = CURLE_USE_SSL_FAILED;
986 result = imap_authenticate(conn);
989 result = imap_state_login(conn);
994 /* For STARTTLS responses */
995 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
999 CURLcode result = CURLE_OK;
1000 struct SessionHandle *data = conn->data;
1002 (void)instate; /* no use for this yet */
1004 if(imapcode != 'O') {
1005 if(data->set.use_ssl != CURLUSESSL_TRY) {
1006 failf(data, "STARTTLS denied. %c", imapcode);
1007 result = CURLE_USE_SSL_FAILED;
1010 result = imap_authenticate(conn);
1013 result = imap_state_upgrade_tls(conn);
1018 /* For AUTHENTICATE PLAIN (without initial response) responses */
1019 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
1023 CURLcode result = CURLE_OK;
1024 struct SessionHandle *data = conn->data;
1026 char *plainauth = NULL;
1028 (void)instate; /* no use for this yet */
1030 if(imapcode != '+') {
1031 failf(data, "Access denied. %c", imapcode);
1032 result = CURLE_LOGIN_DENIED;
1035 /* Create the authorisation message */
1036 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
1039 /* Send the message */
1042 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
1045 state(conn, IMAP_AUTHENTICATE_FINAL);
1048 Curl_safefree(plainauth);
1055 /* For AUTHENTICATE LOGIN (without initial response) responses */
1056 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1060 CURLcode result = CURLE_OK;
1061 struct SessionHandle *data = conn->data;
1063 char *authuser = NULL;
1065 (void)instate; /* no use for this yet */
1067 if(imapcode != '+') {
1068 failf(data, "Access denied: %d", imapcode);
1069 result = CURLE_LOGIN_DENIED;
1072 /* Create the user message */
1073 result = Curl_sasl_create_login_message(data, conn->user,
1079 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1082 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1085 Curl_safefree(authuser);
1092 /* For AUTHENTICATE LOGIN user entry responses */
1093 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1097 CURLcode result = CURLE_OK;
1098 struct SessionHandle *data = conn->data;
1100 char *authpasswd = NULL;
1102 (void)instate; /* no use for this yet */
1104 if(imapcode != '+') {
1105 failf(data, "Access denied: %d", imapcode);
1106 result = CURLE_LOGIN_DENIED;
1109 /* Create the password message */
1110 result = Curl_sasl_create_login_message(data, conn->passwd,
1113 /* Send the password */
1116 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1119 state(conn, IMAP_AUTHENTICATE_FINAL);
1122 Curl_safefree(authpasswd);
1129 #ifndef CURL_DISABLE_CRYPTO_AUTH
1130 /* For AUTHENTICATE CRAM-MD5 responses */
1131 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1135 CURLcode result = CURLE_OK;
1136 struct SessionHandle *data = conn->data;
1137 char *chlg64 = data->state.buffer;
1139 char *rplyb64 = NULL;
1141 (void)instate; /* no use for this yet */
1143 if(imapcode != '+') {
1144 failf(data, "Access denied: %d", imapcode);
1145 return CURLE_LOGIN_DENIED;
1148 /* Get the challenge */
1149 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1152 /* Terminate the challenge */
1153 if(*chlg64 != '=') {
1154 for(len = strlen(chlg64); len--;)
1155 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
1156 chlg64[len] != '\t')
1164 /* Create the response message */
1165 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
1166 conn->passwd, &rplyb64, &len);
1168 /* Send the response */
1171 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1174 state(conn, IMAP_AUTHENTICATE_FINAL);
1177 Curl_safefree(rplyb64);
1183 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1184 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1188 CURLcode result = CURLE_OK;
1189 struct SessionHandle *data = conn->data;
1190 char *chlg64 = data->state.buffer;
1192 char *rplyb64 = NULL;
1194 (void)instate; /* no use for this yet */
1196 if(imapcode != '+') {
1197 failf(data, "Access denied: %d", imapcode);
1198 return CURLE_LOGIN_DENIED;
1201 /* Get the challenge */
1202 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1205 /* Create the response message */
1206 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
1207 conn->passwd, "imap",
1210 /* Send the response */
1213 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1216 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1219 Curl_safefree(rplyb64);
1225 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1226 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1230 CURLcode result = CURLE_OK;
1231 struct SessionHandle *data = conn->data;
1233 (void)instate; /* no use for this yet */
1235 if(imapcode != '+') {
1236 failf(data, "Authentication failed: %d", imapcode);
1237 result = CURLE_LOGIN_DENIED;
1240 /* Send an empty response */
1241 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1244 state(conn, IMAP_AUTHENTICATE_FINAL);
1252 /* For AUTHENTICATE NTLM (without initial response) responses */
1253 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1257 CURLcode result = CURLE_OK;
1258 struct SessionHandle *data = conn->data;
1260 char *type1msg = NULL;
1262 (void)instate; /* no use for this yet */
1264 if(imapcode != '+') {
1265 failf(data, "Access denied: %d", imapcode);
1266 result = CURLE_LOGIN_DENIED;
1269 /* Create the type-1 message */
1270 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1274 /* Send the message */
1277 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1280 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1283 Curl_safefree(type1msg);
1290 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1291 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1295 CURLcode result = CURLE_OK;
1296 struct SessionHandle *data = conn->data;
1298 char *type3msg = NULL;
1300 (void)instate; /* no use for this yet */
1302 if(imapcode != '+') {
1303 failf(data, "Access denied: %d", imapcode);
1304 result = CURLE_LOGIN_DENIED;
1307 /* Create the type-3 message */
1308 result = Curl_sasl_create_ntlm_type3_message(data,
1309 data->state.buffer + 2,
1310 conn->user, conn->passwd,
1314 /* Send the message */
1317 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1320 state(conn, IMAP_AUTHENTICATE_FINAL);
1323 Curl_safefree(type3msg);
1331 /* For final responses to the AUTHENTICATE sequence */
1332 static CURLcode imap_state_auth_final_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, "Authentication failed: %d", imapcode);
1343 result = CURLE_LOGIN_DENIED;
1346 /* End of connect phase */
1347 state(conn, IMAP_STOP);
1352 /* For LOGIN responses */
1353 static CURLcode imap_state_login_resp(struct connectdata *conn,
1357 CURLcode result = CURLE_OK;
1358 struct SessionHandle *data = conn->data;
1360 (void)instate; /* no use for this yet */
1362 if(imapcode != 'O') {
1363 failf(data, "Access denied. %c", imapcode);
1364 result = CURLE_LOGIN_DENIED;
1367 /* End of connect phase */
1368 state(conn, IMAP_STOP);
1373 /* For LIST responses */
1374 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1377 CURLcode result = CURLE_OK;
1378 char *line = conn->data->state.buffer;
1379 size_t len = strlen(line);
1381 (void)instate; /* No use for this yet */
1383 if(imapcode == '*') {
1384 /* Temporarily add the LF character back and send as body to the client */
1386 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1389 else if(imapcode != 'O')
1390 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1392 /* End of DO phase */
1393 state(conn, IMAP_STOP);
1398 /* For SELECT responses */
1399 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1402 CURLcode result = CURLE_OK;
1403 struct SessionHandle *data = conn->data;
1404 struct IMAP *imap = conn->data->state.proto.imap;
1405 struct imap_conn *imapc = &conn->proto.imapc;
1406 const char *line = data->state.buffer;
1409 (void)instate; /* no use for this yet */
1411 if(imapcode == '*') {
1412 /* See if this is an UIDVALIDITY response */
1413 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1414 Curl_safefree(imapc->mailbox_uidvalidity);
1415 imapc->mailbox_uidvalidity = strdup(tmp);
1418 else if(imapcode == 'O') {
1419 /* Check if the UIDVALIDITY has been specified and matches */
1420 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1421 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1422 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1423 result = CURLE_REMOTE_FILE_NOT_FOUND;
1426 /* Note the currently opened mailbox on this connection */
1427 imapc->mailbox = strdup(imap->mailbox);
1430 result = imap_list(conn);
1432 result = imap_fetch(conn);
1436 failf(data, "Select failed");
1437 result = CURLE_LOGIN_DENIED;
1443 /* For the (first line of the) FETCH responses */
1444 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1447 CURLcode result = CURLE_OK;
1448 struct SessionHandle *data = conn->data;
1449 struct imap_conn *imapc = &conn->proto.imapc;
1450 struct pingpong *pp = &imapc->pp;
1451 const char *ptr = data->state.buffer;
1452 bool parsed = FALSE;
1455 (void)instate; /* no use for this yet */
1457 if(imapcode != '*') {
1458 Curl_pgrsSetDownloadSize(data, 0);
1459 state(conn, IMAP_STOP);
1460 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1463 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1464 the continuation data contained within the curly brackets */
1465 while(*ptr && (*ptr != '{'))
1470 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1471 if(endptr - ptr > 1 && endptr[0] == '}' &&
1472 endptr[1] == '\r' && endptr[2] == '\0')
1477 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1478 Curl_pgrsSetDownloadSize(data, size);
1481 /* At this point there is a bunch of data in the header "cache" that is
1482 actually body content, send it as body and then skip it. Do note
1483 that there may even be additional "headers" after the body. */
1484 size_t chunk = pp->cache_size;
1486 if(chunk > (size_t)size)
1487 /* The conversion from curl_off_t to size_t is always fine here */
1488 chunk = (size_t)size;
1490 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1494 data->req.bytecount += chunk;
1497 infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1498 " bytes are left for transfer\n", (curl_off_t)chunk, size);
1500 /* Have we used the entire cache or just part of it?*/
1501 if(pp->cache_size > chunk) {
1502 /* Only part of it so shrink the cache to fit the trailing data */
1503 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1504 pp->cache_size -= chunk;
1507 /* Free the cache */
1508 Curl_safefree(pp->cache);
1510 /* Reset the cache size */
1516 /* The entire data is already transferred! */
1517 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1520 data->req.maxdownload = size;
1521 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1525 /* We don't know how to parse this line */
1526 failf(pp->conn->data, "Failed to parse FETCH response.");
1527 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1530 /* End of DO phase */
1531 state(conn, IMAP_STOP);
1536 /* For final FETCH responses performed after the download */
1537 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1541 CURLcode result = CURLE_OK;
1543 (void)instate; /* No use for this yet */
1546 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1548 /* End of DONE phase */
1549 state(conn, IMAP_STOP);
1554 /* For APPEND responses */
1555 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1558 CURLcode result = CURLE_OK;
1559 struct SessionHandle *data = conn->data;
1561 (void)instate; /* No use for this yet */
1563 if(imapcode != '+') {
1564 result = CURLE_UPLOAD_FAILED;
1567 /* Set the progress upload size */
1568 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1571 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1573 /* End of DO phase */
1574 state(conn, IMAP_STOP);
1580 /* For final APPEND responses performed after the upload */
1581 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1585 CURLcode result = CURLE_OK;
1587 (void)instate; /* No use for this yet */
1590 result = CURLE_UPLOAD_FAILED;
1592 /* End of DONE phase */
1593 state(conn, IMAP_STOP);
1598 static CURLcode imap_statemach_act(struct connectdata *conn)
1600 CURLcode result = CURLE_OK;
1601 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1603 struct imap_conn *imapc = &conn->proto.imapc;
1604 struct pingpong *pp = &imapc->pp;
1607 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1608 if(imapc->state == IMAP_UPGRADETLS)
1609 return imap_state_upgrade_tls(conn);
1611 /* Flush any data that needs to be sent */
1613 return Curl_pp_flushsend(pp);
1616 /* Read the response from the server */
1617 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1621 /* Was there an error parsing the response line? */
1623 return CURLE_FTP_WEIRD_SERVER_REPLY;
1628 /* We have now received a full IMAP server response */
1629 switch(imapc->state) {
1630 case IMAP_SERVERGREET:
1631 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1634 case IMAP_CAPABILITY:
1635 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1639 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1642 case IMAP_AUTHENTICATE_PLAIN:
1643 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1646 case IMAP_AUTHENTICATE_LOGIN:
1647 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1650 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1651 result = imap_state_auth_login_password_resp(conn, imapcode,
1655 #ifndef CURL_DISABLE_CRYPTO_AUTH
1656 case IMAP_AUTHENTICATE_CRAMMD5:
1657 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1660 case IMAP_AUTHENTICATE_DIGESTMD5:
1661 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1664 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1665 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1670 case IMAP_AUTHENTICATE_NTLM:
1671 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1674 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1675 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1680 case IMAP_AUTHENTICATE_FINAL:
1681 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1685 result = imap_state_login_resp(conn, imapcode, imapc->state);
1689 result = imap_state_list_resp(conn, imapcode, imapc->state);
1693 result = imap_state_select_resp(conn, imapcode, imapc->state);
1697 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1700 case IMAP_FETCH_FINAL:
1701 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1705 result = imap_state_append_resp(conn, imapcode, imapc->state);
1708 case IMAP_APPEND_FINAL:
1709 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1713 /* fallthrough, just stop! */
1715 /* internal error */
1716 state(conn, IMAP_STOP);
1719 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1724 /* Called repeatedly until done from multi.c */
1725 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1727 CURLcode result = CURLE_OK;
1728 struct imap_conn *imapc = &conn->proto.imapc;
1730 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1731 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1733 result = Curl_pp_statemach(&imapc->pp, FALSE);
1735 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1740 static CURLcode imap_block_statemach(struct connectdata *conn)
1742 CURLcode result = CURLE_OK;
1743 struct imap_conn *imapc = &conn->proto.imapc;
1745 while(imapc->state != IMAP_STOP && !result)
1746 result = Curl_pp_statemach(&imapc->pp, TRUE);
1751 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1753 static CURLcode imap_init(struct connectdata *conn)
1755 struct SessionHandle *data = conn->data;
1756 struct IMAP *imap = data->state.proto.imap;
1759 imap = data->state.proto.imap = calloc(sizeof(struct IMAP), 1);
1761 return CURLE_OUT_OF_MEMORY;
1767 /* For the IMAP "protocol connect" and "doing" phases only */
1768 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1771 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1774 /***********************************************************************
1776 * imap_connect() should do everything that is to be considered a part of
1777 * the connection phase.
1779 * The variable 'done' points to will be TRUE if the protocol-layer connect
1780 * phase is done when this function returns, or FALSE is not. When called as
1781 * a part of the easy interface, it will always be TRUE.
1783 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1785 CURLcode result = CURLE_OK;
1786 struct imap_conn *imapc = &conn->proto.imapc;
1787 struct pingpong *pp = &imapc->pp;
1789 *done = FALSE; /* default to not done yet */
1791 /* If there already is a protocol-specific struct allocated for this
1792 sessionhandle, deal with it */
1793 Curl_reset_reqproto(conn);
1795 /* Initialise the IMAP layer */
1796 result = imap_init(conn);
1800 /* We always support persistent connections in IMAP */
1801 conn->bits.close = FALSE;
1803 /* Set the default response time-out */
1804 pp->response_time = RESP_TIMEOUT;
1805 pp->statemach_act = imap_statemach_act;
1806 pp->endofresp = imap_endofresp;
1809 /* Initialise the pingpong layer */
1812 /* Start off waiting for the server greeting response */
1813 state(conn, IMAP_SERVERGREET);
1815 /* Start off with an response id of '*' */
1816 strcpy(imapc->resptag, "*");
1818 result = imap_multi_statemach(conn, done);
1823 /***********************************************************************
1827 * The DONE function. This does what needs to be done after a single DO has
1830 * Input argument is already checked for validity.
1832 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1835 CURLcode result = CURLE_OK;
1836 struct SessionHandle *data = conn->data;
1837 struct IMAP *imap = data->state.proto.imap;
1842 /* When the easy handle is removed from the multi interface while libcurl
1843 is still trying to resolve the host name, the IMAP struct is not yet
1844 initialized. However, the removal action calls Curl_done() which in
1845 turn calls this function, so we simply return success. */
1849 conn->bits.close = TRUE; /* marked for closure */
1850 result = status; /* use the already set error code */
1852 else if(!data->set.connect_only && !imap->custom &&
1853 (imap->uid || data->set.upload)) {
1854 /* Handle responses after FETCH or APPEND transfer has finished */
1855 if(!data->set.upload)
1856 state(conn, IMAP_FETCH_FINAL);
1858 /* End the APPEND command first by sending an empty line */
1859 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1861 state(conn, IMAP_APPEND_FINAL);
1864 /* Run the state-machine
1866 TODO: when the multi interface is used, this _really_ should be using
1867 the imap_multi_statemach function but we have no general support for
1868 non-blocking DONE operations, not in the multi state machine and with
1869 Curl_done() invokes on several places in the code!
1872 result = imap_block_statemach(conn);
1875 /* Cleanup our per-request based variables */
1876 Curl_safefree(imap->mailbox);
1877 Curl_safefree(imap->uidvalidity);
1878 Curl_safefree(imap->uid);
1879 Curl_safefree(imap->section);
1880 Curl_safefree(imap->custom);
1881 Curl_safefree(imap->custom_params);
1883 /* Clear the transfer mode for the next request */
1884 imap->transfer = FTPTRANSFER_BODY;
1889 /***********************************************************************
1893 * This is the actual DO function for IMAP. Fetch or append a message, or do
1894 * other things according to the options previously setup.
1896 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1899 /* This is IMAP and no proxy */
1900 CURLcode result = CURLE_OK;
1901 struct SessionHandle *data = conn->data;
1902 struct IMAP *imap = data->state.proto.imap;
1903 struct imap_conn *imapc = &conn->proto.imapc;
1904 bool selected = FALSE;
1906 DEBUGF(infof(conn->data, "DO phase starts\n"));
1908 if(conn->data->set.opt_no_body) {
1909 /* Requested no body means no transfer */
1910 imap->transfer = FTPTRANSFER_INFO;
1913 *dophase_done = FALSE; /* not done yet */
1915 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1916 has already been selected on this connection */
1917 if(imap->mailbox && imapc->mailbox &&
1918 !strcmp(imap->mailbox, imapc->mailbox) &&
1919 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1920 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1923 /* Start the first command in the DO phase */
1924 if(conn->data->set.upload)
1925 /* APPEND can be executed directly */
1926 result = imap_append(conn);
1927 else if(imap->custom && (selected || !imap->mailbox))
1928 /* Custom command using the same mailbox or no mailbox */
1929 result = imap_list(conn);
1930 else if(!imap->custom && selected && imap->uid)
1931 /* FETCH from the same mailbox */
1932 result = imap_fetch(conn);
1933 else if(imap->mailbox && !selected && (imap->custom || imap->uid))
1934 /* SELECT the mailbox */
1935 result = imap_select(conn);
1938 result = imap_list(conn);
1943 /* Run the state-machine */
1944 result = imap_multi_statemach(conn, dophase_done);
1946 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1949 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1954 /***********************************************************************
1958 * This function is registered as 'curl_do' function. It decodes the path
1959 * parts etc as a wrapper to the actual DO function (imap_perform).
1961 * The input argument is already checked for validity.
1963 static CURLcode imap_do(struct connectdata *conn, bool *done)
1965 CURLcode result = CURLE_OK;
1967 *done = FALSE; /* default to false */
1969 /* Since connections can be re-used between SessionHandles, there might be a
1970 connection already existing but on a fresh SessionHandle struct. As such
1971 we make sure we have a good IMAP struct to play with. For new connections
1972 the IMAP struct is allocated and setup in the imap_connect() function. */
1973 Curl_reset_reqproto(conn);
1974 result = imap_init(conn);
1978 /* Parse the URL path */
1979 result = imap_parse_url_path(conn);
1983 /* Parse the custom request */
1984 result = imap_parse_custom_request(conn);
1988 result = imap_regular_transfer(conn, done);
1993 /***********************************************************************
1997 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1998 * resources. BLOCKING.
2000 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2002 struct imap_conn *imapc = &conn->proto.imapc;
2004 /* We cannot send quit unconditionally. If this connection is stale or
2005 bad in any way, sending quit and waiting around here will make the
2006 disconnect wait in vain and cause more problems than we need to. */
2008 /* The IMAP session may or may not have been allocated/setup at this
2010 if(!dead_connection && imapc->pp.conn)
2011 if(!imap_logout(conn))
2012 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
2014 /* Disconnect from the server */
2015 Curl_pp_disconnect(&imapc->pp);
2017 /* Cleanup the SASL module */
2018 Curl_sasl_cleanup(conn, imapc->authused);
2020 /* Cleanup our connection based variables */
2021 Curl_safefree(imapc->mailbox);
2022 Curl_safefree(imapc->mailbox_uidvalidity);
2027 /***********************************************************************
2031 * Portable test of whether the specified char is a "bchar" as defined in the
2032 * grammar of RFC-5092.
2034 static bool imap_is_bchar(char ch)
2038 case ':': case '@': case '/':
2039 /* bchar -> achar */
2041 /* bchar -> achar -> uchar -> unreserved */
2042 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2043 case '7': case '8': case '9':
2044 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2045 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2046 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2047 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2048 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2049 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2050 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2051 case 'v': case 'w': case 'x': case 'y': case 'z':
2052 case '-': case '.': case '_': case '~':
2053 /* bchar -> achar -> uchar -> sub-delims-sh */
2054 case '!': case '$': case '\'': case '(': case ')': case '*':
2056 /* bchar -> achar -> uchar -> pct-encoded */
2057 case '%': /* HEXDIG chars are already included above */
2065 /***********************************************************************
2067 * imap_parse_url_path()
2069 * Parse the URL path into separate path components.
2072 static CURLcode imap_parse_url_path(struct connectdata *conn)
2074 /* The imap struct is already initialised in imap_connect() */
2075 CURLcode result = CURLE_OK;
2076 struct SessionHandle *data = conn->data;
2077 struct IMAP *imap = data->state.proto.imap;
2078 const char *begin = data->state.path;
2079 const char *ptr = begin;
2081 /* See how much of the URL is a valid path and decode it */
2082 while(imap_is_bchar(*ptr))
2086 /* Remove the trailing slash if present */
2087 const char *end = ptr;
2088 if(end > begin && end[-1] == '/')
2091 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2097 imap->mailbox = NULL;
2099 /* There can be any number of parameters in the form ";NAME=VALUE" */
2100 while(*ptr == ';') {
2105 /* Find the length of the name parameter */
2107 while(*ptr && *ptr != '=')
2111 return CURLE_URL_MALFORMAT;
2113 /* Decode the name parameter */
2114 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2118 /* Find the length of the value parameter */
2120 while(imap_is_bchar(*ptr))
2123 /* Decode the value parameter */
2124 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2126 Curl_safefree(name);
2130 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2132 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2133 stripping of the trailing slash character if it is present.
2135 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2136 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2137 if(valuelen > 0 && value[valuelen - 1] == '/')
2138 value[valuelen - 1] = '\0';
2140 imap->uidvalidity = value;
2143 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2144 if(valuelen > 0 && value[valuelen - 1] == '/')
2145 value[valuelen - 1] = '\0';
2150 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2151 if(valuelen > 0 && value[valuelen - 1] == '/')
2152 value[valuelen - 1] = '\0';
2154 imap->section = value;
2158 Curl_safefree(name);
2159 Curl_safefree(value);
2161 return CURLE_URL_MALFORMAT;
2164 Curl_safefree(name);
2165 Curl_safefree(value);
2168 /* Any extra stuff at the end of the URL is an error */
2170 return CURLE_URL_MALFORMAT;
2175 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2177 CURLcode result = CURLE_OK;
2178 struct SessionHandle *data = conn->data;
2179 struct IMAP *imap = data->state.proto.imap;
2180 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2183 /* URL decode the custom request */
2184 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2186 /* Extract the parameters if specified */
2188 const char *params = imap->custom;
2190 while(*params && *params != ' ')
2194 imap->custom_params = strdup(params);
2195 imap->custom[params - imap->custom] = '\0';
2197 if(!imap->custom_params)
2198 result = CURLE_OUT_OF_MEMORY;
2206 /* Call this when the DO phase has completed */
2207 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2209 struct IMAP *imap = conn->data->state.proto.imap;
2213 if(imap->transfer != FTPTRANSFER_BODY)
2214 /* no data to transfer */
2215 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2220 /* Called from multi.c while DOing */
2221 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2223 CURLcode result = imap_multi_statemach(conn, dophase_done);
2226 DEBUGF(infof(conn->data, "DO phase failed\n"));
2227 else if(*dophase_done) {
2228 result = imap_dophase_done(conn, FALSE /* not connected */);
2230 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2236 /***********************************************************************
2238 * imap_regular_transfer()
2240 * The input argument is already checked for validity.
2242 * Performs all commands done before a regular transfer between a local and a
2245 static CURLcode imap_regular_transfer(struct connectdata *conn,
2248 CURLcode result = CURLE_OK;
2249 bool connected = FALSE;
2250 struct SessionHandle *data = conn->data;
2252 /* Make sure size is unknown at this point */
2253 data->req.size = -1;
2255 /* Set the progress data */
2256 Curl_pgrsSetUploadCounter(data, 0);
2257 Curl_pgrsSetDownloadCounter(data, 0);
2258 Curl_pgrsSetUploadSize(data, 0);
2259 Curl_pgrsSetDownloadSize(data, 0);
2261 /* Carry out the perform */
2262 result = imap_perform(conn, &connected, dophase_done);
2264 /* Perform post DO phase operations if necessary */
2265 if(!result && *dophase_done)
2266 result = imap_dophase_done(conn, connected);
2271 static CURLcode imap_setup_connection(struct connectdata *conn)
2273 struct SessionHandle *data = conn->data;
2275 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2276 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2277 switch and use HTTP operations only */
2278 #ifndef CURL_DISABLE_HTTP
2279 if(conn->handler == &Curl_handler_imap)
2280 conn->handler = &Curl_handler_imap_proxy;
2283 conn->handler = &Curl_handler_imaps_proxy;
2285 failf(data, "IMAPS not supported!");
2286 return CURLE_UNSUPPORTED_PROTOCOL;
2290 /* We explicitly mark this connection as persistent here as we're doing
2291 IMAP over HTTP and thus we accidentally avoid setting this value
2293 conn->bits.close = FALSE;
2295 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2296 return CURLE_UNSUPPORTED_PROTOCOL;
2300 data->state.path++; /* don't include the initial slash */
2305 #endif /* CURL_DISABLE_IMAP */