Base code merged to SPIN 2.4
[platform/upstream/curl.git] / lib / imap.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
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.
13  *
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.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
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  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28  * RFC4959 IMAP Extension for SASL Initial Client Response
29  * RFC5092 IMAP URL Scheme
30  * RFC6749 OAuth 2.0 Authorization Framework
31  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
32  *
33  ***************************************************************************/
34
35 #include "curl_setup.h"
36
37 #ifndef CURL_DISABLE_IMAP
38
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #ifdef HAVE_UTSNAME_H
46 #include <sys/utsname.h>
47 #endif
48 #ifdef HAVE_NETDB_H
49 #include <netdb.h>
50 #endif
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57 #undef in_addr_t
58 #define in_addr_t unsigned long
59 #endif
60
61 #include <curl/curl.h>
62 #include "urldata.h"
63 #include "sendf.h"
64 #include "hostip.h"
65 #include "progress.h"
66 #include "transfer.h"
67 #include "escape.h"
68 #include "http.h" /* for HTTP proxy tunnel stuff */
69 #include "socks.h"
70 #include "imap.h"
71
72 #include "strtoofft.h"
73 #include "strequal.h"
74 #include "vtls/vtls.h"
75 #include "connect.h"
76 #include "strerror.h"
77 #include "select.h"
78 #include "multiif.h"
79 #include "url.h"
80 #include "rawstr.h"
81 #include "curl_sasl.h"
82 #include "warnless.h"
83
84 #define _MPRINTF_REPLACE /* use our functions only */
85 #include <curl/mprintf.h>
86
87 #include "curl_memory.h"
88 /* The last #include file should be: */
89 #include "memdebug.h"
90
91 /* Local API functions */
92 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
93 static CURLcode imap_do(struct connectdata *conn, bool *done);
94 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
95                           bool premature);
96 static CURLcode imap_connect(struct connectdata *conn, bool *done);
97 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
98 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
99 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
100                         int numsocks);
101 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
102 static CURLcode imap_setup_connection(struct connectdata *conn);
103 static char *imap_atom(const char *str);
104 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
105 static CURLcode imap_parse_url_options(struct connectdata *conn);
106 static CURLcode imap_parse_url_path(struct connectdata *conn);
107 static CURLcode imap_parse_custom_request(struct connectdata *conn);
108 static CURLcode imap_calc_sasl_details(struct connectdata *conn,
109                                        const char **mech,
110                                        char **initresp, size_t *len,
111                                        imapstate *state1, imapstate *state2);
112
113 /*
114  * IMAP protocol handler.
115  */
116
117 const struct Curl_handler Curl_handler_imap = {
118   "IMAP",                           /* scheme */
119   imap_setup_connection,            /* setup_connection */
120   imap_do,                          /* do_it */
121   imap_done,                        /* done */
122   ZERO_NULL,                        /* do_more */
123   imap_connect,                     /* connect_it */
124   imap_multi_statemach,             /* connecting */
125   imap_doing,                       /* doing */
126   imap_getsock,                     /* proto_getsock */
127   imap_getsock,                     /* doing_getsock */
128   ZERO_NULL,                        /* domore_getsock */
129   ZERO_NULL,                        /* perform_getsock */
130   imap_disconnect,                  /* disconnect */
131   ZERO_NULL,                        /* readwrite */
132   PORT_IMAP,                        /* defport */
133   CURLPROTO_IMAP,                   /* protocol */
134   PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD  /* flags */
135 };
136
137 #ifdef USE_SSL
138 /*
139  * IMAPS protocol handler.
140  */
141
142 const struct Curl_handler Curl_handler_imaps = {
143   "IMAPS",                          /* scheme */
144   imap_setup_connection,            /* setup_connection */
145   imap_do,                          /* do_it */
146   imap_done,                        /* done */
147   ZERO_NULL,                        /* do_more */
148   imap_connect,                     /* connect_it */
149   imap_multi_statemach,             /* connecting */
150   imap_doing,                       /* doing */
151   imap_getsock,                     /* proto_getsock */
152   imap_getsock,                     /* doing_getsock */
153   ZERO_NULL,                        /* domore_getsock */
154   ZERO_NULL,                        /* perform_getsock */
155   imap_disconnect,                  /* disconnect */
156   ZERO_NULL,                        /* readwrite */
157   PORT_IMAPS,                       /* defport */
158   CURLPROTO_IMAPS,                  /* protocol */
159   PROTOPT_CLOSEACTION | PROTOPT_SSL |
160   PROTOPT_NEEDSPWD                  /* flags */
161 };
162 #endif
163
164 #ifndef CURL_DISABLE_HTTP
165 /*
166  * HTTP-proxyed IMAP protocol handler.
167  */
168
169 static const struct Curl_handler Curl_handler_imap_proxy = {
170   "IMAP",                               /* scheme */
171   Curl_http_setup_conn,                 /* setup_connection */
172   Curl_http,                            /* do_it */
173   Curl_http_done,                       /* done */
174   ZERO_NULL,                            /* do_more */
175   ZERO_NULL,                            /* connect_it */
176   ZERO_NULL,                            /* connecting */
177   ZERO_NULL,                            /* doing */
178   ZERO_NULL,                            /* proto_getsock */
179   ZERO_NULL,                            /* doing_getsock */
180   ZERO_NULL,                            /* domore_getsock */
181   ZERO_NULL,                            /* perform_getsock */
182   ZERO_NULL,                            /* disconnect */
183   ZERO_NULL,                            /* readwrite */
184   PORT_IMAP,                            /* defport */
185   CURLPROTO_HTTP,                       /* protocol */
186   PROTOPT_NONE                          /* flags */
187 };
188
189 #ifdef USE_SSL
190 /*
191  * HTTP-proxyed IMAPS protocol handler.
192  */
193
194 static const struct Curl_handler Curl_handler_imaps_proxy = {
195   "IMAPS",                              /* scheme */
196   Curl_http_setup_conn,                 /* setup_connection */
197   Curl_http,                            /* do_it */
198   Curl_http_done,                       /* done */
199   ZERO_NULL,                            /* do_more */
200   ZERO_NULL,                            /* connect_it */
201   ZERO_NULL,                            /* connecting */
202   ZERO_NULL,                            /* doing */
203   ZERO_NULL,                            /* proto_getsock */
204   ZERO_NULL,                            /* doing_getsock */
205   ZERO_NULL,                            /* domore_getsock */
206   ZERO_NULL,                            /* perform_getsock */
207   ZERO_NULL,                            /* disconnect */
208   ZERO_NULL,                            /* readwrite */
209   PORT_IMAPS,                           /* defport */
210   CURLPROTO_HTTP,                       /* protocol */
211   PROTOPT_NONE                          /* flags */
212 };
213 #endif
214 #endif
215
216 #ifdef USE_SSL
217 static void imap_to_imaps(struct connectdata *conn)
218 {
219   conn->handler = &Curl_handler_imaps;
220 }
221 #else
222 #define imap_to_imaps(x) Curl_nop_stmt
223 #endif
224
225 /***********************************************************************
226  *
227  * imap_matchresp()
228  *
229  * Determines whether the untagged response is related to the specified
230  * command by checking if it is in format "* <command-name> ..." or
231  * "* <number> <command-name> ...".
232  *
233  * The "* " marker is assumed to have already been checked by the caller.
234  */
235 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
236 {
237   const char *end = line + len;
238   size_t cmd_len = strlen(cmd);
239
240   /* Skip the untagged response marker */
241   line += 2;
242
243   /* Do we have a number after the marker? */
244   if(line < end && ISDIGIT(*line)) {
245     /* Skip the number */
246     do
247       line++;
248     while(line < end && ISDIGIT(*line));
249
250     /* Do we have the space character? */
251     if(line == end || *line != ' ')
252       return FALSE;
253
254     line++;
255   }
256
257   /* Does the command name match and is it followed by a space character or at
258      the end of line? */
259   if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
260      (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
261     return TRUE;
262
263   return FALSE;
264 }
265
266 /***********************************************************************
267  *
268  * imap_endofresp()
269  *
270  * Checks whether the given string is a valid tagged, untagged or continuation
271  * response which can be processed by the response handler.
272  */
273 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
274                            int *resp)
275 {
276   struct IMAP *imap = conn->data->req.protop;
277   struct imap_conn *imapc = &conn->proto.imapc;
278   const char *id = imapc->resptag;
279   size_t id_len = strlen(id);
280
281   /* Do we have a tagged command response? */
282   if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
283     line += id_len + 1;
284     len -= id_len + 1;
285
286     if(len >= 2 && !memcmp(line, "OK", 2))
287       *resp = 'O';
288     else if(len >= 2 && !memcmp(line, "NO", 2))
289       *resp = 'N';
290     else if(len >= 3 && !memcmp(line, "BAD", 3))
291       *resp = 'B';
292     else {
293       failf(conn->data, "Bad tagged response");
294       *resp = -1;
295     }
296
297     return TRUE;
298   }
299
300   /* Do we have an untagged command response? */
301   if(len >= 2 && !memcmp("* ", line, 2)) {
302     switch(imapc->state) {
303       /* States which are interested in untagged responses */
304       case IMAP_CAPABILITY:
305         if(!imap_matchresp(line, len, "CAPABILITY"))
306           return FALSE;
307         break;
308
309       case IMAP_LIST:
310         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
311           (imap->custom && !imap_matchresp(line, len, imap->custom) &&
312            (strcmp(imap->custom, "STORE") ||
313             !imap_matchresp(line, len, "FETCH")) &&
314            strcmp(imap->custom, "SELECT") &&
315            strcmp(imap->custom, "EXAMINE") &&
316            strcmp(imap->custom, "SEARCH") &&
317            strcmp(imap->custom, "EXPUNGE") &&
318            strcmp(imap->custom, "LSUB") &&
319            strcmp(imap->custom, "UID") &&
320            strcmp(imap->custom, "NOOP")))
321           return FALSE;
322         break;
323
324       case IMAP_SELECT:
325         /* SELECT is special in that its untagged responses do not have a
326            common prefix so accept anything! */
327         break;
328
329       case IMAP_FETCH:
330         if(!imap_matchresp(line, len, "FETCH"))
331           return FALSE;
332         break;
333
334       case IMAP_SEARCH:
335         if(!imap_matchresp(line, len, "SEARCH"))
336           return FALSE;
337         break;
338
339       /* Ignore other untagged responses */
340       default:
341         return FALSE;
342     }
343
344     *resp = '*';
345     return TRUE;
346   }
347
348   /* Do we have a continuation response? This should be a + symbol followed by
349      a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
350      APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
351      some e-mail servers ignore this and only send a single + instead. */
352   if((len == 3 && !memcmp("+", line, 1)) ||
353      (len >= 2 && !memcmp("+ ", line, 2))) {
354     switch(imapc->state) {
355       /* States which are interested in continuation responses */
356       case IMAP_AUTHENTICATE_PLAIN:
357       case IMAP_AUTHENTICATE_LOGIN:
358       case IMAP_AUTHENTICATE_LOGIN_PASSWD:
359       case IMAP_AUTHENTICATE_CRAMMD5:
360       case IMAP_AUTHENTICATE_DIGESTMD5:
361       case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
362       case IMAP_AUTHENTICATE_NTLM:
363       case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
364       case IMAP_AUTHENTICATE_XOAUTH2:
365       case IMAP_AUTHENTICATE_FINAL:
366       case IMAP_APPEND:
367         *resp = '+';
368         break;
369
370       default:
371         failf(conn->data, "Unexpected continuation response");
372         *resp = -1;
373         break;
374     }
375
376     return TRUE;
377   }
378
379   return FALSE; /* Nothing for us */
380 }
381
382 /***********************************************************************
383  *
384  * imap_get_message()
385  *
386  * Gets the authentication message from the response buffer.
387  */
388 static void imap_get_message(char *buffer, char** outptr)
389 {
390   size_t len = 0;
391   char* message = NULL;
392
393   /* Find the start of the message */
394   for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
395     ;
396
397   /* Find the end of the message */
398   for(len = strlen(message); len--;)
399     if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
400         message[len] != '\t')
401       break;
402
403   /* Terminate the message */
404   if(++len) {
405     message[len] = '\0';
406   }
407
408   *outptr = message;
409 }
410
411 /***********************************************************************
412  *
413  * state()
414  *
415  * This is the ONLY way to change IMAP state!
416  */
417 static void state(struct connectdata *conn, imapstate newstate)
418 {
419   struct imap_conn *imapc = &conn->proto.imapc;
420 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
421   /* for debug purposes */
422   static const char * const names[]={
423     "STOP",
424     "SERVERGREET",
425     "CAPABILITY",
426     "STARTTLS",
427     "UPGRADETLS",
428     "AUTHENTICATE_PLAIN",
429     "AUTHENTICATE_LOGIN",
430     "AUTHENTICATE_LOGIN_PASSWD",
431     "AUTHENTICATE_CRAMMD5",
432     "AUTHENTICATE_DIGESTMD5",
433     "AUTHENTICATE_DIGESTMD5_RESP",
434     "AUTHENTICATE_NTLM",
435     "AUTHENTICATE_NTLM_TYPE2MSG",
436     "AUTHENTICATE_GSSAPI",
437     "AUTHENTICATE_GSSAPI_TOKEN",
438     "AUTHENTICATE_GSSAPI_NO_DATA",
439     "AUTHENTICATE_XOAUTH2",
440     "AUTHENTICATE_CANCEL",
441     "AUTHENTICATE_FINAL",
442     "LOGIN",
443     "LIST",
444     "SELECT",
445     "FETCH",
446     "FETCH_FINAL",
447     "APPEND",
448     "APPEND_FINAL",
449     "SEARCH",
450     "LOGOUT",
451     /* LAST */
452   };
453
454   if(imapc->state != newstate)
455     infof(conn->data, "IMAP %p state change from %s to %s\n",
456           (void *)imapc, names[imapc->state], names[newstate]);
457 #endif
458
459   imapc->state = newstate;
460 }
461
462 /***********************************************************************
463  *
464  * imap_perform_capability()
465  *
466  * Sends the CAPABILITY command in order to obtain a list of server side
467  * supported capabilities.
468  */
469 static CURLcode imap_perform_capability(struct connectdata *conn)
470 {
471   CURLcode result = CURLE_OK;
472   struct imap_conn *imapc = &conn->proto.imapc;
473
474   imapc->authmechs = 0;         /* No known authentication mechanisms yet */
475   imapc->authused = 0;          /* Clear the authentication mechanism used */
476   imapc->tls_supported = FALSE; /* Clear the TLS capability */
477
478   /* Send the CAPABILITY command */
479   result = imap_sendf(conn, "CAPABILITY");
480
481   if(!result)
482     state(conn, IMAP_CAPABILITY);
483
484   return result;
485 }
486
487 /***********************************************************************
488  *
489  * imap_perform_starttls()
490  *
491  * Sends the STARTTLS command to start the upgrade to TLS.
492  */
493 static CURLcode imap_perform_starttls(struct connectdata *conn)
494 {
495   CURLcode result = CURLE_OK;
496
497   /* Send the STARTTLS command */
498   result = imap_sendf(conn, "STARTTLS");
499
500   if(!result)
501     state(conn, IMAP_STARTTLS);
502
503   return result;
504 }
505
506 /***********************************************************************
507  *
508  * imap_perform_upgrade_tls()
509  *
510  * Performs the upgrade to TLS.
511  */
512 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
513 {
514   CURLcode result = CURLE_OK;
515   struct imap_conn *imapc = &conn->proto.imapc;
516
517   /* Start the SSL connection */
518   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
519
520   if(!result) {
521     if(imapc->state != IMAP_UPGRADETLS)
522       state(conn, IMAP_UPGRADETLS);
523
524     if(imapc->ssldone) {
525       imap_to_imaps(conn);
526       result = imap_perform_capability(conn);
527     }
528   }
529
530   return result;
531 }
532
533 /***********************************************************************
534  *
535  * imap_perform_login()
536  *
537  * Sends a clear text LOGIN command to authenticate with.
538  */
539 static CURLcode imap_perform_login(struct connectdata *conn)
540 {
541   CURLcode result = CURLE_OK;
542   char *user;
543   char *passwd;
544
545   /* Check we have a username and password to authenticate with and end the
546      connect phase if we don't */
547   if(!conn->bits.user_passwd) {
548     state(conn, IMAP_STOP);
549
550     return result;
551   }
552
553   /* Make sure the username and password are in the correct atom format */
554   user = imap_atom(conn->user);
555   passwd = imap_atom(conn->passwd);
556
557   /* Send the LOGIN command */
558   result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
559                       passwd ? passwd : "");
560
561   Curl_safefree(user);
562   Curl_safefree(passwd);
563
564   if(!result)
565     state(conn, IMAP_LOGIN);
566
567   return result;
568 }
569
570 /***********************************************************************
571  *
572  * imap_perform_authenticate()
573  *
574  * Sends an AUTHENTICATE command allowing the client to login with the given
575  * SASL authentication mechanism.
576  */
577 static CURLcode imap_perform_authenticate(struct connectdata *conn,
578                                           const char *mech,
579                                           const char *initresp,
580                                           imapstate state1, imapstate state2)
581 {
582   CURLcode result = CURLE_OK;
583
584   if(initresp) {
585     /* Send the AUTHENTICATE command with the initial response */
586     result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
587
588     if(!result)
589       state(conn, state2);
590   }
591   else {
592     /* Send the AUTHENTICATE command */
593     result = imap_sendf(conn, "AUTHENTICATE %s", mech);
594
595     if(!result)
596       state(conn, state1);
597   }
598
599   return result;
600 }
601
602 /***********************************************************************
603  *
604  * imap_perform_authentication()
605  *
606  * Initiates the authentication sequence, with the appropriate SASL
607  * authentication mechanism, falling back to clear text should a common
608  * mechanism not be available between the client and server.
609  */
610 static CURLcode imap_perform_authentication(struct connectdata *conn)
611 {
612   CURLcode result = CURLE_OK;
613   struct imap_conn *imapc = &conn->proto.imapc;
614   const char *mech = NULL;
615   char *initresp = NULL;
616   size_t len = 0;
617   imapstate state1 = IMAP_STOP;
618   imapstate state2 = IMAP_STOP;
619
620   /* Check we have a username and password to authenticate with and end the
621      connect phase if we don't */
622   if(!conn->bits.user_passwd) {
623     state(conn, IMAP_STOP);
624
625     return result;
626   }
627
628   /* Calculate the SASL login details */
629   result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
630                                   &state2);
631
632   if(!result) {
633     if(mech && (imapc->preftype & IMAP_TYPE_SASL)) {
634       /* Perform SASL based authentication */
635       result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
636     }
637     else if((!imapc->login_disabled) &&
638             (imapc->preftype & IMAP_TYPE_CLEARTEXT))
639       /* Perform clear text authentication */
640       result = imap_perform_login(conn);
641     else {
642       /* Other mechanisms not supported */
643       infof(conn->data, "No known authentication mechanisms supported!\n");
644       result = CURLE_LOGIN_DENIED;
645     }
646   }
647
648   Curl_safefree(initresp);
649
650   return result;
651 }
652
653 /***********************************************************************
654  *
655  * imap_perform_list()
656  *
657  * Sends a LIST command or an alternative custom request.
658  */
659 static CURLcode imap_perform_list(struct connectdata *conn)
660 {
661   CURLcode result = CURLE_OK;
662   struct SessionHandle *data = conn->data;
663   struct IMAP *imap = data->req.protop;
664   char *mailbox;
665
666   if(imap->custom)
667     /* Send the custom request */
668     result = imap_sendf(conn, "%s%s", imap->custom,
669                         imap->custom_params ? imap->custom_params : "");
670   else {
671     /* Make sure the mailbox is in the correct atom format */
672     mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
673     if(!mailbox)
674       return CURLE_OUT_OF_MEMORY;
675
676     /* Send the LIST command */
677     result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
678
679     Curl_safefree(mailbox);
680   }
681
682   if(!result)
683     state(conn, IMAP_LIST);
684
685   return result;
686 }
687
688 /***********************************************************************
689  *
690  * imap_perform_select()
691  *
692  * Sends a SELECT command to ask the server to change the selected mailbox.
693  */
694 static CURLcode imap_perform_select(struct connectdata *conn)
695 {
696   CURLcode result = CURLE_OK;
697   struct SessionHandle *data = conn->data;
698   struct IMAP *imap = data->req.protop;
699   struct imap_conn *imapc = &conn->proto.imapc;
700   char *mailbox;
701
702   /* Invalidate old information as we are switching mailboxes */
703   Curl_safefree(imapc->mailbox);
704   Curl_safefree(imapc->mailbox_uidvalidity);
705
706   /* Check we have a mailbox */
707   if(!imap->mailbox) {
708     failf(conn->data, "Cannot SELECT without a mailbox.");
709     return CURLE_URL_MALFORMAT;
710   }
711
712   /* Make sure the mailbox is in the correct atom format */
713   mailbox = imap_atom(imap->mailbox);
714   if(!mailbox)
715     return CURLE_OUT_OF_MEMORY;
716
717   /* Send the SELECT command */
718   result = imap_sendf(conn, "SELECT %s", mailbox);
719
720   Curl_safefree(mailbox);
721
722   if(!result)
723     state(conn, IMAP_SELECT);
724
725   return result;
726 }
727
728 /***********************************************************************
729  *
730  * imap_perform_fetch()
731  *
732  * Sends a FETCH command to initiate the download of a message.
733  */
734 static CURLcode imap_perform_fetch(struct connectdata *conn)
735 {
736   CURLcode result = CURLE_OK;
737   struct IMAP *imap = conn->data->req.protop;
738
739   /* Check we have a UID */
740   if(!imap->uid) {
741     failf(conn->data, "Cannot FETCH without a UID.");
742     return CURLE_URL_MALFORMAT;
743   }
744
745   /* Send the FETCH command */
746   if(imap->partial)
747     result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
748                         imap->uid,
749                         imap->section ? imap->section : "",
750                         imap->partial);
751   else
752     result = imap_sendf(conn, "FETCH %s BODY[%s]",
753                         imap->uid,
754                         imap->section ? imap->section : "");
755
756   if(!result)
757     state(conn, IMAP_FETCH);
758
759   return result;
760 }
761
762 /***********************************************************************
763  *
764  * imap_perform_append()
765  *
766  * Sends an APPEND command to initiate the upload of a message.
767  */
768 static CURLcode imap_perform_append(struct connectdata *conn)
769 {
770   CURLcode result = CURLE_OK;
771   struct IMAP *imap = conn->data->req.protop;
772   char *mailbox;
773
774   /* Check we have a mailbox */
775   if(!imap->mailbox) {
776     failf(conn->data, "Cannot APPEND without a mailbox.");
777     return CURLE_URL_MALFORMAT;
778   }
779
780   /* Check we know the size of the upload */
781   if(conn->data->state.infilesize < 0) {
782     failf(conn->data, "Cannot APPEND with unknown input file size\n");
783     return CURLE_UPLOAD_FAILED;
784   }
785
786   /* Make sure the mailbox is in the correct atom format */
787   mailbox = imap_atom(imap->mailbox);
788   if(!mailbox)
789     return CURLE_OUT_OF_MEMORY;
790
791   /* Send the APPEND command */
792   result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
793                       mailbox, conn->data->state.infilesize);
794
795   Curl_safefree(mailbox);
796
797   if(!result)
798     state(conn, IMAP_APPEND);
799
800   return result;
801 }
802
803 /***********************************************************************
804  *
805  * imap_perform_search()
806  *
807  * Sends a SEARCH command.
808  */
809 static CURLcode imap_perform_search(struct connectdata *conn)
810 {
811   CURLcode result = CURLE_OK;
812   struct IMAP *imap = conn->data->req.protop;
813
814   /* Check we have a query string */
815   if(!imap->query) {
816     failf(conn->data, "Cannot SEARCH without a query string.");
817     return CURLE_URL_MALFORMAT;
818   }
819
820   /* Send the SEARCH command */
821   result = imap_sendf(conn, "SEARCH %s", imap->query);
822
823   if(!result)
824     state(conn, IMAP_SEARCH);
825
826   return result;
827 }
828
829 /***********************************************************************
830  *
831  * imap_perform_logout()
832  *
833  * Performs the logout action prior to sclose() being called.
834  */
835 static CURLcode imap_perform_logout(struct connectdata *conn)
836 {
837   CURLcode result = CURLE_OK;
838
839   /* Send the LOGOUT command */
840   result = imap_sendf(conn, "LOGOUT");
841
842   if(!result)
843     state(conn, IMAP_LOGOUT);
844
845   return result;
846 }
847
848 /* For the initial server greeting */
849 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
850                                             int imapcode,
851                                             imapstate instate)
852 {
853   CURLcode result = CURLE_OK;
854   struct SessionHandle *data = conn->data;
855
856   (void)instate; /* no use for this yet */
857
858   if(imapcode != 'O') {
859     failf(data, "Got unexpected imap-server response");
860     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
861   }
862   else
863     result = imap_perform_capability(conn);
864
865   return result;
866 }
867
868 /* For CAPABILITY responses */
869 static CURLcode imap_state_capability_resp(struct connectdata *conn,
870                                            int imapcode,
871                                            imapstate instate)
872 {
873   CURLcode result = CURLE_OK;
874   struct SessionHandle *data = conn->data;
875   struct imap_conn *imapc = &conn->proto.imapc;
876   const char *line = data->state.buffer;
877   size_t wordlen;
878
879   (void)instate; /* no use for this yet */
880
881   /* Do we have a untagged response? */
882   if(imapcode == '*') {
883     line += 2;
884
885     /* Loop through the data line */
886     for(;;) {
887       while(*line &&
888             (*line == ' ' || *line == '\t' ||
889               *line == '\r' || *line == '\n')) {
890
891         line++;
892       }
893
894       if(!*line)
895         break;
896
897       /* Extract the word */
898       for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
899             line[wordlen] != '\t' && line[wordlen] != '\r' &&
900             line[wordlen] != '\n';)
901         wordlen++;
902
903       /* Does the server support the STARTTLS capability? */
904       if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
905         imapc->tls_supported = TRUE;
906
907       /* Has the server explicitly disabled clear text authentication? */
908       else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
909         imapc->login_disabled = TRUE;
910
911       /* Does the server support the SASL-IR capability? */
912       else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
913         imapc->ir_supported = TRUE;
914
915       /* Do we have a SASL based authentication mechanism? */
916       else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
917         line += 5;
918         wordlen -= 5;
919
920         /* Test the word for a matching authentication mechanism */
921         if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
922           imapc->authmechs |= SASL_MECH_LOGIN;
923         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
924           imapc->authmechs |= SASL_MECH_PLAIN;
925         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
926           imapc->authmechs |= SASL_MECH_CRAM_MD5;
927         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
928           imapc->authmechs |= SASL_MECH_DIGEST_MD5;
929         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
930           imapc->authmechs |= SASL_MECH_GSSAPI;
931         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
932           imapc->authmechs |= SASL_MECH_EXTERNAL;
933         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
934           imapc->authmechs |= SASL_MECH_NTLM;
935         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
936           imapc->authmechs |= SASL_MECH_XOAUTH2;
937       }
938
939       line += wordlen;
940     }
941   }
942   else if(imapcode == 'O') {
943     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
944       /* We don't have a SSL/TLS connection yet, but SSL is requested */
945       if(imapc->tls_supported)
946         /* Switch to TLS connection now */
947         result = imap_perform_starttls(conn);
948       else if(data->set.use_ssl == CURLUSESSL_TRY)
949         /* Fallback and carry on with authentication */
950         result = imap_perform_authentication(conn);
951       else {
952         failf(data, "STARTTLS not supported.");
953         result = CURLE_USE_SSL_FAILED;
954       }
955     }
956     else
957       result = imap_perform_authentication(conn);
958   }
959   else
960     result = imap_perform_authentication(conn);
961
962   return result;
963 }
964
965 /* For STARTTLS responses */
966 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
967                                          int imapcode,
968                                          imapstate instate)
969 {
970   CURLcode result = CURLE_OK;
971   struct SessionHandle *data = conn->data;
972
973   (void)instate; /* no use for this yet */
974
975   if(imapcode != 'O') {
976     if(data->set.use_ssl != CURLUSESSL_TRY) {
977       failf(data, "STARTTLS denied. %c", imapcode);
978       result = CURLE_USE_SSL_FAILED;
979     }
980     else
981       result = imap_perform_authentication(conn);
982   }
983   else
984     result = imap_perform_upgrade_tls(conn);
985
986   return result;
987 }
988
989 /* For AUTHENTICATE PLAIN (without initial response) responses */
990 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
991                                            int imapcode,
992                                            imapstate instate)
993 {
994   CURLcode result = CURLE_OK;
995   struct SessionHandle *data = conn->data;
996   size_t len = 0;
997   char *plainauth = NULL;
998
999   (void)instate; /* no use for this yet */
1000
1001   if(imapcode != '+') {
1002     failf(data, "Access denied. %c", imapcode);
1003     result = CURLE_LOGIN_DENIED;
1004   }
1005   else {
1006     /* Create the authorisation message */
1007     result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
1008                                             &plainauth, &len);
1009     if(!result && plainauth) {
1010       /* Send the message */
1011       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
1012
1013       if(!result)
1014         state(conn, IMAP_AUTHENTICATE_FINAL);
1015     }
1016   }
1017
1018   Curl_safefree(plainauth);
1019
1020   return result;
1021 }
1022
1023 /* For AUTHENTICATE LOGIN (without initial response) responses */
1024 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1025                                            int imapcode,
1026                                            imapstate instate)
1027 {
1028   CURLcode result = CURLE_OK;
1029   struct SessionHandle *data = conn->data;
1030   size_t len = 0;
1031   char *authuser = NULL;
1032
1033   (void)instate; /* no use for this yet */
1034
1035   if(imapcode != '+') {
1036     failf(data, "Access denied: %d", imapcode);
1037     result = CURLE_LOGIN_DENIED;
1038   }
1039   else {
1040     /* Create the user message */
1041     result = Curl_sasl_create_login_message(data, conn->user,
1042                                             &authuser, &len);
1043     if(!result && authuser) {
1044       /* Send the user */
1045       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1046
1047       if(!result)
1048         state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1049     }
1050   }
1051
1052   Curl_safefree(authuser);
1053
1054   return result;
1055 }
1056
1057 /* For AUTHENTICATE LOGIN user entry responses */
1058 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1059                                                     int imapcode,
1060                                                     imapstate instate)
1061 {
1062   CURLcode result = CURLE_OK;
1063   struct SessionHandle *data = conn->data;
1064   size_t len = 0;
1065   char *authpasswd = NULL;
1066
1067   (void)instate; /* no use for this yet */
1068
1069   if(imapcode != '+') {
1070     failf(data, "Access denied: %d", imapcode);
1071     result = CURLE_LOGIN_DENIED;
1072   }
1073   else {
1074     /* Create the password message */
1075     result = Curl_sasl_create_login_message(data, conn->passwd,
1076                                             &authpasswd, &len);
1077     if(!result && authpasswd) {
1078       /* Send the password */
1079       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1080
1081       if(!result)
1082         state(conn, IMAP_AUTHENTICATE_FINAL);
1083     }
1084   }
1085
1086   Curl_safefree(authpasswd);
1087
1088   return result;
1089 }
1090
1091 #ifndef CURL_DISABLE_CRYPTO_AUTH
1092 /* For AUTHENTICATE CRAM-MD5 responses */
1093 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1094                                           int imapcode,
1095                                           imapstate instate)
1096 {
1097   CURLcode result = CURLE_OK;
1098   struct SessionHandle *data = conn->data;
1099   char *chlg = NULL;
1100   char *chlg64 = NULL;
1101   char *rplyb64 = NULL;
1102   size_t len = 0;
1103
1104   (void)instate; /* no use for this yet */
1105
1106   if(imapcode != '+') {
1107     failf(data, "Access denied: %d", imapcode);
1108     return CURLE_LOGIN_DENIED;
1109   }
1110
1111   /* Get the challenge message */
1112   imap_get_message(data->state.buffer, &chlg64);
1113
1114   /* Decode the challenge message */
1115   result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
1116   if(result) {
1117     /* Send the cancellation */
1118     result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1119
1120     if(!result)
1121       state(conn, IMAP_AUTHENTICATE_CANCEL);
1122   }
1123   else {
1124     /* Create the response message */
1125     result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
1126                                                conn->passwd, &rplyb64, &len);
1127     if(!result && rplyb64) {
1128       /* Send the response */
1129       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1130
1131       if(!result)
1132         state(conn, IMAP_AUTHENTICATE_FINAL);
1133     }
1134   }
1135
1136   Curl_safefree(chlg);
1137   Curl_safefree(rplyb64);
1138
1139   return result;
1140 }
1141
1142 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1143 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1144                                             int imapcode,
1145                                             imapstate instate)
1146 {
1147   CURLcode result = CURLE_OK;
1148   struct SessionHandle *data = conn->data;
1149   char *chlg64 = NULL;
1150   char *rplyb64 = NULL;
1151   size_t len = 0;
1152
1153   (void)instate; /* no use for this yet */
1154
1155   if(imapcode != '+') {
1156     failf(data, "Access denied: %d", imapcode);
1157     return CURLE_LOGIN_DENIED;
1158   }
1159
1160   /* Get the challenge message */
1161   imap_get_message(data->state.buffer, &chlg64);
1162
1163   /* Create the response message */
1164   result = Curl_sasl_create_digest_md5_message(data, chlg64,
1165                                                conn->user, conn->passwd,
1166                                                "imap", &rplyb64, &len);
1167   if(result) {
1168     if(result == CURLE_BAD_CONTENT_ENCODING) {
1169       /* Send the cancellation */
1170       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1171
1172       if(!result)
1173         state(conn, IMAP_AUTHENTICATE_CANCEL);
1174     }
1175   }
1176   else {
1177     /* Send the response */
1178     result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1179
1180     if(!result)
1181       state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1182   }
1183
1184   Curl_safefree(rplyb64);
1185
1186   return result;
1187 }
1188
1189 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1190 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1191                                                  int imapcode,
1192                                                  imapstate instate)
1193 {
1194   CURLcode result = CURLE_OK;
1195   struct SessionHandle *data = conn->data;
1196
1197   (void)instate; /* no use for this yet */
1198
1199   if(imapcode != '+') {
1200     failf(data, "Authentication failed: %d", imapcode);
1201     result = CURLE_LOGIN_DENIED;
1202   }
1203   else {
1204     /* Send an empty response */
1205     result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1206
1207     if(!result)
1208       state(conn, IMAP_AUTHENTICATE_FINAL);
1209   }
1210
1211   return result;
1212 }
1213 #endif
1214
1215 #ifdef USE_NTLM
1216 /* For AUTHENTICATE NTLM (without initial response) responses */
1217 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1218                                           int imapcode,
1219                                           imapstate instate)
1220 {
1221   CURLcode result = CURLE_OK;
1222   struct SessionHandle *data = conn->data;
1223   size_t len = 0;
1224   char *type1msg = NULL;
1225
1226   (void)instate; /* no use for this yet */
1227
1228   if(imapcode != '+') {
1229     failf(data, "Access denied: %d", imapcode);
1230     result = CURLE_LOGIN_DENIED;
1231   }
1232   else {
1233     /* Create the type-1 message */
1234     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1235                                                  &conn->ntlm,
1236                                                  &type1msg, &len);
1237     if(!result && type1msg) {
1238       /* Send the message */
1239       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1240
1241       if(!result)
1242         state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1243     }
1244   }
1245
1246   Curl_safefree(type1msg);
1247
1248   return result;
1249 }
1250
1251 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1252 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1253                                                    int imapcode,
1254                                                    imapstate instate)
1255 {
1256   CURLcode result = CURLE_OK;
1257   struct SessionHandle *data = conn->data;
1258   char *type2msg = NULL;
1259   char *type3msg = NULL;
1260   size_t len = 0;
1261
1262   (void)instate; /* no use for this yet */
1263
1264   if(imapcode != '+') {
1265     failf(data, "Access denied: %d", imapcode);
1266     result = CURLE_LOGIN_DENIED;
1267   }
1268   else {
1269     /* Get the challenge message */
1270     imap_get_message(data->state.buffer, &type2msg);
1271
1272     /* Decode the type-2 message */
1273     result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1274     if(result) {
1275       /* Send the cancellation */
1276       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1277
1278       if(!result)
1279         state(conn, IMAP_AUTHENTICATE_CANCEL);
1280     }
1281     else {
1282       /* Create the type-3 message */
1283       result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1284                                                    conn->passwd, &conn->ntlm,
1285                                                    &type3msg, &len);
1286       if(!result && type3msg) {
1287         /* Send the message */
1288         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1289
1290         if(!result)
1291           state(conn, IMAP_AUTHENTICATE_FINAL);
1292       }
1293     }
1294   }
1295
1296   Curl_safefree(type3msg);
1297
1298   return result;
1299 }
1300 #endif
1301
1302 #if defined(USE_KERBEROS5)
1303 /* For AUTHENTICATE GSSAPI (without initial response) responses */
1304 static CURLcode imap_state_auth_gssapi_resp(struct connectdata *conn,
1305                                             int imapcode,
1306                                             imapstate instate)
1307 {
1308   CURLcode result = CURLE_OK;
1309   struct SessionHandle *data = conn->data;
1310   struct imap_conn *imapc = &conn->proto.imapc;
1311   size_t len = 0;
1312   char *respmsg = NULL;
1313
1314   (void)instate; /* no use for this yet */
1315
1316   if(imapcode != '+') {
1317     failf(data, "Access denied: %d", imapcode);
1318     result = CURLE_LOGIN_DENIED;
1319   }
1320   else {
1321     /* Create the initial response message */
1322     result = Curl_sasl_create_gssapi_user_message(data, conn->user,
1323                                                   conn->passwd, "imap",
1324                                                   imapc->mutual_auth,
1325                                                   NULL, &conn->krb5,
1326                                                   &respmsg, &len);
1327     if(!result && respmsg) {
1328       /* Send the message */
1329       result = Curl_pp_sendf(&imapc->pp, "%s", respmsg);
1330
1331       if(!result)
1332         state(conn, IMAP_AUTHENTICATE_GSSAPI_TOKEN);
1333     }
1334   }
1335
1336   Curl_safefree(respmsg);
1337
1338   return result;
1339 }
1340
1341 /* For AUTHENTICATE GSSAPI user token responses */
1342 static CURLcode imap_state_auth_gssapi_token_resp(struct connectdata *conn,
1343                                                   int imapcode,
1344                                                   imapstate instate)
1345 {
1346   CURLcode result = CURLE_OK;
1347   struct SessionHandle *data = conn->data;
1348   struct imap_conn *imapc = &conn->proto.imapc;
1349   char *chlgmsg = NULL;
1350   char *respmsg = NULL;
1351   size_t len = 0;
1352
1353   (void)instate; /* no use for this yet */
1354
1355   if(imapcode != '+') {
1356     failf(data, "Access denied: %d", imapcode);
1357     result = CURLE_LOGIN_DENIED;
1358   }
1359   else {
1360     /* Get the challenge message */
1361     imap_get_message(data->state.buffer, &chlgmsg);
1362
1363     if(imapc->mutual_auth)
1364       /* Decode the user token challenge and create the optional response
1365          message */
1366       result = Curl_sasl_create_gssapi_user_message(data, NULL, NULL, NULL,
1367                                                     imapc->mutual_auth,
1368                                                     chlgmsg, &conn->krb5,
1369                                                     &respmsg, &len);
1370     else
1371       /* Decode the security challenge and create the response message */
1372       result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
1373                                                         &conn->krb5,
1374                                                         &respmsg, &len);
1375
1376     if(result) {
1377       if(result == CURLE_BAD_CONTENT_ENCODING) {
1378         /* Send the cancellation */
1379         result = Curl_pp_sendf(&imapc->pp, "%s", "*");
1380
1381         if(!result)
1382           state(conn, IMAP_AUTHENTICATE_CANCEL);
1383       }
1384     }
1385     else {
1386       /* Send the response */
1387       if(respmsg)
1388         result = Curl_pp_sendf(&imapc->pp, "%s", respmsg);
1389       else
1390         result = Curl_pp_sendf(&imapc->pp, "%s", "");
1391
1392       if(!result)
1393         state(conn, (imapc->mutual_auth ? IMAP_AUTHENTICATE_GSSAPI_NO_DATA :
1394                                           IMAP_AUTHENTICATE_FINAL));
1395     }
1396   }
1397
1398   Curl_safefree(respmsg);
1399
1400   return result;
1401 }
1402
1403 /* For AUTHENTICATE GSSAPI no data responses */
1404 static CURLcode imap_state_auth_gssapi_no_data_resp(struct connectdata *conn,
1405                                                     int imapcode,
1406                                                     imapstate instate)
1407 {
1408   CURLcode result = CURLE_OK;
1409   struct SessionHandle *data = conn->data;
1410   char *chlgmsg = NULL;
1411   char *respmsg = NULL;
1412   size_t len = 0;
1413
1414   (void)instate; /* no use for this yet */
1415
1416   if(imapcode != '+') {
1417     failf(data, "Access denied: %d", imapcode);
1418     result = CURLE_LOGIN_DENIED;
1419   }
1420   else {
1421     /* Get the challenge message */
1422     imap_get_message(data->state.buffer, &chlgmsg);
1423
1424     /* Decode the security challenge and create the response message */
1425     result = Curl_sasl_create_gssapi_security_message(data, chlgmsg,
1426                                                       &conn->krb5,
1427                                                       &respmsg, &len);
1428     if(result) {
1429       if(result == CURLE_BAD_CONTENT_ENCODING) {
1430         /* Send the cancellation */
1431         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1432
1433         if(!result)
1434           state(conn, IMAP_AUTHENTICATE_CANCEL);
1435       }
1436     }
1437     else {
1438       /* Send the response */
1439       if(respmsg) {
1440         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", respmsg);
1441
1442         if(!result)
1443           state(conn, IMAP_AUTHENTICATE_FINAL);
1444       }
1445     }
1446   }
1447
1448   Curl_safefree(respmsg);
1449
1450   return result;
1451 }
1452 #endif
1453
1454 /* For AUTHENTICATE XOAUTH2 (without initial response) responses */
1455 static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn,
1456                                              int imapcode,
1457                                              imapstate instate)
1458 {
1459   CURLcode result = CURLE_OK;
1460   struct SessionHandle *data = conn->data;
1461   size_t len = 0;
1462   char *xoauth = NULL;
1463
1464   (void)instate; /* no use for this yet */
1465
1466   if(imapcode != '+') {
1467     failf(data, "Access denied: %d", imapcode);
1468     result = CURLE_LOGIN_DENIED;
1469   }
1470   else {
1471     /* Create the authorisation message */
1472     result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1473                                               conn->xoauth2_bearer,
1474                                               &xoauth, &len);
1475     if(!result && xoauth) {
1476       /* Send the message */
1477       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth);
1478
1479       if(!result)
1480         state(conn, IMAP_AUTHENTICATE_FINAL);
1481     }
1482   }
1483
1484   Curl_safefree(xoauth);
1485
1486   return result;
1487 }
1488
1489 /* For AUTHENTICATE cancellation responses */
1490 static CURLcode imap_state_auth_cancel_resp(struct connectdata *conn,
1491                                             int imapcode,
1492                                             imapstate instate)
1493 {
1494   CURLcode result = CURLE_OK;
1495   struct SessionHandle *data = conn->data;
1496   struct imap_conn *imapc = &conn->proto.imapc;
1497   const char *mech = NULL;
1498   char *initresp = NULL;
1499   size_t len = 0;
1500   imapstate state1 = IMAP_STOP;
1501   imapstate state2 = IMAP_STOP;
1502
1503   (void)imapcode;
1504   (void)instate; /* no use for this yet */
1505
1506   /* Remove the offending mechanism from the supported list */
1507   imapc->authmechs ^= imapc->authused;
1508
1509   /* Calculate alternative SASL login details */
1510   result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1511                                   &state2);
1512
1513   if(!result) {
1514     /* Do we have any mechanisms left or can we fallback to clear text? */
1515     if(mech) {
1516       /* Retry SASL based authentication */
1517       result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
1518
1519       Curl_safefree(initresp);
1520     }
1521     else if((!imapc->login_disabled) &&
1522             (imapc->preftype & IMAP_TYPE_CLEARTEXT))
1523       /* Perform clear text authentication */
1524       result = imap_perform_login(conn);
1525     else {
1526       failf(data, "Authentication cancelled");
1527
1528       result = CURLE_LOGIN_DENIED;
1529     }
1530   }
1531
1532   return result;
1533 }
1534
1535 /* For final responses in the AUTHENTICATE sequence */
1536 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1537                                            int imapcode,
1538                                            imapstate instate)
1539 {
1540   CURLcode result = CURLE_OK;
1541   struct SessionHandle *data = conn->data;
1542
1543   (void)instate; /* no use for this yet */
1544
1545   if(imapcode != 'O') {
1546     failf(data, "Authentication failed: %d", imapcode);
1547     result = CURLE_LOGIN_DENIED;
1548   }
1549   else
1550     /* End of connect phase */
1551     state(conn, IMAP_STOP);
1552
1553   return result;
1554 }
1555
1556 /* For LOGIN responses */
1557 static CURLcode imap_state_login_resp(struct connectdata *conn,
1558                                       int imapcode,
1559                                       imapstate instate)
1560 {
1561   CURLcode result = CURLE_OK;
1562   struct SessionHandle *data = conn->data;
1563
1564   (void)instate; /* no use for this yet */
1565
1566   if(imapcode != 'O') {
1567     failf(data, "Access denied. %c", imapcode);
1568     result = CURLE_LOGIN_DENIED;
1569   }
1570   else
1571     /* End of connect phase */
1572     state(conn, IMAP_STOP);
1573
1574   return result;
1575 }
1576
1577 /* For LIST responses */
1578 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1579                                      imapstate instate)
1580 {
1581   CURLcode result = CURLE_OK;
1582   char *line = conn->data->state.buffer;
1583   size_t len = strlen(line);
1584
1585   (void)instate; /* No use for this yet */
1586
1587   if(imapcode == '*') {
1588     /* Temporarily add the LF character back and send as body to the client */
1589     line[len] = '\n';
1590     result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1591     line[len] = '\0';
1592   }
1593   else if(imapcode != 'O')
1594     result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1595   else
1596     /* End of DO phase */
1597     state(conn, IMAP_STOP);
1598
1599   return result;
1600 }
1601
1602 /* For SELECT responses */
1603 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1604                                        imapstate instate)
1605 {
1606   CURLcode result = CURLE_OK;
1607   struct SessionHandle *data = conn->data;
1608   struct IMAP *imap = conn->data->req.protop;
1609   struct imap_conn *imapc = &conn->proto.imapc;
1610   const char *line = data->state.buffer;
1611   char tmp[20];
1612
1613   (void)instate; /* no use for this yet */
1614
1615   if(imapcode == '*') {
1616     /* See if this is an UIDVALIDITY response */
1617     if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1618       Curl_safefree(imapc->mailbox_uidvalidity);
1619       imapc->mailbox_uidvalidity = strdup(tmp);
1620     }
1621   }
1622   else if(imapcode == 'O') {
1623     /* Check if the UIDVALIDITY has been specified and matches */
1624     if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1625        strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1626       failf(conn->data, "Mailbox UIDVALIDITY has changed");
1627       result = CURLE_REMOTE_FILE_NOT_FOUND;
1628     }
1629     else {
1630       /* Note the currently opened mailbox on this connection */
1631       imapc->mailbox = strdup(imap->mailbox);
1632
1633       if(imap->custom)
1634         result = imap_perform_list(conn);
1635       else if(imap->query)
1636         result = imap_perform_search(conn);
1637       else
1638         result = imap_perform_fetch(conn);
1639     }
1640   }
1641   else {
1642     failf(data, "Select failed");
1643     result = CURLE_LOGIN_DENIED;
1644   }
1645
1646   return result;
1647 }
1648
1649 /* For the (first line of the) FETCH responses */
1650 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1651                                       imapstate instate)
1652 {
1653   CURLcode result = CURLE_OK;
1654   struct SessionHandle *data = conn->data;
1655   struct imap_conn *imapc = &conn->proto.imapc;
1656   struct pingpong *pp = &imapc->pp;
1657   const char *ptr = data->state.buffer;
1658   bool parsed = FALSE;
1659   curl_off_t size;
1660
1661   (void)instate; /* no use for this yet */
1662
1663   if(imapcode != '*') {
1664     Curl_pgrsSetDownloadSize(data, -1);
1665     state(conn, IMAP_STOP);
1666     return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1667   }
1668
1669   /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1670      the continuation data contained within the curly brackets */
1671   while(*ptr && (*ptr != '{'))
1672     ptr++;
1673
1674   if(*ptr == '{') {
1675     char *endptr;
1676     size = curlx_strtoofft(ptr + 1, &endptr, 10);
1677     if(endptr - ptr > 1 && endptr[0] == '}' &&
1678        endptr[1] == '\r' && endptr[2] == '\0')
1679       parsed = TRUE;
1680   }
1681
1682   if(parsed) {
1683     infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1684           size);
1685     Curl_pgrsSetDownloadSize(data, size);
1686
1687     if(pp->cache) {
1688       /* At this point there is a bunch of data in the header "cache" that is
1689          actually body content, send it as body and then skip it. Do note
1690          that there may even be additional "headers" after the body. */
1691       size_t chunk = pp->cache_size;
1692
1693       if(chunk > (size_t)size)
1694         /* The conversion from curl_off_t to size_t is always fine here */
1695         chunk = (size_t)size;
1696
1697       result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1698       if(result)
1699         return result;
1700
1701       data->req.bytecount += chunk;
1702
1703       infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1704             " bytes, %" CURL_FORMAT_CURL_OFF_TU
1705             " bytes are left for transfer\n", (curl_off_t)chunk,
1706             size - chunk);
1707
1708       /* Have we used the entire cache or just part of it?*/
1709       if(pp->cache_size > chunk) {
1710         /* Only part of it so shrink the cache to fit the trailing data */
1711         memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1712         pp->cache_size -= chunk;
1713       }
1714       else {
1715         /* Free the cache */
1716         Curl_safefree(pp->cache);
1717
1718         /* Reset the cache size */
1719         pp->cache_size = 0;
1720       }
1721     }
1722
1723     if(data->req.bytecount == size)
1724       /* The entire data is already transferred! */
1725       Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1726     else {
1727       /* IMAP download */
1728       data->req.maxdownload = size;
1729       Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1730     }
1731   }
1732   else {
1733     /* We don't know how to parse this line */
1734     failf(pp->conn->data, "Failed to parse FETCH response.");
1735     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1736   }
1737
1738   /* End of DO phase */
1739   state(conn, IMAP_STOP);
1740
1741   return result;
1742 }
1743
1744 /* For final FETCH responses performed after the download */
1745 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1746                                             int imapcode,
1747                                             imapstate instate)
1748 {
1749   CURLcode result = CURLE_OK;
1750
1751   (void)instate; /* No use for this yet */
1752
1753   if(imapcode != 'O')
1754     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1755   else
1756     /* End of DONE phase */
1757     state(conn, IMAP_STOP);
1758
1759   return result;
1760 }
1761
1762 /* For APPEND responses */
1763 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1764                                        imapstate instate)
1765 {
1766   CURLcode result = CURLE_OK;
1767   struct SessionHandle *data = conn->data;
1768
1769   (void)instate; /* No use for this yet */
1770
1771   if(imapcode != '+') {
1772     result = CURLE_UPLOAD_FAILED;
1773   }
1774   else {
1775     /* Set the progress upload size */
1776     Curl_pgrsSetUploadSize(data, data->state.infilesize);
1777
1778     /* IMAP upload */
1779     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1780
1781     /* End of DO phase */
1782     state(conn, IMAP_STOP);
1783   }
1784
1785   return result;
1786 }
1787
1788 /* For final APPEND responses performed after the upload */
1789 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1790                                              int imapcode,
1791                                              imapstate instate)
1792 {
1793   CURLcode result = CURLE_OK;
1794
1795   (void)instate; /* No use for this yet */
1796
1797   if(imapcode != 'O')
1798     result = CURLE_UPLOAD_FAILED;
1799   else
1800     /* End of DONE phase */
1801     state(conn, IMAP_STOP);
1802
1803   return result;
1804 }
1805
1806 /* For SEARCH responses */
1807 static CURLcode imap_state_search_resp(struct connectdata *conn, int imapcode,
1808                                        imapstate instate)
1809 {
1810   CURLcode result = CURLE_OK;
1811   char *line = conn->data->state.buffer;
1812   size_t len = strlen(line);
1813
1814   (void)instate; /* No use for this yet */
1815
1816   if(imapcode == '*') {
1817     /* Temporarily add the LF character back and send as body to the client */
1818     line[len] = '\n';
1819     result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1820     line[len] = '\0';
1821   }
1822   else if(imapcode != 'O')
1823     result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1824   else
1825     /* End of DO phase */
1826     state(conn, IMAP_STOP);
1827
1828   return result;
1829 }
1830
1831 static CURLcode imap_statemach_act(struct connectdata *conn)
1832 {
1833   CURLcode result = CURLE_OK;
1834   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1835   int imapcode;
1836   struct imap_conn *imapc = &conn->proto.imapc;
1837   struct pingpong *pp = &imapc->pp;
1838   size_t nread = 0;
1839
1840   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1841   if(imapc->state == IMAP_UPGRADETLS)
1842     return imap_perform_upgrade_tls(conn);
1843
1844   /* Flush any data that needs to be sent */
1845   if(pp->sendleft)
1846     return Curl_pp_flushsend(pp);
1847
1848   do {
1849     /* Read the response from the server */
1850     result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1851     if(result)
1852       return result;
1853
1854     /* Was there an error parsing the response line? */
1855     if(imapcode == -1)
1856       return CURLE_FTP_WEIRD_SERVER_REPLY;
1857
1858     if(!imapcode)
1859       break;
1860
1861     /* We have now received a full IMAP server response */
1862     switch(imapc->state) {
1863     case IMAP_SERVERGREET:
1864       result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1865       break;
1866
1867     case IMAP_CAPABILITY:
1868       result = imap_state_capability_resp(conn, imapcode, imapc->state);
1869       break;
1870
1871     case IMAP_STARTTLS:
1872       result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1873       break;
1874
1875     case IMAP_AUTHENTICATE_PLAIN:
1876       result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1877       break;
1878
1879     case IMAP_AUTHENTICATE_LOGIN:
1880       result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1881       break;
1882
1883     case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1884       result = imap_state_auth_login_password_resp(conn, imapcode,
1885                                                    imapc->state);
1886       break;
1887
1888 #ifndef CURL_DISABLE_CRYPTO_AUTH
1889     case IMAP_AUTHENTICATE_CRAMMD5:
1890       result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1891       break;
1892
1893     case IMAP_AUTHENTICATE_DIGESTMD5:
1894       result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1895       break;
1896
1897     case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1898       result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1899       break;
1900 #endif
1901
1902 #ifdef USE_NTLM
1903     case IMAP_AUTHENTICATE_NTLM:
1904       result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1905       break;
1906
1907     case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1908       result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1909                                                   imapc->state);
1910       break;
1911 #endif
1912
1913 #if defined(USE_KERBEROS5)
1914     case IMAP_AUTHENTICATE_GSSAPI:
1915       result = imap_state_auth_gssapi_resp(conn, imapcode, imapc->state);
1916       break;
1917
1918     case IMAP_AUTHENTICATE_GSSAPI_TOKEN:
1919       result = imap_state_auth_gssapi_token_resp(conn, imapcode, imapc->state);
1920       break;
1921
1922     case IMAP_AUTHENTICATE_GSSAPI_NO_DATA:
1923       result = imap_state_auth_gssapi_no_data_resp(conn, imapcode,
1924                                                    imapc->state);
1925       break;
1926 #endif
1927
1928     case IMAP_AUTHENTICATE_XOAUTH2:
1929       result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state);
1930       break;
1931
1932     case IMAP_AUTHENTICATE_CANCEL:
1933       result = imap_state_auth_cancel_resp(conn, imapcode, imapc->state);
1934       break;
1935
1936     case IMAP_AUTHENTICATE_FINAL:
1937       result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1938       break;
1939
1940     case IMAP_LOGIN:
1941       result = imap_state_login_resp(conn, imapcode, imapc->state);
1942       break;
1943
1944     case IMAP_LIST:
1945       result = imap_state_list_resp(conn, imapcode, imapc->state);
1946       break;
1947
1948     case IMAP_SELECT:
1949       result = imap_state_select_resp(conn, imapcode, imapc->state);
1950       break;
1951
1952     case IMAP_FETCH:
1953       result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1954       break;
1955
1956     case IMAP_FETCH_FINAL:
1957       result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1958       break;
1959
1960     case IMAP_APPEND:
1961       result = imap_state_append_resp(conn, imapcode, imapc->state);
1962       break;
1963
1964     case IMAP_APPEND_FINAL:
1965       result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1966       break;
1967
1968     case IMAP_SEARCH:
1969       result = imap_state_search_resp(conn, imapcode, imapc->state);
1970       break;
1971
1972     case IMAP_LOGOUT:
1973       /* fallthrough, just stop! */
1974     default:
1975       /* internal error */
1976       state(conn, IMAP_STOP);
1977       break;
1978     }
1979   } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1980
1981   return result;
1982 }
1983
1984 /* Called repeatedly until done from multi.c */
1985 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1986 {
1987   CURLcode result = CURLE_OK;
1988   struct imap_conn *imapc = &conn->proto.imapc;
1989
1990   if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1991     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1992     if(result || !imapc->ssldone)
1993       return result;
1994   }
1995
1996   result = Curl_pp_statemach(&imapc->pp, FALSE);
1997   *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1998
1999   return result;
2000 }
2001
2002 static CURLcode imap_block_statemach(struct connectdata *conn)
2003 {
2004   CURLcode result = CURLE_OK;
2005   struct imap_conn *imapc = &conn->proto.imapc;
2006
2007   while(imapc->state != IMAP_STOP && !result)
2008     result = Curl_pp_statemach(&imapc->pp, TRUE);
2009
2010   return result;
2011 }
2012
2013 /* Allocate and initialize the struct IMAP for the current SessionHandle if
2014    required */
2015 static CURLcode imap_init(struct connectdata *conn)
2016 {
2017   CURLcode result = CURLE_OK;
2018   struct SessionHandle *data = conn->data;
2019   struct IMAP *imap;
2020
2021   imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
2022   if(!imap)
2023     result = CURLE_OUT_OF_MEMORY;
2024
2025   return result;
2026 }
2027
2028 /* For the IMAP "protocol connect" and "doing" phases only */
2029 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
2030                         int numsocks)
2031 {
2032   return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
2033 }
2034
2035 /***********************************************************************
2036  *
2037  * imap_connect()
2038  *
2039  * This function should do everything that is to be considered a part of the
2040  * connection phase.
2041  *
2042  * The variable 'done' points to will be TRUE if the protocol-layer connect
2043  * phase is done when this function returns, or FALSE if not.
2044  */
2045 static CURLcode imap_connect(struct connectdata *conn, bool *done)
2046 {
2047   CURLcode result = CURLE_OK;
2048   struct imap_conn *imapc = &conn->proto.imapc;
2049   struct pingpong *pp = &imapc->pp;
2050
2051   *done = FALSE; /* default to not done yet */
2052
2053   /* We always support persistent connections in IMAP */
2054   connkeep(conn, "IMAP default");
2055
2056   /* Set the default response time-out */
2057   pp->response_time = RESP_TIMEOUT;
2058   pp->statemach_act = imap_statemach_act;
2059   pp->endofresp = imap_endofresp;
2060   pp->conn = conn;
2061
2062   /* Set the default preferred authentication type and mechanism */
2063   imapc->preftype = IMAP_TYPE_ANY;
2064   imapc->prefmech = SASL_AUTH_ANY;
2065
2066   /* Initialise the pingpong layer */
2067   Curl_pp_init(pp);
2068
2069   /* Parse the URL options */
2070   result = imap_parse_url_options(conn);
2071   if(result)
2072     return result;
2073
2074   /* Start off waiting for the server greeting response */
2075   state(conn, IMAP_SERVERGREET);
2076
2077   /* Start off with an response id of '*' */
2078   strcpy(imapc->resptag, "*");
2079
2080   result = imap_multi_statemach(conn, done);
2081
2082   return result;
2083 }
2084
2085 /***********************************************************************
2086  *
2087  * imap_done()
2088  *
2089  * The DONE function. This does what needs to be done after a single DO has
2090  * performed.
2091  *
2092  * Input argument is already checked for validity.
2093  */
2094 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
2095                           bool premature)
2096 {
2097   CURLcode result = CURLE_OK;
2098   struct SessionHandle *data = conn->data;
2099   struct IMAP *imap = data->req.protop;
2100
2101   (void)premature;
2102
2103   if(!imap)
2104     /* When the easy handle is removed from the multi interface while libcurl
2105        is still trying to resolve the host name, the IMAP struct is not yet
2106        initialized. However, the removal action calls Curl_done() which in
2107        turn calls this function, so we simply return success. */
2108     return CURLE_OK;
2109
2110   if(status) {
2111     connclose(conn, "IMAP done with bad status"); /* marked for closure */
2112     result = status;         /* use the already set error code */
2113   }
2114   else if(!data->set.connect_only && !imap->custom &&
2115           (imap->uid || data->set.upload)) {
2116     /* Handle responses after FETCH or APPEND transfer has finished */
2117     if(!data->set.upload)
2118       state(conn, IMAP_FETCH_FINAL);
2119     else {
2120       /* End the APPEND command first by sending an empty line */
2121       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
2122       if(!result)
2123         state(conn, IMAP_APPEND_FINAL);
2124     }
2125
2126     /* Run the state-machine
2127
2128        TODO: when the multi interface is used, this _really_ should be using
2129        the imap_multi_statemach function but we have no general support for
2130        non-blocking DONE operations, not in the multi state machine and with
2131        Curl_done() invokes on several places in the code!
2132     */
2133     if(!result)
2134       result = imap_block_statemach(conn);
2135   }
2136
2137   /* Cleanup our per-request based variables */
2138   Curl_safefree(imap->mailbox);
2139   Curl_safefree(imap->uidvalidity);
2140   Curl_safefree(imap->uid);
2141   Curl_safefree(imap->section);
2142   Curl_safefree(imap->partial);
2143   Curl_safefree(imap->query);
2144   Curl_safefree(imap->custom);
2145   Curl_safefree(imap->custom_params);
2146
2147   /* Clear the transfer mode for the next request */
2148   imap->transfer = FTPTRANSFER_BODY;
2149
2150   return result;
2151 }
2152
2153 /***********************************************************************
2154  *
2155  * imap_perform()
2156  *
2157  * This is the actual DO function for IMAP. Fetch or append a message, or do
2158  * other things according to the options previously setup.
2159  */
2160 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
2161                              bool *dophase_done)
2162 {
2163   /* This is IMAP and no proxy */
2164   CURLcode result = CURLE_OK;
2165   struct SessionHandle *data = conn->data;
2166   struct IMAP *imap = data->req.protop;
2167   struct imap_conn *imapc = &conn->proto.imapc;
2168   bool selected = FALSE;
2169
2170   DEBUGF(infof(conn->data, "DO phase starts\n"));
2171
2172   if(conn->data->set.opt_no_body) {
2173     /* Requested no body means no transfer */
2174     imap->transfer = FTPTRANSFER_INFO;
2175   }
2176
2177   *dophase_done = FALSE; /* not done yet */
2178
2179   /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
2180      has already been selected on this connection */
2181   if(imap->mailbox && imapc->mailbox &&
2182      !strcmp(imap->mailbox, imapc->mailbox) &&
2183      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
2184       !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
2185     selected = TRUE;
2186
2187   /* Start the first command in the DO phase */
2188   if(conn->data->set.upload)
2189     /* APPEND can be executed directly */
2190     result = imap_perform_append(conn);
2191   else if(imap->custom && (selected || !imap->mailbox))
2192     /* Custom command using the same mailbox or no mailbox */
2193     result = imap_perform_list(conn);
2194   else if(!imap->custom && selected && imap->uid)
2195     /* FETCH from the same mailbox */
2196     result = imap_perform_fetch(conn);
2197   else if(!imap->custom && selected && imap->query)
2198     /* SEARCH the current mailbox */
2199     result = imap_perform_search(conn);
2200   else if(imap->mailbox && !selected &&
2201          (imap->custom || imap->uid || imap->query))
2202     /* SELECT the mailbox */
2203     result = imap_perform_select(conn);
2204   else
2205     /* LIST */
2206     result = imap_perform_list(conn);
2207
2208   if(result)
2209     return result;
2210
2211   /* Run the state-machine */
2212   result = imap_multi_statemach(conn, dophase_done);
2213
2214   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
2215
2216   if(*dophase_done)
2217     DEBUGF(infof(conn->data, "DO phase is complete\n"));
2218
2219   return result;
2220 }
2221
2222 /***********************************************************************
2223  *
2224  * imap_do()
2225  *
2226  * This function is registered as 'curl_do' function. It decodes the path
2227  * parts etc as a wrapper to the actual DO function (imap_perform).
2228  *
2229  * The input argument is already checked for validity.
2230  */
2231 static CURLcode imap_do(struct connectdata *conn, bool *done)
2232 {
2233   CURLcode result = CURLE_OK;
2234
2235   *done = FALSE; /* default to false */
2236
2237   /* Parse the URL path */
2238   result = imap_parse_url_path(conn);
2239   if(result)
2240     return result;
2241
2242   /* Parse the custom request */
2243   result = imap_parse_custom_request(conn);
2244   if(result)
2245     return result;
2246
2247   result = imap_regular_transfer(conn, done);
2248
2249   return result;
2250 }
2251
2252 /***********************************************************************
2253  *
2254  * imap_disconnect()
2255  *
2256  * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
2257  * resources. BLOCKING.
2258  */
2259 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2260 {
2261   struct imap_conn *imapc = &conn->proto.imapc;
2262
2263   /* We cannot send quit unconditionally. If this connection is stale or
2264      bad in any way, sending quit and waiting around here will make the
2265      disconnect wait in vain and cause more problems than we need to. */
2266
2267   /* The IMAP session may or may not have been allocated/setup at this
2268      point! */
2269   if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
2270     if(!imap_perform_logout(conn))
2271       (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
2272
2273   /* Disconnect from the server */
2274   Curl_pp_disconnect(&imapc->pp);
2275
2276   /* Cleanup the SASL module */
2277   Curl_sasl_cleanup(conn, imapc->authused);
2278
2279   /* Cleanup our connection based variables */
2280   Curl_safefree(imapc->mailbox);
2281   Curl_safefree(imapc->mailbox_uidvalidity);
2282
2283   return CURLE_OK;
2284 }
2285
2286 /* Call this when the DO phase has completed */
2287 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2288 {
2289   struct IMAP *imap = conn->data->req.protop;
2290
2291   (void)connected;
2292
2293   if(imap->transfer != FTPTRANSFER_BODY)
2294     /* no data to transfer */
2295     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2296
2297   return CURLE_OK;
2298 }
2299
2300 /* Called from multi.c while DOing */
2301 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2302 {
2303   CURLcode result = imap_multi_statemach(conn, dophase_done);
2304
2305   if(result)
2306     DEBUGF(infof(conn->data, "DO phase failed\n"));
2307   else if(*dophase_done) {
2308     result = imap_dophase_done(conn, FALSE /* not connected */);
2309
2310     DEBUGF(infof(conn->data, "DO phase is complete\n"));
2311   }
2312
2313   return result;
2314 }
2315
2316 /***********************************************************************
2317  *
2318  * imap_regular_transfer()
2319  *
2320  * The input argument is already checked for validity.
2321  *
2322  * Performs all commands done before a regular transfer between a local and a
2323  * remote host.
2324  */
2325 static CURLcode imap_regular_transfer(struct connectdata *conn,
2326                                       bool *dophase_done)
2327 {
2328   CURLcode result = CURLE_OK;
2329   bool connected = FALSE;
2330   struct SessionHandle *data = conn->data;
2331
2332   /* Make sure size is unknown at this point */
2333   data->req.size = -1;
2334
2335   /* Set the progress data */
2336   Curl_pgrsSetUploadCounter(data, 0);
2337   Curl_pgrsSetDownloadCounter(data, 0);
2338   Curl_pgrsSetUploadSize(data, -1);
2339   Curl_pgrsSetDownloadSize(data, -1);
2340
2341   /* Carry out the perform */
2342   result = imap_perform(conn, &connected, dophase_done);
2343
2344   /* Perform post DO phase operations if necessary */
2345   if(!result && *dophase_done)
2346     result = imap_dophase_done(conn, connected);
2347
2348   return result;
2349 }
2350
2351 static CURLcode imap_setup_connection(struct connectdata *conn)
2352 {
2353   struct SessionHandle *data = conn->data;
2354
2355   /* Initialise the IMAP layer */
2356   CURLcode result = imap_init(conn);
2357   if(result)
2358     return result;
2359
2360   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2361     /* Unless we have asked to tunnel IMAP operations through the proxy, we
2362        switch and use HTTP operations only */
2363 #ifndef CURL_DISABLE_HTTP
2364     if(conn->handler == &Curl_handler_imap)
2365       conn->handler = &Curl_handler_imap_proxy;
2366     else {
2367 #ifdef USE_SSL
2368       conn->handler = &Curl_handler_imaps_proxy;
2369 #else
2370       failf(data, "IMAPS not supported!");
2371       return CURLE_UNSUPPORTED_PROTOCOL;
2372 #endif
2373     }
2374
2375     /* set it up as an HTTP connection instead */
2376     return conn->handler->setup_connection(conn);
2377 #else
2378     failf(data, "IMAP over http proxy requires HTTP support built-in!");
2379     return CURLE_UNSUPPORTED_PROTOCOL;
2380 #endif
2381   }
2382
2383   data->state.path++;   /* don't include the initial slash */
2384
2385   return CURLE_OK;
2386 }
2387
2388 /***********************************************************************
2389  *
2390  * imap_sendf()
2391  *
2392  * Sends the formated string as an IMAP command to the server.
2393  *
2394  * Designed to never block.
2395  */
2396 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
2397 {
2398   CURLcode result = CURLE_OK;
2399   struct imap_conn *imapc = &conn->proto.imapc;
2400   char *taggedfmt;
2401   va_list ap;
2402
2403   DEBUGASSERT(fmt);
2404
2405   /* Calculate the next command ID wrapping at 3 digits */
2406   imapc->cmdid = (imapc->cmdid + 1) % 1000;
2407
2408   /* Calculate the tag based on the connection ID and command ID */
2409   snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
2410            'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
2411
2412   /* Prefix the format with the tag */
2413   taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
2414   if(!taggedfmt)
2415     return CURLE_OUT_OF_MEMORY;
2416
2417   /* Send the data with the tag */
2418   va_start(ap, fmt);
2419   result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
2420   va_end(ap);
2421
2422   Curl_safefree(taggedfmt);
2423
2424   return result;
2425 }
2426
2427 /***********************************************************************
2428  *
2429  * imap_atom()
2430  *
2431  * Checks the input string for characters that need escaping and returns an
2432  * atom ready for sending to the server.
2433  *
2434  * The returned string needs to be freed.
2435  *
2436  */
2437 static char *imap_atom(const char *str)
2438 {
2439   const char *p1;
2440   char *p2;
2441   size_t backsp_count = 0;
2442   size_t quote_count = 0;
2443   bool space_exists = FALSE;
2444   size_t newlen = 0;
2445   char *newstr = NULL;
2446
2447   if(!str)
2448     return NULL;
2449
2450   /* Count any unescaped characters */
2451   p1 = str;
2452   while(*p1) {
2453     if(*p1 == '\\')
2454       backsp_count++;
2455     else if(*p1 == '"')
2456       quote_count++;
2457     else if(*p1 == ' ')
2458       space_exists = TRUE;
2459
2460     p1++;
2461   }
2462
2463   /* Does the input contain any unescaped characters? */
2464   if(!backsp_count && !quote_count && !space_exists)
2465     return strdup(str);
2466
2467   /* Calculate the new string length */
2468   newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
2469
2470   /* Allocate the new string */
2471   newstr = (char *) malloc((newlen + 1) * sizeof(char));
2472   if(!newstr)
2473     return NULL;
2474
2475   /* Surround the string in quotes if necessary */
2476   p2 = newstr;
2477   if(space_exists) {
2478     newstr[0] = '"';
2479     newstr[newlen - 1] = '"';
2480     p2++;
2481   }
2482
2483   /* Copy the string, escaping backslash and quote characters along the way */
2484   p1 = str;
2485   while(*p1) {
2486     if(*p1 == '\\' || *p1 == '"') {
2487       *p2 = '\\';
2488       p2++;
2489     }
2490
2491    *p2 = *p1;
2492
2493     p1++;
2494     p2++;
2495   }
2496
2497   /* Terminate the string */
2498   newstr[newlen] = '\0';
2499
2500   return newstr;
2501 }
2502
2503 /***********************************************************************
2504  *
2505  * imap_is_bchar()
2506  *
2507  * Portable test of whether the specified char is a "bchar" as defined in the
2508  * grammar of RFC-5092.
2509  */
2510 static bool imap_is_bchar(char ch)
2511 {
2512   switch(ch) {
2513     /* bchar */
2514     case ':': case '@': case '/':
2515     /* bchar -> achar */
2516     case '&': case '=':
2517     /* bchar -> achar -> uchar -> unreserved */
2518     case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2519     case '7': case '8': case '9':
2520     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2521     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2522     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2523     case 'V': case 'W': case 'X': case 'Y': case 'Z':
2524     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2525     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2526     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2527     case 'v': case 'w': case 'x': case 'y': case 'z':
2528     case '-': case '.': case '_': case '~':
2529     /* bchar -> achar -> uchar -> sub-delims-sh */
2530     case '!': case '$': case '\'': case '(': case ')': case '*':
2531     case '+': case ',':
2532     /* bchar -> achar -> uchar -> pct-encoded */
2533     case '%': /* HEXDIG chars are already included above */
2534       return true;
2535
2536     default:
2537       return false;
2538   }
2539 }
2540
2541 /***********************************************************************
2542  *
2543  * imap_parse_url_options()
2544  *
2545  * Parse the URL login options.
2546  */
2547 static CURLcode imap_parse_url_options(struct connectdata *conn)
2548 {
2549   CURLcode result = CURLE_OK;
2550   struct imap_conn *imapc = &conn->proto.imapc;
2551   const char *options = conn->options;
2552   const char *ptr = options;
2553   bool reset = TRUE;
2554
2555   while(ptr && *ptr) {
2556     const char *key = ptr;
2557
2558     while(*ptr && *ptr != '=')
2559         ptr++;
2560
2561     if(strnequal(key, "AUTH", 4)) {
2562       size_t len = 0;
2563       const char *value = ++ptr;
2564
2565       if(reset) {
2566         reset = FALSE;
2567         imapc->preftype = IMAP_TYPE_NONE;
2568         imapc->prefmech = SASL_AUTH_NONE;
2569       }
2570
2571       while(*ptr && *ptr != ';') {
2572         ptr++;
2573         len++;
2574       }
2575
2576       if(strnequal(value, "*", len)) {
2577         imapc->preftype = IMAP_TYPE_ANY;
2578         imapc->prefmech = SASL_AUTH_ANY;
2579       }
2580       else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
2581         imapc->preftype = IMAP_TYPE_SASL;
2582         imapc->prefmech |= SASL_MECH_LOGIN;
2583       }
2584       else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
2585         imapc->preftype = IMAP_TYPE_SASL;
2586         imapc->prefmech |= SASL_MECH_PLAIN;
2587       }
2588       else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
2589         imapc->preftype = IMAP_TYPE_SASL;
2590         imapc->prefmech |= SASL_MECH_CRAM_MD5;
2591       }
2592       else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
2593         imapc->preftype = IMAP_TYPE_SASL;
2594         imapc->prefmech |= SASL_MECH_DIGEST_MD5;
2595       }
2596       else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
2597         imapc->preftype = IMAP_TYPE_SASL;
2598         imapc->prefmech |= SASL_MECH_GSSAPI;
2599       }
2600       else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
2601         imapc->preftype = IMAP_TYPE_SASL;
2602         imapc->prefmech |= SASL_MECH_NTLM;
2603       }
2604       else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
2605         imapc->preftype = IMAP_TYPE_SASL;
2606         imapc->prefmech |= SASL_MECH_XOAUTH2;
2607       }
2608
2609       if(*ptr == ';')
2610         ptr++;
2611     }
2612     else
2613       result = CURLE_URL_MALFORMAT;
2614   }
2615
2616   return result;
2617 }
2618
2619 /***********************************************************************
2620  *
2621  * imap_parse_url_path()
2622  *
2623  * Parse the URL path into separate path components.
2624  *
2625  */
2626 static CURLcode imap_parse_url_path(struct connectdata *conn)
2627 {
2628   /* The imap struct is already initialised in imap_connect() */
2629   CURLcode result = CURLE_OK;
2630   struct SessionHandle *data = conn->data;
2631   struct IMAP *imap = data->req.protop;
2632   const char *begin = data->state.path;
2633   const char *ptr = begin;
2634
2635   /* See how much of the URL is a valid path and decode it */
2636   while(imap_is_bchar(*ptr))
2637     ptr++;
2638
2639   if(ptr != begin) {
2640     /* Remove the trailing slash if present */
2641     const char *end = ptr;
2642     if(end > begin && end[-1] == '/')
2643       end--;
2644
2645     result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2646                             TRUE);
2647     if(result)
2648       return result;
2649   }
2650   else
2651     imap->mailbox = NULL;
2652
2653   /* There can be any number of parameters in the form ";NAME=VALUE" */
2654   while(*ptr == ';') {
2655     char *name;
2656     char *value;
2657     size_t valuelen;
2658
2659     /* Find the length of the name parameter */
2660     begin = ++ptr;
2661     while(*ptr && *ptr != '=')
2662       ptr++;
2663
2664     if(!*ptr)
2665       return CURLE_URL_MALFORMAT;
2666
2667     /* Decode the name parameter */
2668     result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2669     if(result)
2670       return result;
2671
2672     /* Find the length of the value parameter */
2673     begin = ++ptr;
2674     while(imap_is_bchar(*ptr))
2675       ptr++;
2676
2677     /* Decode the value parameter */
2678     result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2679     if(result) {
2680       Curl_safefree(name);
2681       return result;
2682     }
2683
2684     DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2685
2686     /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2687        PARTIAL) stripping of the trailing slash character if it is present.
2688
2689        Note: Unknown parameters trigger a URL_MALFORMAT error. */
2690     if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2691       if(valuelen > 0 && value[valuelen - 1] == '/')
2692         value[valuelen - 1] = '\0';
2693
2694       imap->uidvalidity = value;
2695       value = NULL;
2696     }
2697     else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2698       if(valuelen > 0 && value[valuelen - 1] == '/')
2699         value[valuelen - 1] = '\0';
2700
2701       imap->uid = value;
2702       value = NULL;
2703     }
2704     else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2705       if(valuelen > 0 && value[valuelen - 1] == '/')
2706         value[valuelen - 1] = '\0';
2707
2708       imap->section = value;
2709       value = NULL;
2710     }
2711     else if(Curl_raw_equal(name, "PARTIAL") && !imap->partial) {
2712       if(valuelen > 0 && value[valuelen - 1] == '/')
2713         value[valuelen - 1] = '\0';
2714
2715       imap->partial = value;
2716       value = NULL;
2717     }
2718     else {
2719       Curl_safefree(name);
2720       Curl_safefree(value);
2721
2722       return CURLE_URL_MALFORMAT;
2723     }
2724
2725     Curl_safefree(name);
2726     Curl_safefree(value);
2727   }
2728
2729   /* Does the URL contain a query parameter? Only valid when we have a mailbox
2730      and no UID as per RFC-5092 */
2731   if(imap->mailbox && !imap->uid && *ptr == '?') {
2732     /* Find the length of the query parameter */
2733     begin = ++ptr;
2734     while(imap_is_bchar(*ptr))
2735       ptr++;
2736
2737     /* Decode the query parameter */
2738     result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2739                             TRUE);
2740     if(result)
2741       return result;
2742   }
2743
2744   /* Any extra stuff at the end of the URL is an error */
2745   if(*ptr)
2746     return CURLE_URL_MALFORMAT;
2747
2748   return CURLE_OK;
2749 }
2750
2751 /***********************************************************************
2752  *
2753  * imap_parse_custom_request()
2754  *
2755  * Parse the custom request.
2756  */
2757 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2758 {
2759   CURLcode result = CURLE_OK;
2760   struct SessionHandle *data = conn->data;
2761   struct IMAP *imap = data->req.protop;
2762   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2763
2764   if(custom) {
2765     /* URL decode the custom request */
2766     result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2767
2768     /* Extract the parameters if specified */
2769     if(!result) {
2770       const char *params = imap->custom;
2771
2772       while(*params && *params != ' ')
2773         params++;
2774
2775       if(*params) {
2776         imap->custom_params = strdup(params);
2777         imap->custom[params - imap->custom] = '\0';
2778
2779         if(!imap->custom_params)
2780           result = CURLE_OUT_OF_MEMORY;
2781       }
2782     }
2783   }
2784
2785   return result;
2786 }
2787
2788 /***********************************************************************
2789  *
2790  * imap_calc_sasl_details()
2791  *
2792  * Calculate the required login details for SASL authentication.
2793  */
2794 static CURLcode imap_calc_sasl_details(struct connectdata *conn,
2795                                        const char **mech,
2796                                        char **initresp, size_t *len,
2797                                        imapstate *state1, imapstate *state2)
2798 {
2799   CURLcode result = CURLE_OK;
2800   struct SessionHandle *data = conn->data;
2801   struct imap_conn *imapc = &conn->proto.imapc;
2802
2803   /* Calculate the supported authentication mechanism, by decreasing order of
2804      security, as well as the initial response where appropriate */
2805 #if defined(USE_KERBEROS5)
2806     if((imapc->authmechs & SASL_MECH_GSSAPI) &&
2807        (imapc->prefmech & SASL_MECH_GSSAPI)) {
2808     imapc->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
2809
2810     *mech = SASL_MECH_STRING_GSSAPI;
2811     *state1 = IMAP_AUTHENTICATE_GSSAPI;
2812     *state2 = IMAP_AUTHENTICATE_GSSAPI_TOKEN;
2813     imapc->authused = SASL_MECH_GSSAPI;
2814
2815     if(imapc->ir_supported || data->set.sasl_ir)
2816       result = Curl_sasl_create_gssapi_user_message(data, conn->user,
2817                                                     conn->passwd, "imap",
2818                                                     imapc->mutual_auth,
2819                                                     NULL, &conn->krb5,
2820                                                     initresp, len);
2821   }
2822   else
2823 #endif
2824 #ifndef CURL_DISABLE_CRYPTO_AUTH
2825   if((imapc->authmechs & SASL_MECH_DIGEST_MD5) &&
2826      (imapc->prefmech & SASL_MECH_DIGEST_MD5)) {
2827     *mech = SASL_MECH_STRING_DIGEST_MD5;
2828     *state1 = IMAP_AUTHENTICATE_DIGESTMD5;
2829     imapc->authused = SASL_MECH_DIGEST_MD5;
2830   }
2831   else if((imapc->authmechs & SASL_MECH_CRAM_MD5) &&
2832           (imapc->prefmech & SASL_MECH_CRAM_MD5)) {
2833     *mech = SASL_MECH_STRING_CRAM_MD5;
2834     *state1 = IMAP_AUTHENTICATE_CRAMMD5;
2835     imapc->authused = SASL_MECH_CRAM_MD5;
2836   }
2837   else
2838 #endif
2839 #ifdef USE_NTLM
2840     if((imapc->authmechs & SASL_MECH_NTLM) &&
2841        (imapc->prefmech & SASL_MECH_NTLM)) {
2842     *mech = SASL_MECH_STRING_NTLM;
2843     *state1 = IMAP_AUTHENTICATE_NTLM;
2844     *state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
2845     imapc->authused = SASL_MECH_NTLM;
2846
2847     if(imapc->ir_supported || data->set.sasl_ir)
2848       result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2849                                                    &conn->ntlm,
2850                                                    initresp, len);
2851   }
2852   else
2853 #endif
2854   if(((imapc->authmechs & SASL_MECH_XOAUTH2) &&
2855       (imapc->prefmech & SASL_MECH_XOAUTH2) &&
2856       (imapc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2857     *mech = SASL_MECH_STRING_XOAUTH2;
2858     *state1 = IMAP_AUTHENTICATE_XOAUTH2;
2859     *state2 = IMAP_AUTHENTICATE_FINAL;
2860     imapc->authused = SASL_MECH_XOAUTH2;
2861
2862     if(imapc->ir_supported || data->set.sasl_ir)
2863       result = Curl_sasl_create_xoauth2_message(data, conn->user,
2864                                                 conn->xoauth2_bearer,
2865                                                 initresp, len);
2866   }
2867   else if((imapc->authmechs & SASL_MECH_LOGIN) &&
2868           (imapc->prefmech & SASL_MECH_LOGIN)) {
2869     *mech = SASL_MECH_STRING_LOGIN;
2870     *state1 = IMAP_AUTHENTICATE_LOGIN;
2871     *state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
2872     imapc->authused = SASL_MECH_LOGIN;
2873
2874     if(imapc->ir_supported || data->set.sasl_ir)
2875       result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2876   }
2877   else if((imapc->authmechs & SASL_MECH_PLAIN) &&
2878           (imapc->prefmech & SASL_MECH_PLAIN)) {
2879     *mech = SASL_MECH_STRING_PLAIN;
2880     *state1 = IMAP_AUTHENTICATE_PLAIN;
2881     *state2 = IMAP_AUTHENTICATE_FINAL;
2882     imapc->authused = SASL_MECH_PLAIN;
2883
2884     if(imapc->ir_supported || data->set.sasl_ir)
2885       result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2886                                               initresp, len);
2887   }
2888
2889   return result;
2890 }
2891
2892 #endif /* CURL_DISABLE_IMAP */