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