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_login_message(conn->data, conn->user,
681 if(imapc->authmechs & SASL_MECH_LOGIN) {
683 state1 = IMAP_AUTHENTICATE_LOGIN;
684 state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
685 imapc->authused = SASL_MECH_LOGIN;
687 if(imapc->ir_supported)
688 result = Curl_sasl_create_plain_message(conn->data, conn->user,
689 conn->passwd, &initresp, &len);
691 else if(imapc->authmechs & SASL_MECH_PLAIN) {
693 state1 = IMAP_AUTHENTICATE_PLAIN;
694 state2 = IMAP_AUTHENTICATE_FINAL;
695 imapc->authused = SASL_MECH_PLAIN;
697 if(imapc->ir_supported)
698 result = Curl_sasl_create_plain_message(conn->data, conn->user,
699 conn->passwd, &initresp, &len);
707 /* Perform SASL based authentication */
708 result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
714 result = imap_sendf(conn, "AUTHENTICATE %s", mech);
720 Curl_safefree(initresp);
722 else if(!imapc->login_disabled)
723 /* Perform clear text authentication */
724 result = imap_perform_login(conn);
726 /* Other mechanisms not supported */
727 infof(conn->data, "No known authentication mechanisms supported!\n");
728 result = CURLE_LOGIN_DENIED;
734 /***********************************************************************
736 * imap_perform_list()
738 * Sends a LIST command or an alternative custom request.
740 static CURLcode imap_perform_list(struct connectdata *conn)
742 CURLcode result = CURLE_OK;
743 struct SessionHandle *data = conn->data;
744 struct IMAP *imap = data->state.proto.imap;
748 /* Send the custom request */
749 result = imap_sendf(conn, "%s%s", imap->custom,
750 imap->custom_params ? imap->custom_params : "");
752 /* Make sure the mailbox is in the correct atom format */
753 mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
755 return CURLE_OUT_OF_MEMORY;
757 /* Send the LIST command */
758 result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
760 Curl_safefree(mailbox);
764 state(conn, IMAP_LIST);
769 /***********************************************************************
771 * imap_perform_select()
773 * Sends a SELECT command to ask the server to change the selected mailbox.
775 static CURLcode imap_perform_select(struct connectdata *conn)
777 CURLcode result = CURLE_OK;
778 struct SessionHandle *data = conn->data;
779 struct IMAP *imap = data->state.proto.imap;
780 struct imap_conn *imapc = &conn->proto.imapc;
783 /* Invalidate old information as we are switching mailboxes */
784 Curl_safefree(imapc->mailbox);
785 Curl_safefree(imapc->mailbox_uidvalidity);
787 /* Check we have a mailbox */
789 failf(conn->data, "Cannot SELECT without a mailbox.");
790 return CURLE_URL_MALFORMAT;
793 /* Make sure the mailbox is in the correct atom format */
794 mailbox = imap_atom(imap->mailbox);
796 return CURLE_OUT_OF_MEMORY;
798 /* Send the SELECT command */
799 result = imap_sendf(conn, "SELECT %s", mailbox);
801 Curl_safefree(mailbox);
804 state(conn, IMAP_SELECT);
809 /***********************************************************************
811 * imap_perform_fetch()
813 * Sends a FETCH command to initiate the download of a message.
815 static CURLcode imap_perform_fetch(struct connectdata *conn)
817 CURLcode result = CURLE_OK;
818 struct IMAP *imap = conn->data->state.proto.imap;
820 /* Check we have a UID */
822 failf(conn->data, "Cannot FETCH without a UID.");
823 return CURLE_URL_MALFORMAT;
826 /* Send the FETCH command */
827 result = imap_sendf(conn, "FETCH %s BODY[%s]",
829 imap->section ? imap->section : "");
832 state(conn, IMAP_FETCH);
837 /***********************************************************************
839 * imap_perform_append()
841 * Sends an APPEND command to initiate the upload of a message.
843 static CURLcode imap_perform_append(struct connectdata *conn)
845 CURLcode result = CURLE_OK;
846 struct IMAP *imap = conn->data->state.proto.imap;
849 /* Check we have a mailbox */
851 failf(conn->data, "Cannot APPEND without a mailbox.");
852 return CURLE_URL_MALFORMAT;
855 /* Check we know the size of the upload */
856 if(conn->data->set.infilesize < 0) {
857 failf(conn->data, "Cannot APPEND with unknown input file size\n");
858 return CURLE_UPLOAD_FAILED;
861 /* Make sure the mailbox is in the correct atom format */
862 mailbox = imap_atom(imap->mailbox);
864 return CURLE_OUT_OF_MEMORY;
866 /* Send the APPEND command */
867 result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
868 mailbox, conn->data->set.infilesize);
870 Curl_safefree(mailbox);
873 state(conn, IMAP_APPEND);
878 /***********************************************************************
880 * imap_perform_logout()
882 * Performs the logout action prior to sclose() being called.
884 static CURLcode imap_perform_logout(struct connectdata *conn)
886 CURLcode result = CURLE_OK;
888 /* Send the LOGOUT command */
889 result = imap_sendf(conn, "LOGOUT");
892 state(conn, IMAP_LOGOUT);
897 /* For the initial server greeting */
898 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
902 CURLcode result = CURLE_OK;
903 struct SessionHandle *data = conn->data;
905 (void)instate; /* no use for this yet */
907 if(imapcode != 'O') {
908 failf(data, "Got unexpected imap-server response");
909 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
912 result = imap_perform_capability(conn);
917 /* For CAPABILITY responses */
918 static CURLcode imap_state_capability_resp(struct connectdata *conn,
922 CURLcode result = CURLE_OK;
923 struct SessionHandle *data = conn->data;
924 struct imap_conn *imapc = &conn->proto.imapc;
925 const char *line = data->state.buffer;
928 (void)instate; /* no use for this yet */
930 /* Do we have a untagged response? */
931 if(imapcode == '*') {
934 /* Loop through the data line */
937 (*line == ' ' || *line == '\t' ||
938 *line == '\r' || *line == '\n')) {
946 /* Extract the word */
947 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
948 line[wordlen] != '\t' && line[wordlen] != '\r' &&
949 line[wordlen] != '\n';)
952 /* Does the server support the STARTTLS capability? */
953 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
954 imapc->tls_supported = TRUE;
956 /* Has the server explicitly disabled clear text authentication? */
957 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
958 imapc->login_disabled = TRUE;
960 /* Does the server support the SASL-IR capability? */
961 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
962 imapc->ir_supported = TRUE;
964 /* Do we have a SASL based authentication mechanism? */
965 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
969 /* Test the word for a matching authentication mechanism */
970 if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
971 imapc->authmechs |= SASL_MECH_LOGIN;
972 if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
973 imapc->authmechs |= SASL_MECH_PLAIN;
974 else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
975 imapc->authmechs |= SASL_MECH_CRAM_MD5;
976 else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
977 imapc->authmechs |= SASL_MECH_DIGEST_MD5;
978 else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
979 imapc->authmechs |= SASL_MECH_GSSAPI;
980 else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
981 imapc->authmechs |= SASL_MECH_EXTERNAL;
982 else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
983 imapc->authmechs |= SASL_MECH_NTLM;
989 else if(imapcode == 'O') {
990 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
991 /* We don't have a SSL/TLS connection yet, but SSL is requested */
992 if(imapc->tls_supported)
993 /* Switch to TLS connection now */
994 result = imap_perform_starttls(conn);
995 else if(data->set.use_ssl == CURLUSESSL_TRY)
996 /* Fallback and carry on with authentication */
997 result = imap_perform_authenticate(conn);
999 failf(data, "STARTTLS not supported.");
1000 result = CURLE_USE_SSL_FAILED;
1004 result = imap_perform_authenticate(conn);
1007 result = imap_perform_login(conn);
1012 /* For STARTTLS responses */
1013 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
1017 CURLcode result = CURLE_OK;
1018 struct SessionHandle *data = conn->data;
1020 (void)instate; /* no use for this yet */
1022 if(imapcode != 'O') {
1023 if(data->set.use_ssl != CURLUSESSL_TRY) {
1024 failf(data, "STARTTLS denied. %c", imapcode);
1025 result = CURLE_USE_SSL_FAILED;
1028 result = imap_perform_authenticate(conn);
1031 result = imap_perform_upgrade_tls(conn);
1036 /* For AUTHENTICATE PLAIN (without initial response) responses */
1037 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
1041 CURLcode result = CURLE_OK;
1042 struct SessionHandle *data = conn->data;
1044 char *plainauth = NULL;
1046 (void)instate; /* no use for this yet */
1048 if(imapcode != '+') {
1049 failf(data, "Access denied. %c", imapcode);
1050 result = CURLE_LOGIN_DENIED;
1053 /* Create the authorisation message */
1054 result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
1057 /* Send the message */
1060 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
1063 state(conn, IMAP_AUTHENTICATE_FINAL);
1066 Curl_safefree(plainauth);
1073 /* For AUTHENTICATE LOGIN (without initial response) responses */
1074 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1078 CURLcode result = CURLE_OK;
1079 struct SessionHandle *data = conn->data;
1081 char *authuser = NULL;
1083 (void)instate; /* no use for this yet */
1085 if(imapcode != '+') {
1086 failf(data, "Access denied: %d", imapcode);
1087 result = CURLE_LOGIN_DENIED;
1090 /* Create the user message */
1091 result = Curl_sasl_create_login_message(data, conn->user,
1097 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1100 state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1103 Curl_safefree(authuser);
1110 /* For AUTHENTICATE LOGIN user entry responses */
1111 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1115 CURLcode result = CURLE_OK;
1116 struct SessionHandle *data = conn->data;
1118 char *authpasswd = NULL;
1120 (void)instate; /* no use for this yet */
1122 if(imapcode != '+') {
1123 failf(data, "Access denied: %d", imapcode);
1124 result = CURLE_LOGIN_DENIED;
1127 /* Create the password message */
1128 result = Curl_sasl_create_login_message(data, conn->passwd,
1131 /* Send the password */
1134 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1137 state(conn, IMAP_AUTHENTICATE_FINAL);
1140 Curl_safefree(authpasswd);
1147 #ifndef CURL_DISABLE_CRYPTO_AUTH
1148 /* For AUTHENTICATE CRAM-MD5 responses */
1149 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1153 CURLcode result = CURLE_OK;
1154 struct SessionHandle *data = conn->data;
1155 char *chlg64 = data->state.buffer;
1157 char *rplyb64 = NULL;
1159 (void)instate; /* no use for this yet */
1161 if(imapcode != '+') {
1162 failf(data, "Access denied: %d", imapcode);
1163 return CURLE_LOGIN_DENIED;
1166 /* Get the challenge */
1167 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1170 /* Terminate the challenge */
1171 if(*chlg64 != '=') {
1172 for(len = strlen(chlg64); len--;)
1173 if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
1174 chlg64[len] != '\t')
1182 /* Create the response message */
1183 result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
1184 conn->passwd, &rplyb64, &len);
1186 /* Send the response */
1189 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1192 state(conn, IMAP_AUTHENTICATE_FINAL);
1195 Curl_safefree(rplyb64);
1201 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1202 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1206 CURLcode result = CURLE_OK;
1207 struct SessionHandle *data = conn->data;
1208 char *chlg64 = data->state.buffer;
1210 char *rplyb64 = NULL;
1212 (void)instate; /* no use for this yet */
1214 if(imapcode != '+') {
1215 failf(data, "Access denied: %d", imapcode);
1216 return CURLE_LOGIN_DENIED;
1219 /* Get the challenge */
1220 for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1223 /* Create the response message */
1224 result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
1225 conn->passwd, "imap",
1228 /* Send the response */
1231 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1234 state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1237 Curl_safefree(rplyb64);
1243 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1244 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1248 CURLcode result = CURLE_OK;
1249 struct SessionHandle *data = conn->data;
1251 (void)instate; /* no use for this yet */
1253 if(imapcode != '+') {
1254 failf(data, "Authentication failed: %d", imapcode);
1255 result = CURLE_LOGIN_DENIED;
1258 /* Send an empty response */
1259 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1262 state(conn, IMAP_AUTHENTICATE_FINAL);
1270 /* For AUTHENTICATE NTLM (without initial response) responses */
1271 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1275 CURLcode result = CURLE_OK;
1276 struct SessionHandle *data = conn->data;
1278 char *type1msg = NULL;
1280 (void)instate; /* no use for this yet */
1282 if(imapcode != '+') {
1283 failf(data, "Access denied: %d", imapcode);
1284 result = CURLE_LOGIN_DENIED;
1287 /* Create the type-1 message */
1288 result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1292 /* Send the message */
1295 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1298 state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1301 Curl_safefree(type1msg);
1308 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1309 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1313 CURLcode result = CURLE_OK;
1314 struct SessionHandle *data = conn->data;
1316 char *type3msg = NULL;
1318 (void)instate; /* no use for this yet */
1320 if(imapcode != '+') {
1321 failf(data, "Access denied: %d", imapcode);
1322 result = CURLE_LOGIN_DENIED;
1325 /* Create the type-3 message */
1326 result = Curl_sasl_create_ntlm_type3_message(data,
1327 data->state.buffer + 2,
1328 conn->user, conn->passwd,
1332 /* Send the message */
1335 result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1338 state(conn, IMAP_AUTHENTICATE_FINAL);
1341 Curl_safefree(type3msg);
1349 /* For final responses to the AUTHENTICATE sequence */
1350 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1354 CURLcode result = CURLE_OK;
1355 struct SessionHandle *data = conn->data;
1357 (void)instate; /* no use for this yet */
1359 if(imapcode != 'O') {
1360 failf(data, "Authentication failed: %d", imapcode);
1361 result = CURLE_LOGIN_DENIED;
1364 /* End of connect phase */
1365 state(conn, IMAP_STOP);
1370 /* For LOGIN responses */
1371 static CURLcode imap_state_login_resp(struct connectdata *conn,
1375 CURLcode result = CURLE_OK;
1376 struct SessionHandle *data = conn->data;
1378 (void)instate; /* no use for this yet */
1380 if(imapcode != 'O') {
1381 failf(data, "Access denied. %c", imapcode);
1382 result = CURLE_LOGIN_DENIED;
1385 /* End of connect phase */
1386 state(conn, IMAP_STOP);
1391 /* For LIST responses */
1392 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1395 CURLcode result = CURLE_OK;
1396 char *line = conn->data->state.buffer;
1397 size_t len = strlen(line);
1399 (void)instate; /* No use for this yet */
1401 if(imapcode == '*') {
1402 /* Temporarily add the LF character back and send as body to the client */
1404 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1407 else if(imapcode != 'O')
1408 result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1410 /* End of DO phase */
1411 state(conn, IMAP_STOP);
1416 /* For SELECT responses */
1417 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1420 CURLcode result = CURLE_OK;
1421 struct SessionHandle *data = conn->data;
1422 struct IMAP *imap = conn->data->state.proto.imap;
1423 struct imap_conn *imapc = &conn->proto.imapc;
1424 const char *line = data->state.buffer;
1427 (void)instate; /* no use for this yet */
1429 if(imapcode == '*') {
1430 /* See if this is an UIDVALIDITY response */
1431 if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1432 Curl_safefree(imapc->mailbox_uidvalidity);
1433 imapc->mailbox_uidvalidity = strdup(tmp);
1436 else if(imapcode == 'O') {
1437 /* Check if the UIDVALIDITY has been specified and matches */
1438 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1439 strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1440 failf(conn->data, "Mailbox UIDVALIDITY has changed");
1441 result = CURLE_REMOTE_FILE_NOT_FOUND;
1444 /* Note the currently opened mailbox on this connection */
1445 imapc->mailbox = strdup(imap->mailbox);
1448 result = imap_perform_list(conn);
1450 result = imap_perform_fetch(conn);
1454 failf(data, "Select failed");
1455 result = CURLE_LOGIN_DENIED;
1461 /* For the (first line of the) FETCH responses */
1462 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1465 CURLcode result = CURLE_OK;
1466 struct SessionHandle *data = conn->data;
1467 struct imap_conn *imapc = &conn->proto.imapc;
1468 struct pingpong *pp = &imapc->pp;
1469 const char *ptr = data->state.buffer;
1470 bool parsed = FALSE;
1473 (void)instate; /* no use for this yet */
1475 if(imapcode != '*') {
1476 Curl_pgrsSetDownloadSize(data, 0);
1477 state(conn, IMAP_STOP);
1478 return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1481 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1482 the continuation data contained within the curly brackets */
1483 while(*ptr && (*ptr != '{'))
1488 size = curlx_strtoofft(ptr + 1, &endptr, 10);
1489 if(endptr - ptr > 1 && endptr[0] == '}' &&
1490 endptr[1] == '\r' && endptr[2] == '\0')
1495 infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1496 Curl_pgrsSetDownloadSize(data, size);
1499 /* At this point there is a bunch of data in the header "cache" that is
1500 actually body content, send it as body and then skip it. Do note
1501 that there may even be additional "headers" after the body. */
1502 size_t chunk = pp->cache_size;
1504 if(chunk > (size_t)size)
1505 /* The conversion from curl_off_t to size_t is always fine here */
1506 chunk = (size_t)size;
1508 result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1512 data->req.bytecount += chunk;
1515 infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1516 " bytes are left for transfer\n", (curl_off_t)chunk, size);
1518 /* Have we used the entire cache or just part of it?*/
1519 if(pp->cache_size > chunk) {
1520 /* Only part of it so shrink the cache to fit the trailing data */
1521 memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1522 pp->cache_size -= chunk;
1525 /* Free the cache */
1526 Curl_safefree(pp->cache);
1528 /* Reset the cache size */
1534 /* The entire data is already transferred! */
1535 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1538 data->req.maxdownload = size;
1539 Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1543 /* We don't know how to parse this line */
1544 failf(pp->conn->data, "Failed to parse FETCH response.");
1545 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1548 /* End of DO phase */
1549 state(conn, IMAP_STOP);
1554 /* For final FETCH responses performed after the download */
1555 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1559 CURLcode result = CURLE_OK;
1561 (void)instate; /* No use for this yet */
1564 result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1566 /* End of DONE phase */
1567 state(conn, IMAP_STOP);
1572 /* For APPEND responses */
1573 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1576 CURLcode result = CURLE_OK;
1577 struct SessionHandle *data = conn->data;
1579 (void)instate; /* No use for this yet */
1581 if(imapcode != '+') {
1582 result = CURLE_UPLOAD_FAILED;
1585 /* Set the progress upload size */
1586 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1589 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1591 /* End of DO phase */
1592 state(conn, IMAP_STOP);
1598 /* For final APPEND responses performed after the upload */
1599 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1603 CURLcode result = CURLE_OK;
1605 (void)instate; /* No use for this yet */
1608 result = CURLE_UPLOAD_FAILED;
1610 /* End of DONE phase */
1611 state(conn, IMAP_STOP);
1616 static CURLcode imap_statemach_act(struct connectdata *conn)
1618 CURLcode result = CURLE_OK;
1619 curl_socket_t sock = conn->sock[FIRSTSOCKET];
1621 struct imap_conn *imapc = &conn->proto.imapc;
1622 struct pingpong *pp = &imapc->pp;
1625 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1626 if(imapc->state == IMAP_UPGRADETLS)
1627 return imap_perform_upgrade_tls(conn);
1629 /* Flush any data that needs to be sent */
1631 return Curl_pp_flushsend(pp);
1634 /* Read the response from the server */
1635 result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1639 /* Was there an error parsing the response line? */
1641 return CURLE_FTP_WEIRD_SERVER_REPLY;
1646 /* We have now received a full IMAP server response */
1647 switch(imapc->state) {
1648 case IMAP_SERVERGREET:
1649 result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1652 case IMAP_CAPABILITY:
1653 result = imap_state_capability_resp(conn, imapcode, imapc->state);
1657 result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1660 case IMAP_AUTHENTICATE_PLAIN:
1661 result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1664 case IMAP_AUTHENTICATE_LOGIN:
1665 result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1668 case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1669 result = imap_state_auth_login_password_resp(conn, imapcode,
1673 #ifndef CURL_DISABLE_CRYPTO_AUTH
1674 case IMAP_AUTHENTICATE_CRAMMD5:
1675 result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1678 case IMAP_AUTHENTICATE_DIGESTMD5:
1679 result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1682 case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1683 result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1688 case IMAP_AUTHENTICATE_NTLM:
1689 result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1692 case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1693 result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1698 case IMAP_AUTHENTICATE_FINAL:
1699 result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1703 result = imap_state_login_resp(conn, imapcode, imapc->state);
1707 result = imap_state_list_resp(conn, imapcode, imapc->state);
1711 result = imap_state_select_resp(conn, imapcode, imapc->state);
1715 result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1718 case IMAP_FETCH_FINAL:
1719 result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1723 result = imap_state_append_resp(conn, imapcode, imapc->state);
1726 case IMAP_APPEND_FINAL:
1727 result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1731 /* fallthrough, just stop! */
1733 /* internal error */
1734 state(conn, IMAP_STOP);
1737 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1742 /* Called repeatedly until done from multi.c */
1743 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1745 CURLcode result = CURLE_OK;
1746 struct imap_conn *imapc = &conn->proto.imapc;
1748 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1749 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1751 result = Curl_pp_statemach(&imapc->pp, FALSE);
1753 *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1758 static CURLcode imap_block_statemach(struct connectdata *conn)
1760 CURLcode result = CURLE_OK;
1761 struct imap_conn *imapc = &conn->proto.imapc;
1763 while(imapc->state != IMAP_STOP && !result)
1764 result = Curl_pp_statemach(&imapc->pp, TRUE);
1769 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1771 static CURLcode imap_init(struct connectdata *conn)
1773 CURLcode result = CURLE_OK;
1774 struct SessionHandle *data = conn->data;
1775 struct IMAP *imap = data->state.proto.imap;
1778 imap = data->state.proto.imap = calloc(sizeof(struct IMAP), 1);
1780 result = CURLE_OUT_OF_MEMORY;
1786 /* For the IMAP "protocol connect" and "doing" phases only */
1787 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1790 return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1793 /***********************************************************************
1795 * imap_connect() should do everything that is to be considered a part of
1796 * the connection phase.
1798 * The variable 'done' points to will be TRUE if the protocol-layer connect
1799 * phase is done when this function returns, or FALSE is not. When called as
1800 * a part of the easy interface, it will always be TRUE.
1802 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1804 CURLcode result = CURLE_OK;
1805 struct imap_conn *imapc = &conn->proto.imapc;
1806 struct pingpong *pp = &imapc->pp;
1808 *done = FALSE; /* default to not done yet */
1810 /* If there already is a protocol-specific struct allocated for this
1811 sessionhandle, deal with it */
1812 Curl_reset_reqproto(conn);
1814 /* Initialise the IMAP layer */
1815 result = imap_init(conn);
1819 /* We always support persistent connections in IMAP */
1820 conn->bits.close = FALSE;
1822 /* Set the default response time-out */
1823 pp->response_time = RESP_TIMEOUT;
1824 pp->statemach_act = imap_statemach_act;
1825 pp->endofresp = imap_endofresp;
1828 /* Initialise the pingpong layer */
1831 /* Start off waiting for the server greeting response */
1832 state(conn, IMAP_SERVERGREET);
1834 /* Start off with an response id of '*' */
1835 strcpy(imapc->resptag, "*");
1837 result = imap_multi_statemach(conn, done);
1842 /***********************************************************************
1846 * The DONE function. This does what needs to be done after a single DO has
1849 * Input argument is already checked for validity.
1851 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1854 CURLcode result = CURLE_OK;
1855 struct SessionHandle *data = conn->data;
1856 struct IMAP *imap = data->state.proto.imap;
1861 /* When the easy handle is removed from the multi interface while libcurl
1862 is still trying to resolve the host name, the IMAP struct is not yet
1863 initialized. However, the removal action calls Curl_done() which in
1864 turn calls this function, so we simply return success. */
1868 conn->bits.close = TRUE; /* marked for closure */
1869 result = status; /* use the already set error code */
1871 else if(!data->set.connect_only && !imap->custom &&
1872 (imap->uid || data->set.upload)) {
1873 /* Handle responses after FETCH or APPEND transfer has finished */
1874 if(!data->set.upload)
1875 state(conn, IMAP_FETCH_FINAL);
1877 /* End the APPEND command first by sending an empty line */
1878 result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1880 state(conn, IMAP_APPEND_FINAL);
1883 /* Run the state-machine
1885 TODO: when the multi interface is used, this _really_ should be using
1886 the imap_multi_statemach function but we have no general support for
1887 non-blocking DONE operations, not in the multi state machine and with
1888 Curl_done() invokes on several places in the code!
1891 result = imap_block_statemach(conn);
1894 /* Cleanup our per-request based variables */
1895 Curl_safefree(imap->mailbox);
1896 Curl_safefree(imap->uidvalidity);
1897 Curl_safefree(imap->uid);
1898 Curl_safefree(imap->section);
1899 Curl_safefree(imap->custom);
1900 Curl_safefree(imap->custom_params);
1902 /* Clear the transfer mode for the next request */
1903 imap->transfer = FTPTRANSFER_BODY;
1908 /***********************************************************************
1912 * This is the actual DO function for IMAP. Fetch or append a message, or do
1913 * other things according to the options previously setup.
1915 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1918 /* This is IMAP and no proxy */
1919 CURLcode result = CURLE_OK;
1920 struct SessionHandle *data = conn->data;
1921 struct IMAP *imap = data->state.proto.imap;
1922 struct imap_conn *imapc = &conn->proto.imapc;
1923 bool selected = FALSE;
1925 DEBUGF(infof(conn->data, "DO phase starts\n"));
1927 if(conn->data->set.opt_no_body) {
1928 /* Requested no body means no transfer */
1929 imap->transfer = FTPTRANSFER_INFO;
1932 *dophase_done = FALSE; /* not done yet */
1934 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1935 has already been selected on this connection */
1936 if(imap->mailbox && imapc->mailbox &&
1937 !strcmp(imap->mailbox, imapc->mailbox) &&
1938 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1939 !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1942 /* Start the first command in the DO phase */
1943 if(conn->data->set.upload)
1944 /* APPEND can be executed directly */
1945 result = imap_perform_append(conn);
1946 else if(imap->custom && (selected || !imap->mailbox))
1947 /* Custom command using the same mailbox or no mailbox */
1948 result = imap_perform_list(conn);
1949 else if(!imap->custom && selected && imap->uid)
1950 /* FETCH from the same mailbox */
1951 result = imap_perform_fetch(conn);
1952 else if(imap->mailbox && !selected && (imap->custom || imap->uid))
1953 /* SELECT the mailbox */
1954 result = imap_perform_select(conn);
1957 result = imap_perform_list(conn);
1962 /* Run the state-machine */
1963 result = imap_multi_statemach(conn, dophase_done);
1965 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1968 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1973 /***********************************************************************
1977 * This function is registered as 'curl_do' function. It decodes the path
1978 * parts etc as a wrapper to the actual DO function (imap_perform).
1980 * The input argument is already checked for validity.
1982 static CURLcode imap_do(struct connectdata *conn, bool *done)
1984 CURLcode result = CURLE_OK;
1986 *done = FALSE; /* default to false */
1988 /* Since connections can be re-used between SessionHandles, there might be a
1989 connection already existing but on a fresh SessionHandle struct. As such
1990 we make sure we have a good IMAP struct to play with. For new connections
1991 the IMAP struct is allocated and setup in the imap_connect() function. */
1992 Curl_reset_reqproto(conn);
1993 result = imap_init(conn);
1997 /* Parse the URL path */
1998 result = imap_parse_url_path(conn);
2002 /* Parse the custom request */
2003 result = imap_parse_custom_request(conn);
2007 result = imap_regular_transfer(conn, done);
2012 /***********************************************************************
2016 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
2017 * resources. BLOCKING.
2019 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2021 struct imap_conn *imapc = &conn->proto.imapc;
2023 /* We cannot send quit unconditionally. If this connection is stale or
2024 bad in any way, sending quit and waiting around here will make the
2025 disconnect wait in vain and cause more problems than we need to. */
2027 /* The IMAP session may or may not have been allocated/setup at this
2029 if(!dead_connection && imapc->pp.conn)
2030 if(!imap_perform_logout(conn))
2031 (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
2033 /* Disconnect from the server */
2034 Curl_pp_disconnect(&imapc->pp);
2036 /* Cleanup the SASL module */
2037 Curl_sasl_cleanup(conn, imapc->authused);
2039 /* Cleanup our connection based variables */
2040 Curl_safefree(imapc->mailbox);
2041 Curl_safefree(imapc->mailbox_uidvalidity);
2046 /***********************************************************************
2050 * Portable test of whether the specified char is a "bchar" as defined in the
2051 * grammar of RFC-5092.
2053 static bool imap_is_bchar(char ch)
2057 case ':': case '@': case '/':
2058 /* bchar -> achar */
2060 /* bchar -> achar -> uchar -> unreserved */
2061 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2062 case '7': case '8': case '9':
2063 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2064 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2065 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2066 case 'V': case 'W': case 'X': case 'Y': case 'Z':
2067 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2068 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2069 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2070 case 'v': case 'w': case 'x': case 'y': case 'z':
2071 case '-': case '.': case '_': case '~':
2072 /* bchar -> achar -> uchar -> sub-delims-sh */
2073 case '!': case '$': case '\'': case '(': case ')': case '*':
2075 /* bchar -> achar -> uchar -> pct-encoded */
2076 case '%': /* HEXDIG chars are already included above */
2084 /***********************************************************************
2086 * imap_parse_url_path()
2088 * Parse the URL path into separate path components.
2091 static CURLcode imap_parse_url_path(struct connectdata *conn)
2093 /* The imap struct is already initialised in imap_connect() */
2094 CURLcode result = CURLE_OK;
2095 struct SessionHandle *data = conn->data;
2096 struct IMAP *imap = data->state.proto.imap;
2097 const char *begin = data->state.path;
2098 const char *ptr = begin;
2100 /* See how much of the URL is a valid path and decode it */
2101 while(imap_is_bchar(*ptr))
2105 /* Remove the trailing slash if present */
2106 const char *end = ptr;
2107 if(end > begin && end[-1] == '/')
2110 result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2116 imap->mailbox = NULL;
2118 /* There can be any number of parameters in the form ";NAME=VALUE" */
2119 while(*ptr == ';') {
2124 /* Find the length of the name parameter */
2126 while(*ptr && *ptr != '=')
2130 return CURLE_URL_MALFORMAT;
2132 /* Decode the name parameter */
2133 result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2137 /* Find the length of the value parameter */
2139 while(imap_is_bchar(*ptr))
2142 /* Decode the value parameter */
2143 result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2145 Curl_safefree(name);
2149 DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2151 /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2152 stripping of the trailing slash character if it is present.
2154 Note: Unknown parameters trigger a URL_MALFORMAT error. */
2155 if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2156 if(valuelen > 0 && value[valuelen - 1] == '/')
2157 value[valuelen - 1] = '\0';
2159 imap->uidvalidity = value;
2162 else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2163 if(valuelen > 0 && value[valuelen - 1] == '/')
2164 value[valuelen - 1] = '\0';
2169 else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2170 if(valuelen > 0 && value[valuelen - 1] == '/')
2171 value[valuelen - 1] = '\0';
2173 imap->section = value;
2177 Curl_safefree(name);
2178 Curl_safefree(value);
2180 return CURLE_URL_MALFORMAT;
2183 Curl_safefree(name);
2184 Curl_safefree(value);
2187 /* Any extra stuff at the end of the URL is an error */
2189 return CURLE_URL_MALFORMAT;
2194 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2196 CURLcode result = CURLE_OK;
2197 struct SessionHandle *data = conn->data;
2198 struct IMAP *imap = data->state.proto.imap;
2199 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2202 /* URL decode the custom request */
2203 result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2205 /* Extract the parameters if specified */
2207 const char *params = imap->custom;
2209 while(*params && *params != ' ')
2213 imap->custom_params = strdup(params);
2214 imap->custom[params - imap->custom] = '\0';
2216 if(!imap->custom_params)
2217 result = CURLE_OUT_OF_MEMORY;
2225 /* Call this when the DO phase has completed */
2226 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2228 struct IMAP *imap = conn->data->state.proto.imap;
2232 if(imap->transfer != FTPTRANSFER_BODY)
2233 /* no data to transfer */
2234 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2239 /* Called from multi.c while DOing */
2240 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2242 CURLcode result = imap_multi_statemach(conn, dophase_done);
2245 DEBUGF(infof(conn->data, "DO phase failed\n"));
2246 else if(*dophase_done) {
2247 result = imap_dophase_done(conn, FALSE /* not connected */);
2249 DEBUGF(infof(conn->data, "DO phase is complete\n"));
2255 /***********************************************************************
2257 * imap_regular_transfer()
2259 * The input argument is already checked for validity.
2261 * Performs all commands done before a regular transfer between a local and a
2264 static CURLcode imap_regular_transfer(struct connectdata *conn,
2267 CURLcode result = CURLE_OK;
2268 bool connected = FALSE;
2269 struct SessionHandle *data = conn->data;
2271 /* Make sure size is unknown at this point */
2272 data->req.size = -1;
2274 /* Set the progress data */
2275 Curl_pgrsSetUploadCounter(data, 0);
2276 Curl_pgrsSetDownloadCounter(data, 0);
2277 Curl_pgrsSetUploadSize(data, 0);
2278 Curl_pgrsSetDownloadSize(data, 0);
2280 /* Carry out the perform */
2281 result = imap_perform(conn, &connected, dophase_done);
2283 /* Perform post DO phase operations if necessary */
2284 if(!result && *dophase_done)
2285 result = imap_dophase_done(conn, connected);
2290 static CURLcode imap_setup_connection(struct connectdata *conn)
2292 struct SessionHandle *data = conn->data;
2294 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2295 /* Unless we have asked to tunnel IMAP operations through the proxy, we
2296 switch and use HTTP operations only */
2297 #ifndef CURL_DISABLE_HTTP
2298 if(conn->handler == &Curl_handler_imap)
2299 conn->handler = &Curl_handler_imap_proxy;
2302 conn->handler = &Curl_handler_imaps_proxy;
2304 failf(data, "IMAPS not supported!");
2305 return CURLE_UNSUPPORTED_PROTOCOL;
2309 /* We explicitly mark this connection as persistent here as we're doing
2310 IMAP over HTTP and thus we accidentally avoid setting this value
2312 conn->bits.close = FALSE;
2314 failf(data, "IMAP over http proxy requires HTTP support built-in!");
2315 return CURLE_UNSUPPORTED_PROTOCOL;
2319 data->state.path++; /* don't include the initial slash */
2324 #endif /* CURL_DISABLE_IMAP */