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