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