13e9784a4a1bb4b856852661974814be22057fb6
[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 #include "warnless.h"
81
82 #define _MPRINTF_REPLACE /* use our functions only */
83 #include <curl/mprintf.h>
84
85 #include "curl_memory.h"
86 /* The last #include file should be: */
87 #include "memdebug.h"
88
89 /* Local API functions */
90 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
91 static CURLcode imap_do(struct connectdata *conn, bool *done);
92 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
93                           bool premature);
94 static CURLcode imap_connect(struct connectdata *conn, bool *done);
95 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
96 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
97 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
98                         int numsocks);
99 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode imap_setup_connection(struct connectdata *conn);
101 static char *imap_atom(const char *str);
102 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
103 static CURLcode imap_parse_url_options(struct connectdata *conn);
104 static CURLcode imap_parse_url_path(struct connectdata *conn);
105 static CURLcode imap_parse_custom_request(struct connectdata *conn);
106
107 /*
108  * IMAP protocol handler.
109  */
110
111 const struct Curl_handler Curl_handler_imap = {
112   "IMAP",                           /* scheme */
113   imap_setup_connection,            /* setup_connection */
114   imap_do,                          /* do_it */
115   imap_done,                        /* done */
116   ZERO_NULL,                        /* do_more */
117   imap_connect,                     /* connect_it */
118   imap_multi_statemach,             /* connecting */
119   imap_doing,                       /* doing */
120   imap_getsock,                     /* proto_getsock */
121   imap_getsock,                     /* doing_getsock */
122   ZERO_NULL,                        /* domore_getsock */
123   ZERO_NULL,                        /* perform_getsock */
124   imap_disconnect,                  /* disconnect */
125   ZERO_NULL,                        /* readwrite */
126   PORT_IMAP,                        /* defport */
127   CURLPROTO_IMAP,                   /* protocol */
128   PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
129   | PROTOPT_NOURLQUERY              /* flags */
130 };
131
132 #ifdef USE_SSL
133 /*
134  * IMAPS protocol handler.
135  */
136
137 const struct Curl_handler Curl_handler_imaps = {
138   "IMAPS",                          /* scheme */
139   imap_setup_connection,            /* setup_connection */
140   imap_do,                          /* do_it */
141   imap_done,                        /* done */
142   ZERO_NULL,                        /* do_more */
143   imap_connect,                     /* connect_it */
144   imap_multi_statemach,             /* connecting */
145   imap_doing,                       /* doing */
146   imap_getsock,                     /* proto_getsock */
147   imap_getsock,                     /* doing_getsock */
148   ZERO_NULL,                        /* domore_getsock */
149   ZERO_NULL,                        /* perform_getsock */
150   imap_disconnect,                  /* disconnect */
151   ZERO_NULL,                        /* readwrite */
152   PORT_IMAPS,                       /* defport */
153   CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
154   PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
155   | PROTOPT_NOURLQUERY              /* flags */
156 };
157 #endif
158
159 #ifndef CURL_DISABLE_HTTP
160 /*
161  * HTTP-proxyed IMAP protocol handler.
162  */
163
164 static const struct Curl_handler Curl_handler_imap_proxy = {
165   "IMAP",                               /* scheme */
166   ZERO_NULL,                            /* setup_connection */
167   Curl_http,                            /* do_it */
168   Curl_http_done,                       /* done */
169   ZERO_NULL,                            /* do_more */
170   ZERO_NULL,                            /* connect_it */
171   ZERO_NULL,                            /* connecting */
172   ZERO_NULL,                            /* doing */
173   ZERO_NULL,                            /* proto_getsock */
174   ZERO_NULL,                            /* doing_getsock */
175   ZERO_NULL,                            /* domore_getsock */
176   ZERO_NULL,                            /* perform_getsock */
177   ZERO_NULL,                            /* disconnect */
178   ZERO_NULL,                            /* readwrite */
179   PORT_IMAP,                            /* defport */
180   CURLPROTO_HTTP,                       /* protocol */
181   PROTOPT_NONE                          /* flags */
182 };
183
184 #ifdef USE_SSL
185 /*
186  * HTTP-proxyed IMAPS protocol handler.
187  */
188
189 static const struct Curl_handler Curl_handler_imaps_proxy = {
190   "IMAPS",                              /* scheme */
191   ZERO_NULL,                            /* setup_connection */
192   Curl_http,                            /* do_it */
193   Curl_http_done,                       /* done */
194   ZERO_NULL,                            /* do_more */
195   ZERO_NULL,                            /* connect_it */
196   ZERO_NULL,                            /* connecting */
197   ZERO_NULL,                            /* doing */
198   ZERO_NULL,                            /* proto_getsock */
199   ZERO_NULL,                            /* doing_getsock */
200   ZERO_NULL,                            /* domore_getsock */
201   ZERO_NULL,                            /* perform_getsock */
202   ZERO_NULL,                            /* disconnect */
203   ZERO_NULL,                            /* readwrite */
204   PORT_IMAPS,                           /* defport */
205   CURLPROTO_HTTP,                       /* protocol */
206   PROTOPT_NONE                          /* flags */
207 };
208 #endif
209 #endif
210
211 #ifdef USE_SSL
212 static void imap_to_imaps(struct connectdata *conn)
213 {
214   conn->handler = &Curl_handler_imaps;
215 }
216 #else
217 #define imap_to_imaps(x) Curl_nop_stmt
218 #endif
219
220 /***********************************************************************
221  *
222  * imap_matchresp()
223  *
224  * Determines whether the untagged response is related to the specified
225  * command by checking if it is in format "* <command-name> ..." or
226  * "* <number> <command-name> ...".
227  *
228  * The "* " marker is assumed to have already been checked by the caller.
229  */
230 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
231 {
232   const char *end = line + len;
233   size_t cmd_len = strlen(cmd);
234
235   /* Skip the untagged response marker */
236   line += 2;
237
238   /* Do we have a number after the marker? */
239   if(line < end && ISDIGIT(*line)) {
240     /* Skip the number */
241     do
242       line++;
243     while(line < end && ISDIGIT(*line));
244
245     /* Do we have the space character? */
246     if(line == end || *line != ' ')
247       return FALSE;
248
249     line++;
250   }
251
252   /* Does the command name match and is it followed by a space character or at
253      the end of line? */
254   if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
255      (line[cmd_len] == ' ' || line + cmd_len == end))
256     return TRUE;
257
258   return FALSE;
259 }
260
261 /***********************************************************************
262  *
263  * imap_endofresp()
264  *
265  * Checks whether the given string is a valid tagged, untagged or continuation
266  * response which can be processed by the response handler.
267  */
268 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
269                            int *resp)
270 {
271   struct IMAP *imap = conn->data->state.proto.imap;
272   struct imap_conn *imapc = &conn->proto.imapc;
273   const char *id = imapc->resptag;
274   size_t id_len = strlen(id);
275
276   /* Do we have a tagged command response? */
277   if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
278     line += id_len + 1;
279     len -= id_len + 1;
280
281     if(len >= 2 && !memcmp(line, "OK", 2))
282       *resp = 'O';
283     else if(len >= 2 && !memcmp(line, "NO", 2))
284       *resp = 'N';
285     else if(len >= 3 && !memcmp(line, "BAD", 3))
286       *resp = 'B';
287     else {
288       failf(conn->data, "Bad tagged response");
289       *resp = -1;
290     }
291
292     return TRUE;
293   }
294
295   /* Do we have an untagged command response? */
296   if(len >= 2 && !memcmp("* ", line, 2)) {
297     switch(imapc->state) {
298       /* States which are interested in untagged responses */
299       case IMAP_CAPABILITY:
300         if(!imap_matchresp(line, len, "CAPABILITY"))
301           return FALSE;
302         break;
303
304       case IMAP_LIST:
305         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
306           (imap->custom && !imap_matchresp(line, len, imap->custom) &&
307            (strcmp(imap->custom, "STORE") ||
308             !imap_matchresp(line, len, "FETCH")) &&
309            strcmp(imap->custom, "SELECT") &&
310            strcmp(imap->custom, "EXAMINE")))
311           return FALSE;
312         break;
313
314       case IMAP_SELECT:
315         /* SELECT is special in that its untagged responses do not have a
316            common prefix so accept anything! */
317         break;
318
319       case IMAP_FETCH:
320         if(!imap_matchresp(line, len, "FETCH"))
321           return FALSE;
322         break;
323
324       /* Ignore other untagged responses */
325       default:
326         return FALSE;
327     }
328
329     *resp = '*';
330     return TRUE;
331   }
332
333   /* Do we have a continuation response? */
334   if((len == 3 && !memcmp("+", line, 1)) ||
335      (len >= 2 && !memcmp("+ ", line, 2))) {
336     switch(imapc->state) {
337       /* States which are interested in continuation responses */
338       case IMAP_AUTHENTICATE_PLAIN:
339       case IMAP_AUTHENTICATE_LOGIN:
340       case IMAP_AUTHENTICATE_LOGIN_PASSWD:
341       case IMAP_AUTHENTICATE_CRAMMD5:
342       case IMAP_AUTHENTICATE_DIGESTMD5:
343       case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
344       case IMAP_AUTHENTICATE_NTLM:
345       case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
346       case IMAP_AUTHENTICATE_FINAL:
347       case IMAP_APPEND:
348         *resp = '+';
349         break;
350
351       default:
352         failf(conn->data, "Unexpected continuation response");
353         *resp = -1;
354         break;
355     }
356
357     return TRUE;
358   }
359
360   return FALSE; /* Nothing for us */
361 }
362
363 /***********************************************************************
364  *
365  * state()
366  *
367  * This is the ONLY way to change IMAP state!
368  */
369 static void state(struct connectdata *conn, imapstate newstate)
370 {
371   struct imap_conn *imapc = &conn->proto.imapc;
372 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
373   /* for debug purposes */
374   static const char * const names[]={
375     "STOP",
376     "SERVERGREET",
377     "CAPABILITY",
378     "STARTTLS",
379     "UPGRADETLS",
380     "AUTHENTICATE_PLAIN",
381     "AUTHENTICATE_LOGIN",
382     "AUTHENTICATE_LOGIN_PASSWD",
383     "AUTHENTICATE_CRAMMD5",
384     "AUTHENTICATE_DIGESTMD5",
385     "AUTHENTICATE_DIGESTMD5_RESP",
386     "AUTHENTICATE_NTLM",
387     "AUTHENTICATE_NTLM_TYPE2MSG",
388     "AUTHENTICATE_FINAL",
389     "LOGIN",
390     "LIST",
391     "SELECT",
392     "FETCH",
393     "FETCH_FINAL",
394     "APPEND",
395     "APPEND_FINAL",
396     "LOGOUT",
397     /* LAST */
398   };
399
400   if(imapc->state != newstate)
401     infof(conn->data, "IMAP %p state change from %s to %s\n",
402           (void *)imapc, names[imapc->state], names[newstate]);
403 #endif
404
405   imapc->state = newstate;
406 }
407
408 /***********************************************************************
409  *
410  * imap_perform_capability()
411  *
412  * Sends the CAPABILITY command in order to obtain a list of server side
413  * supported capabilities.
414  */
415 static CURLcode imap_perform_capability(struct connectdata *conn)
416 {
417   CURLcode result = CURLE_OK;
418   struct imap_conn *imapc = &conn->proto.imapc;
419
420   imapc->authmechs = 0;         /* No known authentication mechanisms yet */
421   imapc->authused = 0;          /* Clear the authentication mechanism used */
422   imapc->tls_supported = FALSE; /* Clear the TLS capability */
423
424   /* Send the CAPABILITY command */
425   result = imap_sendf(conn, "CAPABILITY");
426
427   if(!result)
428     state(conn, IMAP_CAPABILITY);
429
430   return result;
431 }
432
433 /***********************************************************************
434  *
435  * imap_perform_starttls()
436  *
437  * Sends the STARTTLS command to start the upgrade to TLS.
438  */
439 static CURLcode imap_perform_starttls(struct connectdata *conn)
440 {
441   CURLcode result = CURLE_OK;
442
443   /* Send the STARTTLS command */
444   result = imap_sendf(conn, "STARTTLS");
445
446   if(!result)
447     state(conn, IMAP_STARTTLS);
448
449   return result;
450 }
451
452 /***********************************************************************
453  *
454  * imap_perform_upgrade_tls()
455  *
456  * Performs the upgrade to TLS.
457  */
458 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
459 {
460   CURLcode result = CURLE_OK;
461   struct imap_conn *imapc = &conn->proto.imapc;
462
463   /* Start the SSL connection */
464   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
465
466   if(!result) {
467     if(imapc->state != IMAP_UPGRADETLS)
468       state(conn, IMAP_UPGRADETLS);
469
470     if(imapc->ssldone) {
471       imap_to_imaps(conn);
472       result = imap_perform_capability(conn);
473     }
474   }
475
476   return result;
477 }
478
479 /***********************************************************************
480  *
481  * imap_perform_login()
482  *
483  * Sends a clear text LOGIN command to authenticate with.
484  */
485 static CURLcode imap_perform_login(struct connectdata *conn)
486 {
487   CURLcode result = CURLE_OK;
488   char *user;
489   char *passwd;
490
491   /* Check we have a username and password to authenticate with and end the
492      connect phase if we don't */
493   if(!conn->bits.user_passwd) {
494     state(conn, IMAP_STOP);
495
496     return result;
497   }
498
499   /* Make sure the username and password are in the correct atom format */
500   user = imap_atom(conn->user);
501   passwd = imap_atom(conn->passwd);
502
503   /* Send the LOGIN command */
504   result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
505                       passwd ? passwd : "");
506
507   Curl_safefree(user);
508   Curl_safefree(passwd);
509
510   if(!result)
511     state(conn, IMAP_LOGIN);
512
513   return result;
514 }
515
516 /***********************************************************************
517  *
518  * imap_perform_authenticate()
519  *
520  * Sends an AUTHENTICATE command allowing the client to login with the
521  * appropriate SASL authentication mechanism.
522  *
523  * Additionally, the function will perform fallback to the LOGIN command
524  * should a common mechanism not be available between the client and server.
525  */
526 static CURLcode imap_perform_authenticate(struct connectdata *conn)
527 {
528   CURLcode result = CURLE_OK;
529   struct SessionHandle *data = conn->data;
530   struct imap_conn *imapc = &conn->proto.imapc;
531   const char *mech = NULL;
532   char *initresp = NULL;
533   size_t len = 0;
534   imapstate state1 = IMAP_STOP;
535   imapstate state2 = IMAP_STOP;
536
537   /* Check we have a username and password to authenticate with and end the
538      connect phase if we don't */
539   if(!conn->bits.user_passwd) {
540     state(conn, IMAP_STOP);
541
542     return result;
543   }
544
545   /* Calculate the supported authentication mechanism by decreasing order of
546      security */
547 #ifndef CURL_DISABLE_CRYPTO_AUTH
548   if((imapc->authmechs & SASL_MECH_DIGEST_MD5) &&
549      (imapc->prefmech & SASL_MECH_DIGEST_MD5)) {
550     mech = "DIGEST-MD5";
551     state1 = IMAP_AUTHENTICATE_DIGESTMD5;
552     imapc->authused = SASL_MECH_DIGEST_MD5;
553   }
554   else if((imapc->authmechs & SASL_MECH_CRAM_MD5) &&
555           (imapc->prefmech & SASL_MECH_CRAM_MD5)) {
556     mech = "CRAM-MD5";
557     state1 = IMAP_AUTHENTICATE_CRAMMD5;
558     imapc->authused = SASL_MECH_CRAM_MD5;
559   }
560   else
561 #endif
562 #ifdef USE_NTLM
563     if((imapc->authmechs & SASL_MECH_NTLM) &&
564        (imapc->prefmech & SASL_MECH_NTLM)) {
565     mech = "NTLM";
566     state1 = IMAP_AUTHENTICATE_NTLM;
567     state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
568     imapc->authused = SASL_MECH_NTLM;
569
570     if(imapc->ir_supported || data->set.sasl_ir)
571       result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
572                                                    &conn->ntlm,
573                                                    &initresp, &len);
574   }
575   else
576 #endif
577   if((imapc->authmechs & SASL_MECH_LOGIN) &&
578      (imapc->prefmech & SASL_MECH_LOGIN)) {
579     mech = "LOGIN";
580     state1 = IMAP_AUTHENTICATE_LOGIN;
581     state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
582     imapc->authused = SASL_MECH_LOGIN;
583
584     if(imapc->ir_supported || data->set.sasl_ir)
585       result = Curl_sasl_create_login_message(conn->data, conn->user,
586                                               &initresp, &len);
587   }
588   else if((imapc->authmechs & SASL_MECH_PLAIN) &&
589           (imapc->prefmech & SASL_MECH_PLAIN)) {
590     mech = "PLAIN";
591     state1 = IMAP_AUTHENTICATE_PLAIN;
592     state2 = IMAP_AUTHENTICATE_FINAL;
593     imapc->authused = SASL_MECH_PLAIN;
594
595     if(imapc->ir_supported || data->set.sasl_ir)
596       result = Curl_sasl_create_plain_message(conn->data, conn->user,
597                                               conn->passwd, &initresp, &len);
598   }
599
600   if(!result) {
601     if(mech) {
602       /* Perform SASL based authentication */
603       if(initresp) {
604         result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
605
606         if(!result)
607           state(conn, state2);
608       }
609       else {
610         result = imap_sendf(conn, "AUTHENTICATE %s", mech);
611
612         if(!result)
613           state(conn, state1);
614       }
615
616       Curl_safefree(initresp);
617     }
618     else if(!imapc->login_disabled)
619       /* Perform clear text authentication */
620       result = imap_perform_login(conn);
621     else {
622       /* Other mechanisms not supported */
623       infof(conn->data, "No known authentication mechanisms supported!\n");
624       result = CURLE_LOGIN_DENIED;
625     }
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, "%s", "");
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 if not.
1699  */
1700 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1701 {
1702   CURLcode result = CURLE_OK;
1703   struct imap_conn *imapc = &conn->proto.imapc;
1704   struct pingpong *pp = &imapc->pp;
1705
1706   *done = FALSE; /* default to not done yet */
1707
1708   /* If there already is a protocol-specific struct allocated for this
1709      sessionhandle, deal with it */
1710   Curl_reset_reqproto(conn);
1711
1712   /* Initialise the IMAP layer */
1713   result = imap_init(conn);
1714   if(result)
1715     return result;
1716
1717   /* We always support persistent connections in IMAP */
1718   conn->bits.close = FALSE;
1719
1720   /* Set the default response time-out */
1721   pp->response_time = RESP_TIMEOUT;
1722   pp->statemach_act = imap_statemach_act;
1723   pp->endofresp = imap_endofresp;
1724   pp->conn = conn;
1725
1726   /* Set the default preferred authentication mechanism */
1727   imapc->prefmech = SASL_AUTH_ANY;
1728
1729   /* Initialise the pingpong layer */
1730   Curl_pp_init(pp);
1731
1732   /* Parse the URL options */
1733   result = imap_parse_url_options(conn);
1734   if(result)
1735     return result;
1736
1737   /* Start off waiting for the server greeting response */
1738   state(conn, IMAP_SERVERGREET);
1739
1740   /* Start off with an response id of '*' */
1741   strcpy(imapc->resptag, "*");
1742
1743   result = imap_multi_statemach(conn, done);
1744
1745   return result;
1746 }
1747
1748 /***********************************************************************
1749  *
1750  * imap_done()
1751  *
1752  * The DONE function. This does what needs to be done after a single DO has
1753  * performed.
1754  *
1755  * Input argument is already checked for validity.
1756  */
1757 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1758                           bool premature)
1759 {
1760   CURLcode result = CURLE_OK;
1761   struct SessionHandle *data = conn->data;
1762   struct IMAP *imap = data->state.proto.imap;
1763
1764   (void)premature;
1765
1766   if(!imap)
1767     /* When the easy handle is removed from the multi interface while libcurl
1768        is still trying to resolve the host name, the IMAP struct is not yet
1769        initialized. However, the removal action calls Curl_done() which in
1770        turn calls this function, so we simply return success. */
1771     return CURLE_OK;
1772
1773   if(status) {
1774     conn->bits.close = TRUE; /* marked for closure */
1775     result = status;         /* use the already set error code */
1776   }
1777   else if(!data->set.connect_only && !imap->custom &&
1778           (imap->uid || data->set.upload)) {
1779     /* Handle responses after FETCH or APPEND transfer has finished */
1780     if(!data->set.upload)
1781       state(conn, IMAP_FETCH_FINAL);
1782     else {
1783       /* End the APPEND command first by sending an empty line */
1784       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1785       if(!result)
1786         state(conn, IMAP_APPEND_FINAL);
1787     }
1788
1789     /* Run the state-machine
1790
1791        TODO: when the multi interface is used, this _really_ should be using
1792        the imap_multi_statemach function but we have no general support for
1793        non-blocking DONE operations, not in the multi state machine and with
1794        Curl_done() invokes on several places in the code!
1795     */
1796     if(!result)
1797       result = imap_block_statemach(conn);
1798   }
1799
1800   /* Cleanup our per-request based variables */
1801   Curl_safefree(imap->mailbox);
1802   Curl_safefree(imap->uidvalidity);
1803   Curl_safefree(imap->uid);
1804   Curl_safefree(imap->section);
1805   Curl_safefree(imap->custom);
1806   Curl_safefree(imap->custom_params);
1807
1808   /* Clear the transfer mode for the next request */
1809   imap->transfer = FTPTRANSFER_BODY;
1810
1811   return result;
1812 }
1813
1814 /***********************************************************************
1815  *
1816  * imap_perform()
1817  *
1818  * This is the actual DO function for IMAP. Fetch or append a message, or do
1819  * other things according to the options previously setup.
1820  */
1821 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1822                              bool *dophase_done)
1823 {
1824   /* This is IMAP and no proxy */
1825   CURLcode result = CURLE_OK;
1826   struct SessionHandle *data = conn->data;
1827   struct IMAP *imap = data->state.proto.imap;
1828   struct imap_conn *imapc = &conn->proto.imapc;
1829   bool selected = FALSE;
1830
1831   DEBUGF(infof(conn->data, "DO phase starts\n"));
1832
1833   if(conn->data->set.opt_no_body) {
1834     /* Requested no body means no transfer */
1835     imap->transfer = FTPTRANSFER_INFO;
1836   }
1837
1838   *dophase_done = FALSE; /* not done yet */
1839
1840   /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1841      has already been selected on this connection */
1842   if(imap->mailbox && imapc->mailbox &&
1843      !strcmp(imap->mailbox, imapc->mailbox) &&
1844      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1845       !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1846     selected = TRUE;
1847
1848   /* Start the first command in the DO phase */
1849   if(conn->data->set.upload)
1850     /* APPEND can be executed directly */
1851     result = imap_perform_append(conn);
1852   else if(imap->custom && (selected || !imap->mailbox))
1853     /* Custom command using the same mailbox or no mailbox */
1854     result = imap_perform_list(conn);
1855   else if(!imap->custom && selected && imap->uid)
1856     /* FETCH from the same mailbox */
1857     result = imap_perform_fetch(conn);
1858   else if(imap->mailbox && !selected && (imap->custom || imap->uid))
1859     /* SELECT the mailbox */
1860     result = imap_perform_select(conn);
1861   else
1862     /* LIST */
1863     result = imap_perform_list(conn);
1864
1865   if(result)
1866     return result;
1867
1868   /* Run the state-machine */
1869   result = imap_multi_statemach(conn, dophase_done);
1870
1871   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1872
1873   if(*dophase_done)
1874     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1875
1876   return result;
1877 }
1878
1879 /***********************************************************************
1880  *
1881  * imap_do()
1882  *
1883  * This function is registered as 'curl_do' function. It decodes the path
1884  * parts etc as a wrapper to the actual DO function (imap_perform).
1885  *
1886  * The input argument is already checked for validity.
1887  */
1888 static CURLcode imap_do(struct connectdata *conn, bool *done)
1889 {
1890   CURLcode result = CURLE_OK;
1891
1892   *done = FALSE; /* default to false */
1893
1894   /* Since connections can be re-used between SessionHandles, there might be a
1895      connection already existing but on a fresh SessionHandle struct. As such
1896      we make sure we have a good IMAP struct to play with. For new connections
1897      the IMAP struct is allocated and setup in the imap_connect() function. */
1898   Curl_reset_reqproto(conn);
1899   result = imap_init(conn);
1900   if(result)
1901     return result;
1902
1903   /* Parse the URL path */
1904   result = imap_parse_url_path(conn);
1905   if(result)
1906     return result;
1907
1908   /* Parse the custom request */
1909   result = imap_parse_custom_request(conn);
1910   if(result)
1911     return result;
1912
1913   result = imap_regular_transfer(conn, done);
1914
1915   return result;
1916 }
1917
1918 /***********************************************************************
1919  *
1920  * imap_disconnect()
1921  *
1922  * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1923  * resources. BLOCKING.
1924  */
1925 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1926 {
1927   struct imap_conn *imapc = &conn->proto.imapc;
1928
1929   /* We cannot send quit unconditionally. If this connection is stale or
1930      bad in any way, sending quit and waiting around here will make the
1931      disconnect wait in vain and cause more problems than we need to. */
1932
1933   /* The IMAP session may or may not have been allocated/setup at this
1934      point! */
1935   if(!dead_connection && imapc->pp.conn)
1936     if(!imap_perform_logout(conn))
1937       (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1938
1939   /* Disconnect from the server */
1940   Curl_pp_disconnect(&imapc->pp);
1941
1942   /* Cleanup the SASL module */
1943   Curl_sasl_cleanup(conn, imapc->authused);
1944
1945   /* Cleanup our connection based variables */
1946   Curl_safefree(imapc->mailbox);
1947   Curl_safefree(imapc->mailbox_uidvalidity);
1948
1949   return CURLE_OK;
1950 }
1951
1952 /* Call this when the DO phase has completed */
1953 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1954 {
1955   struct IMAP *imap = conn->data->state.proto.imap;
1956
1957   (void)connected;
1958
1959   if(imap->transfer != FTPTRANSFER_BODY)
1960     /* no data to transfer */
1961     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1962
1963   return CURLE_OK;
1964 }
1965
1966 /* Called from multi.c while DOing */
1967 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1968 {
1969   CURLcode result = imap_multi_statemach(conn, dophase_done);
1970
1971   if(result)
1972     DEBUGF(infof(conn->data, "DO phase failed\n"));
1973   else if(*dophase_done) {
1974     result = imap_dophase_done(conn, FALSE /* not connected */);
1975
1976     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1977   }
1978
1979   return result;
1980 }
1981
1982 /***********************************************************************
1983  *
1984  * imap_regular_transfer()
1985  *
1986  * The input argument is already checked for validity.
1987  *
1988  * Performs all commands done before a regular transfer between a local and a
1989  * remote host.
1990  */
1991 static CURLcode imap_regular_transfer(struct connectdata *conn,
1992                                       bool *dophase_done)
1993 {
1994   CURLcode result = CURLE_OK;
1995   bool connected = FALSE;
1996   struct SessionHandle *data = conn->data;
1997
1998   /* Make sure size is unknown at this point */
1999   data->req.size = -1;
2000
2001   /* Set the progress data */
2002   Curl_pgrsSetUploadCounter(data, 0);
2003   Curl_pgrsSetDownloadCounter(data, 0);
2004   Curl_pgrsSetUploadSize(data, 0);
2005   Curl_pgrsSetDownloadSize(data, 0);
2006
2007   /* Carry out the perform */
2008   result = imap_perform(conn, &connected, dophase_done);
2009
2010   /* Perform post DO phase operations if necessary */
2011   if(!result && *dophase_done)
2012     result = imap_dophase_done(conn, connected);
2013
2014   return result;
2015 }
2016
2017 static CURLcode imap_setup_connection(struct connectdata *conn)
2018 {
2019   struct SessionHandle *data = conn->data;
2020
2021   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2022     /* Unless we have asked to tunnel IMAP operations through the proxy, we
2023        switch and use HTTP operations only */
2024 #ifndef CURL_DISABLE_HTTP
2025     if(conn->handler == &Curl_handler_imap)
2026       conn->handler = &Curl_handler_imap_proxy;
2027     else {
2028 #ifdef USE_SSL
2029       conn->handler = &Curl_handler_imaps_proxy;
2030 #else
2031       failf(data, "IMAPS not supported!");
2032       return CURLE_UNSUPPORTED_PROTOCOL;
2033 #endif
2034     }
2035
2036     /* We explicitly mark this connection as persistent here as we're doing
2037        IMAP over HTTP and thus we accidentally avoid setting this value
2038        otherwise */
2039     conn->bits.close = FALSE;
2040 #else
2041     failf(data, "IMAP over http proxy requires HTTP support built-in!");
2042     return CURLE_UNSUPPORTED_PROTOCOL;
2043 #endif
2044   }
2045
2046   data->state.path++;   /* don't include the initial slash */
2047
2048   return CURLE_OK;
2049 }
2050
2051 /***********************************************************************
2052  *
2053  * imap_sendf()
2054  *
2055  * Sends the formated string as an IMAP command to the server.
2056  *
2057  * Designed to never block.
2058  */
2059 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
2060 {
2061   CURLcode result = CURLE_OK;
2062   struct imap_conn *imapc = &conn->proto.imapc;
2063   char *taggedfmt;
2064   va_list ap;
2065
2066   DEBUGASSERT(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' + curlx_sltosi(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   va_start(ap, fmt);
2082   result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
2083   va_end(ap);
2084
2085   Curl_safefree(taggedfmt);
2086
2087   return result;
2088 }
2089
2090 /***********************************************************************
2091  *
2092  * imap_atom()
2093  *
2094  * Checks the input string for characters that need escaping and returns an
2095  * atom ready for sending to the server.
2096  *
2097  * The returned string needs to be freed.
2098  *
2099  */
2100 static char *imap_atom(const char *str)
2101 {
2102   const char *p1;
2103   char *p2;
2104   size_t backsp_count = 0;
2105   size_t quote_count = 0;
2106   bool space_exists = FALSE;
2107   size_t newlen = 0;
2108   char *newstr = NULL;
2109
2110   if(!str)
2111     return NULL;
2112
2113   /* Count any unescapped characters */
2114   p1 = str;
2115   while(*p1) {
2116     if(*p1 == '\\')
2117       backsp_count++;
2118     else if(*p1 == '"')
2119       quote_count++;
2120     else if(*p1 == ' ')
2121       space_exists = TRUE;
2122
2123     p1++;
2124   }
2125
2126   /* Does the input contain any unescapped characters? */
2127   if(!backsp_count && !quote_count && !space_exists)
2128     return strdup(str);
2129
2130   /* Calculate the new string length */
2131   newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
2132
2133   /* Allocate the new string */
2134   newstr = (char *) malloc((newlen + 1) * sizeof(char));
2135   if(!newstr)
2136     return NULL;
2137
2138   /* Surround the string in quotes if necessary */
2139   p2 = newstr;
2140   if(space_exists) {
2141     newstr[0] = '"';
2142     newstr[newlen - 1] = '"';
2143     p2++;
2144   }
2145
2146   /* Copy the string, escaping backslash and quote characters along the way */
2147   p1 = str;
2148   while(*p1) {
2149     if(*p1 == '\\' || *p1 == '"') {
2150       *p2 = '\\';
2151       p2++;
2152     }
2153
2154    *p2 = *p1;
2155
2156     p1++;
2157     p2++;
2158   }
2159
2160   /* Terminate the string */
2161   newstr[newlen] = '\0';
2162
2163   return newstr;
2164 }
2165
2166 /***********************************************************************
2167  *
2168  * imap_is_bchar()
2169  *
2170  * Portable test of whether the specified char is a "bchar" as defined in the
2171  * grammar of RFC-5092.
2172  */
2173 static bool imap_is_bchar(char ch)
2174 {
2175   switch(ch) {
2176     /* bchar */
2177     case ':': case '@': case '/':
2178     /* bchar -> achar */
2179     case '&': case '=':
2180     /* bchar -> achar -> uchar -> unreserved */
2181     case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2182     case '7': case '8': case '9':
2183     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2184     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2185     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2186     case 'V': case 'W': case 'X': case 'Y': case 'Z':
2187     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2188     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2189     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2190     case 'v': case 'w': case 'x': case 'y': case 'z':
2191     case '-': case '.': case '_': case '~':
2192     /* bchar -> achar -> uchar -> sub-delims-sh */
2193     case '!': case '$': case '\'': case '(': case ')': case '*':
2194     case '+': case ',':
2195     /* bchar -> achar -> uchar -> pct-encoded */
2196     case '%': /* HEXDIG chars are already included above */
2197       return true;
2198
2199     default:
2200       return false;
2201   }
2202 }
2203
2204 /***********************************************************************
2205  *
2206  * imap_parse_url_options()
2207  *
2208  * Parse the URL login options.
2209  */
2210 static CURLcode imap_parse_url_options(struct connectdata *conn)
2211 {
2212   CURLcode result = CURLE_OK;
2213   struct imap_conn *imapc = &conn->proto.imapc;
2214   const char *options = conn->options;
2215   const char *ptr = options;
2216
2217   if(options) {
2218     const char *key = ptr;
2219
2220     while(*ptr && *ptr != '=')
2221         ptr++;
2222
2223     if(strnequal(key, "AUTH", 4)) {
2224       const char *value = ptr + 1;
2225
2226       if(strequal(value, "*"))
2227         imapc->prefmech = SASL_AUTH_ANY;
2228       else if(strequal(value, "LOGIN"))
2229         imapc->prefmech = SASL_MECH_LOGIN;
2230       else if(strequal(value, "PLAIN"))
2231         imapc->prefmech = SASL_MECH_PLAIN;
2232       else if(strequal(value, "CRAM-MD5"))
2233         imapc->prefmech = SASL_MECH_CRAM_MD5;
2234       else if(strequal(value, "DIGEST-MD5"))
2235         imapc->prefmech = SASL_MECH_DIGEST_MD5;
2236       else if(strequal(value, "GSSAPI"))
2237         imapc->prefmech = SASL_MECH_GSSAPI;
2238       else if(strequal(value, "NTLM"))
2239         imapc->prefmech = SASL_MECH_NTLM;
2240       else
2241         imapc->prefmech = SASL_AUTH_NONE;
2242     }
2243     else
2244       result = CURLE_URL_MALFORMAT;
2245   }
2246
2247   return result;
2248 }
2249
2250 /***********************************************************************
2251  *
2252  * imap_parse_url_path()
2253  *
2254  * Parse the URL path into separate path components.
2255  *
2256  */
2257 static CURLcode imap_parse_url_path(struct connectdata *conn)
2258 {
2259   /* The imap struct is already initialised in imap_connect() */
2260   CURLcode result = CURLE_OK;
2261   struct SessionHandle *data = conn->data;
2262   struct IMAP *imap = data->state.proto.imap;
2263   const char *begin = data->state.path;
2264   const char *ptr = begin;
2265
2266   /* See how much of the URL is a valid path and decode it */
2267   while(imap_is_bchar(*ptr))
2268     ptr++;
2269
2270   if(ptr != begin) {
2271     /* Remove the trailing slash if present */
2272     const char *end = ptr;
2273     if(end > begin && end[-1] == '/')
2274       end--;
2275
2276     result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2277                             TRUE);
2278     if(result)
2279       return result;
2280   }
2281   else
2282     imap->mailbox = NULL;
2283
2284   /* There can be any number of parameters in the form ";NAME=VALUE" */
2285   while(*ptr == ';') {
2286     char *name;
2287     char *value;
2288     size_t valuelen;
2289
2290     /* Find the length of the name parameter */
2291     begin = ++ptr;
2292     while(*ptr && *ptr != '=')
2293       ptr++;
2294
2295     if(!*ptr)
2296       return CURLE_URL_MALFORMAT;
2297
2298     /* Decode the name parameter */
2299     result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2300     if(result)
2301       return result;
2302
2303     /* Find the length of the value parameter */
2304     begin = ++ptr;
2305     while(imap_is_bchar(*ptr))
2306       ptr++;
2307
2308     /* Decode the value parameter */
2309     result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2310     if(result) {
2311       Curl_safefree(name);
2312       return result;
2313     }
2314
2315     DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2316
2317     /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2318        stripping of the trailing slash character if it is present.
2319
2320        Note: Unknown parameters trigger a URL_MALFORMAT error. */
2321     if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2322       if(valuelen > 0 && value[valuelen - 1] == '/')
2323         value[valuelen - 1] = '\0';
2324
2325       imap->uidvalidity = value;
2326       value = NULL;
2327     }
2328     else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2329       if(valuelen > 0 && value[valuelen - 1] == '/')
2330         value[valuelen - 1] = '\0';
2331
2332       imap->uid = value;
2333       value = NULL;
2334     }
2335     else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2336       if(valuelen > 0 && value[valuelen - 1] == '/')
2337         value[valuelen - 1] = '\0';
2338
2339       imap->section = value;
2340       value = NULL;
2341     }
2342     else {
2343       Curl_safefree(name);
2344       Curl_safefree(value);
2345
2346       return CURLE_URL_MALFORMAT;
2347     }
2348
2349     Curl_safefree(name);
2350     Curl_safefree(value);
2351   }
2352
2353   /* Any extra stuff at the end of the URL is an error */
2354   if(*ptr)
2355     return CURLE_URL_MALFORMAT;
2356
2357   return CURLE_OK;
2358 }
2359
2360 /***********************************************************************
2361  *
2362  * imap_parse_custom_request()
2363  *
2364  * Parse the custom request.
2365  */
2366 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2367 {
2368   CURLcode result = CURLE_OK;
2369   struct SessionHandle *data = conn->data;
2370   struct IMAP *imap = data->state.proto.imap;
2371   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2372
2373   if(custom) {
2374     /* URL decode the custom request */
2375     result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2376
2377     /* Extract the parameters if specified */
2378     if(!result) {
2379       const char *params = imap->custom;
2380
2381       while(*params && *params != ' ')
2382         params++;
2383
2384       if(*params) {
2385         imap->custom_params = strdup(params);
2386         imap->custom[params - imap->custom] = '\0';
2387
2388         if(!imap->custom_params)
2389           result = CURLE_OUT_OF_MEMORY;
2390       }
2391     }
2392   }
2393
2394   return result;
2395 }
2396
2397 #endif /* CURL_DISABLE_IMAP */