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