imap: Added list perform and response handler 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   struct imap_conn *imapc = &conn->proto.imapc;
695   char *mailbox;
696
697   /* Make sure the mailbox is in the correct atom format */
698   mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
699   if(!mailbox)
700     return CURLE_OUT_OF_MEMORY;
701
702   /* Send the LIST command */
703   result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
704
705   Curl_safefree(mailbox);
706
707   if(!result)
708     state(conn, IMAP_LIST);
709
710   return result;
711 }
712
713 static CURLcode imap_select(struct connectdata *conn)
714 {
715   CURLcode result = CURLE_OK;
716   struct SessionHandle *data = conn->data;
717   struct IMAP *imap = data->state.proto.imap;
718   struct imap_conn *imapc = &conn->proto.imapc;
719   char *mailbox;
720
721   /* Invalidate old information as we are switching mailboxes */
722   Curl_safefree(imapc->mailbox);
723   Curl_safefree(imapc->mailbox_uidvalidity);
724
725   /* Check we have a mailbox */
726   if(!imap->mailbox) {
727     failf(conn->data, "Cannot SELECT without a mailbox.");
728     return CURLE_URL_MALFORMAT;
729   }
730
731   /* Make sure the mailbox is in the correct atom format */
732   mailbox = imap_atom(imap->mailbox);
733   if(!mailbox)
734     return CURLE_OUT_OF_MEMORY;
735
736   /* Send the SELECT command */
737   result = imap_sendf(conn, "SELECT %s", mailbox);
738
739   Curl_safefree(mailbox);
740
741   if(!result)
742     state(conn, IMAP_SELECT);
743
744   return result;
745 }
746
747 static CURLcode imap_fetch(struct connectdata *conn)
748 {
749   CURLcode result = CURLE_OK;
750   struct IMAP *imap = conn->data->state.proto.imap;
751
752   /* Send the FETCH command */
753   result = imap_sendf(conn, "FETCH %s BODY[%s]",
754                       imap->uid ? imap->uid : "1",
755                       imap->section ? imap->section : "");
756
757   if(!result)
758     state(conn, IMAP_FETCH);
759
760   return result;
761 }
762
763 static CURLcode imap_append(struct connectdata *conn)
764 {
765   CURLcode result;
766   struct IMAP *imap = conn->data->state.proto.imap;
767   char *mailbox;
768
769   /* Check we have a mailbox */
770   if(!imap->mailbox) {
771     failf(conn->data, "Cannot APPEND without a mailbox.");
772     return CURLE_URL_MALFORMAT;
773   }
774
775   /* Check we know the size of the upload */
776   if(conn->data->set.infilesize < 0) {
777     failf(conn->data, "Cannot APPEND with unknown input file size\n");
778     return CURLE_UPLOAD_FAILED;
779   }
780
781   /* Make sure the mailbox is in the correct atom format */
782   mailbox = imap_atom(imap->mailbox);
783   if(!mailbox)
784     return CURLE_OUT_OF_MEMORY;
785
786   /* Send the APPEND command */
787   result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
788                       mailbox, conn->data->set.infilesize);
789
790   Curl_safefree(mailbox);
791
792   if(!result)
793     state(conn, IMAP_APPEND);
794
795   return result;
796 }
797
798 static CURLcode imap_custom(struct connectdata *conn)
799 {
800   struct IMAP *imap = conn->data->state.proto.imap;
801
802   /* Send the custom request */
803   CURLcode result = imap_sendf(conn, "%s%s", imap->custom,
804                                imap->custom_params ? imap->custom_params : "");
805
806   if(!result) {
807     /* We don't know how much data will be received */
808     Curl_pgrsSetDownloadSize(conn->data, -1);
809
810     state(conn, IMAP_CUSTOM);
811   }
812
813   return result;
814 }
815
816 /* For the initial server greeting */
817 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
818                                             int imapcode,
819                                             imapstate instate)
820 {
821   CURLcode result = CURLE_OK;
822   struct SessionHandle *data = conn->data;
823
824   (void)instate; /* no use for this yet */
825
826   if(imapcode != 'O') {
827     failf(data, "Got unexpected imap-server response");
828     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
829   }
830   else
831     result = imap_state_capability(conn);
832
833   return result;
834 }
835
836 /* For CAPABILITY responses */
837 static CURLcode imap_state_capability_resp(struct connectdata *conn,
838                                            int imapcode,
839                                            imapstate instate)
840 {
841   CURLcode result = CURLE_OK;
842   struct SessionHandle *data = conn->data;
843   struct imap_conn *imapc = &conn->proto.imapc;
844   const char *line = data->state.buffer;
845   size_t wordlen;
846
847   (void)instate; /* no use for this yet */
848
849   /* Do we have a untagged response? */
850   if(imapcode == '*') {
851     line += 2;
852
853     /* Loop through the data line */
854     for(;;) {
855       while(*line &&
856             (*line == ' ' || *line == '\t' ||
857               *line == '\r' || *line == '\n')) {
858
859         line++;
860       }
861
862       if(!*line)
863         break;
864
865       /* Extract the word */
866       for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
867             line[wordlen] != '\t' && line[wordlen] != '\r' &&
868             line[wordlen] != '\n';)
869         wordlen++;
870
871       /* Does the server support the STARTTLS capability? */
872       if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
873         imapc->tls_supported = TRUE;
874
875       /* Has the server explicitly disabled clear text authentication? */
876       else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
877         imapc->login_disabled = TRUE;
878
879       /* Does the server support the SASL-IR capability? */
880       else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
881         imapc->ir_supported = TRUE;
882
883       /* Do we have a SASL based authentication mechanism? */
884       else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
885         line += 5;
886         wordlen -= 5;
887
888         /* Test the word for a matching authentication mechanism */
889         if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
890           imapc->authmechs |= SASL_MECH_LOGIN;
891         if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
892           imapc->authmechs |= SASL_MECH_PLAIN;
893         else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
894           imapc->authmechs |= SASL_MECH_CRAM_MD5;
895         else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
896           imapc->authmechs |= SASL_MECH_DIGEST_MD5;
897         else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
898           imapc->authmechs |= SASL_MECH_GSSAPI;
899         else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
900           imapc->authmechs |= SASL_MECH_EXTERNAL;
901         else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
902           imapc->authmechs |= SASL_MECH_NTLM;
903       }
904
905       line += wordlen;
906     }
907   }
908   else if(imapcode == 'O') {
909     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
910       /* We don't have a SSL/TLS connection yet, but SSL is requested */
911       if(imapc->tls_supported)
912         /* Switch to TLS connection now */
913         result = imap_state_starttls(conn);
914       else if(data->set.use_ssl == CURLUSESSL_TRY)
915         /* Fallback and carry on with authentication */
916         result = imap_authenticate(conn);
917       else {
918         failf(data, "STARTTLS not supported.");
919         result = CURLE_USE_SSL_FAILED;
920       }
921     }
922     else
923       result = imap_authenticate(conn);
924   }
925   else
926     result = imap_state_login(conn);
927
928   return result;
929 }
930
931 /* For STARTTLS responses */
932 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
933                                          int imapcode,
934                                          imapstate instate)
935 {
936   CURLcode result = CURLE_OK;
937   struct SessionHandle *data = conn->data;
938
939   (void)instate; /* no use for this yet */
940
941   if(imapcode != 'O') {
942     if(data->set.use_ssl != CURLUSESSL_TRY) {
943       failf(data, "STARTTLS denied. %c", imapcode);
944       result = CURLE_USE_SSL_FAILED;
945     }
946     else
947       result = imap_authenticate(conn);
948   }
949   else
950     result = imap_state_upgrade_tls(conn);
951
952   return result;
953 }
954
955 /* For AUTHENTICATE PLAIN (without initial response) responses */
956 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
957                                            int imapcode,
958                                            imapstate instate)
959 {
960   CURLcode result = CURLE_OK;
961   struct SessionHandle *data = conn->data;
962   size_t len = 0;
963   char *plainauth = NULL;
964
965   (void)instate; /* no use for this yet */
966
967   if(imapcode != '+') {
968     failf(data, "Access denied. %c", imapcode);
969     result = CURLE_LOGIN_DENIED;
970   }
971   else {
972     /* Create the authorisation message */
973     result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
974                                             &plainauth, &len);
975
976     /* Send the message */
977     if(!result) {
978       if(plainauth) {
979         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
980
981         if(!result)
982           state(conn, IMAP_AUTHENTICATE_FINAL);
983       }
984
985       Curl_safefree(plainauth);
986     }
987   }
988
989   return result;
990 }
991
992 /* For AUTHENTICATE LOGIN (without initial response) responses */
993 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
994                                            int imapcode,
995                                            imapstate instate)
996 {
997   CURLcode result = CURLE_OK;
998   struct SessionHandle *data = conn->data;
999   size_t len = 0;
1000   char *authuser = NULL;
1001
1002   (void)instate; /* no use for this yet */
1003
1004   if(imapcode != '+') {
1005     failf(data, "Access denied: %d", imapcode);
1006     result = CURLE_LOGIN_DENIED;
1007   }
1008   else {
1009     /* Create the user message */
1010     result = Curl_sasl_create_login_message(data, conn->user,
1011                                             &authuser, &len);
1012
1013     /* Send the user */
1014     if(!result) {
1015       if(authuser) {
1016         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1017
1018         if(!result)
1019           state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1020       }
1021
1022       Curl_safefree(authuser);
1023     }
1024   }
1025
1026   return result;
1027 }
1028
1029 /* For AUTHENTICATE LOGIN user entry responses */
1030 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1031                                                     int imapcode,
1032                                                     imapstate instate)
1033 {
1034   CURLcode result = CURLE_OK;
1035   struct SessionHandle *data = conn->data;
1036   size_t len = 0;
1037   char *authpasswd = NULL;
1038
1039   (void)instate; /* no use for this yet */
1040
1041   if(imapcode != '+') {
1042     failf(data, "Access denied: %d", imapcode);
1043     result = CURLE_LOGIN_DENIED;
1044   }
1045   else {
1046     /* Create the password message */
1047     result = Curl_sasl_create_login_message(data, conn->passwd,
1048                                             &authpasswd, &len);
1049
1050     /* Send the password */
1051     if(!result) {
1052       if(authpasswd) {
1053         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1054
1055         if(!result)
1056           state(conn, IMAP_AUTHENTICATE_FINAL);
1057       }
1058
1059       Curl_safefree(authpasswd);
1060     }
1061   }
1062
1063   return result;
1064 }
1065
1066 #ifndef CURL_DISABLE_CRYPTO_AUTH
1067 /* For AUTHENTICATE CRAM-MD5 responses */
1068 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1069                                           int imapcode,
1070                                           imapstate instate)
1071 {
1072   CURLcode result = CURLE_OK;
1073   struct SessionHandle *data = conn->data;
1074   char *chlg64 = data->state.buffer;
1075   size_t len = 0;
1076   char *rplyb64 = NULL;
1077
1078   (void)instate; /* no use for this yet */
1079
1080   if(imapcode != '+') {
1081     failf(data, "Access denied: %d", imapcode);
1082     return CURLE_LOGIN_DENIED;
1083   }
1084
1085   /* Get the challenge */
1086   for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1087     ;
1088
1089   /* Terminate the challenge */
1090   if(*chlg64 != '=') {
1091     for(len = strlen(chlg64); len--;)
1092       if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
1093          chlg64[len] != '\t')
1094         break;
1095
1096     if(++len) {
1097       chlg64[len] = '\0';
1098     }
1099   }
1100
1101   /* Create the response message */
1102   result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
1103                                              conn->passwd, &rplyb64, &len);
1104
1105   /* Send the response */
1106   if(!result) {
1107     if(rplyb64) {
1108       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1109
1110       if(!result)
1111         state(conn, IMAP_AUTHENTICATE_FINAL);
1112     }
1113
1114     Curl_safefree(rplyb64);
1115   }
1116
1117   return result;
1118 }
1119
1120 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1121 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1122                                             int imapcode,
1123                                             imapstate instate)
1124 {
1125   CURLcode result = CURLE_OK;
1126   struct SessionHandle *data = conn->data;
1127   char *chlg64 = data->state.buffer;
1128   size_t len = 0;
1129   char *rplyb64 = NULL;
1130
1131   (void)instate; /* no use for this yet */
1132
1133   if(imapcode != '+') {
1134     failf(data, "Access denied: %d", imapcode);
1135     return CURLE_LOGIN_DENIED;
1136   }
1137
1138   /* Get the challenge */
1139   for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
1140     ;
1141
1142   /* Create the response message */
1143   result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
1144                                                conn->passwd, "imap",
1145                                                &rplyb64, &len);
1146
1147   /* Send the response */
1148   if(!result) {
1149     if(rplyb64) {
1150       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1151
1152       if(!result)
1153         state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1154     }
1155
1156     Curl_safefree(rplyb64);
1157   }
1158
1159   return result;
1160 }
1161
1162 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1163 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1164                                                  int imapcode,
1165                                                  imapstate instate)
1166 {
1167   CURLcode result = CURLE_OK;
1168   struct SessionHandle *data = conn->data;
1169
1170   (void)instate; /* no use for this yet */
1171
1172   if(imapcode != '+') {
1173     failf(data, "Authentication failed: %d", imapcode);
1174     result = CURLE_LOGIN_DENIED;
1175   }
1176   else {
1177     /* Send an empty response */
1178     result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1179
1180     if(!result)
1181       state(conn, IMAP_AUTHENTICATE_FINAL);
1182   }
1183
1184   return result;
1185 }
1186 #endif
1187
1188 #ifdef USE_NTLM
1189 /* For AUTHENTICATE NTLM (without initial response) responses */
1190 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1191                                           int imapcode,
1192                                           imapstate instate)
1193 {
1194   CURLcode result = CURLE_OK;
1195   struct SessionHandle *data = conn->data;
1196   size_t len = 0;
1197   char *type1msg = NULL;
1198
1199   (void)instate; /* no use for this yet */
1200
1201   if(imapcode != '+') {
1202     failf(data, "Access denied: %d", imapcode);
1203     result = CURLE_LOGIN_DENIED;
1204   }
1205   else {
1206     /* Create the type-1 message */
1207     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1208                                                  &conn->ntlm,
1209                                                  &type1msg, &len);
1210
1211     /* Send the message */
1212     if(!result) {
1213       if(type1msg) {
1214         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1215
1216         if(!result)
1217           state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1218       }
1219
1220       Curl_safefree(type1msg);
1221     }
1222   }
1223
1224   return result;
1225 }
1226
1227 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1228 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1229                                                    int imapcode,
1230                                                    imapstate instate)
1231 {
1232   CURLcode result = CURLE_OK;
1233   struct SessionHandle *data = conn->data;
1234   size_t len = 0;
1235   char *type3msg = NULL;
1236
1237   (void)instate; /* no use for this yet */
1238
1239   if(imapcode != '+') {
1240     failf(data, "Access denied: %d", imapcode);
1241     result = CURLE_LOGIN_DENIED;
1242   }
1243   else {
1244     /* Create the type-3 message */
1245     result = Curl_sasl_create_ntlm_type3_message(data,
1246                                                  data->state.buffer + 2,
1247                                                  conn->user, conn->passwd,
1248                                                  &conn->ntlm,
1249                                                  &type3msg, &len);
1250
1251     /* Send the message */
1252     if(!result) {
1253       if(type3msg) {
1254         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1255
1256         if(!result)
1257           state(conn, IMAP_AUTHENTICATE_FINAL);
1258       }
1259
1260       Curl_safefree(type3msg);
1261     }
1262   }
1263
1264   return result;
1265 }
1266 #endif
1267
1268 /* For final responses to the AUTHENTICATE sequence */
1269 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1270                                            int imapcode,
1271                                            imapstate instate)
1272 {
1273   CURLcode result = CURLE_OK;
1274   struct SessionHandle *data = conn->data;
1275
1276   (void)instate; /* no use for this yet */
1277
1278   if(imapcode != 'O') {
1279     failf(data, "Authentication failed: %d", imapcode);
1280     result = CURLE_LOGIN_DENIED;
1281   }
1282   else
1283     /* End of connect phase */
1284     state(conn, IMAP_STOP);
1285
1286   return result;
1287 }
1288
1289 /* For LOGIN responses */
1290 static CURLcode imap_state_login_resp(struct connectdata *conn,
1291                                       int imapcode,
1292                                       imapstate instate)
1293 {
1294   CURLcode result = CURLE_OK;
1295   struct SessionHandle *data = conn->data;
1296
1297   (void)instate; /* no use for this yet */
1298
1299   if(imapcode != 'O') {
1300     failf(data, "Access denied. %c", imapcode);
1301     result = CURLE_LOGIN_DENIED;
1302   }
1303   else
1304     /* End of connect phase */
1305     state(conn, IMAP_STOP);
1306
1307   return result;
1308 }
1309
1310 /* For LIST responses */
1311 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1312                                      imapstate instate)
1313 {
1314   CURLcode result = CURLE_OK;
1315   char *line = conn->data->state.buffer;
1316   size_t len = strlen(line);
1317
1318   (void)instate; /* No use for this yet */
1319
1320   if(imapcode == '*') {
1321     /* The client which asked for this custom command should know best
1322        how to cope with the result, just send it as body.
1323        Add back the LF character temporarily while saving. */
1324     line[len] = '\n';
1325     result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1326     line[len] = '\0';
1327   }
1328   else {
1329     /* Final response. Stop and return the final status. */
1330     if(imapcode != 'O')
1331       result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1332     else
1333       result = CURLE_OK;
1334
1335     /* End of DO phase */
1336     state(conn, IMAP_STOP);
1337   }
1338
1339   return result;
1340 }
1341
1342 /* For SELECT responses */
1343 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1344                                        imapstate instate)
1345 {
1346   CURLcode result = CURLE_OK;
1347   struct SessionHandle *data = conn->data;
1348   struct IMAP *imap = conn->data->state.proto.imap;
1349   struct imap_conn *imapc = &conn->proto.imapc;
1350   const char *line = data->state.buffer;
1351   char tmp[20];
1352
1353   (void)instate; /* no use for this yet */
1354
1355   if(imapcode == '*') {
1356     /* See if this is an UIDVALIDITY response */
1357     if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1358       Curl_safefree(imapc->mailbox_uidvalidity);
1359       imapc->mailbox_uidvalidity = strdup(tmp);
1360     }
1361   }
1362   else if(imapcode == 'O') {
1363     /* Check if the UIDVALIDITY has been specified and matches */
1364     if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1365        strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1366       failf(conn->data, "Mailbox UIDVALIDITY has changed");
1367       result = CURLE_REMOTE_FILE_NOT_FOUND;
1368     }
1369     else {
1370       /* Note the currently opened mailbox on this connection */
1371       imapc->mailbox = strdup(imap->mailbox);
1372
1373       if(imap->custom)
1374         result = imap_custom(conn);
1375       else
1376         result = imap_fetch(conn);
1377     }
1378   }
1379   else {
1380     failf(data, "Select failed");
1381     result = CURLE_LOGIN_DENIED;
1382   }
1383
1384   return result;
1385 }
1386
1387 /* For the (first line of the) FETCH responses */
1388 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1389                                       imapstate instate)
1390 {
1391   CURLcode result = CURLE_OK;
1392   struct SessionHandle *data = conn->data;
1393   struct imap_conn *imapc = &conn->proto.imapc;
1394   struct pingpong *pp = &imapc->pp;
1395   const char *ptr = data->state.buffer;
1396   bool parsed = FALSE;
1397   curl_off_t size;
1398
1399   (void)instate; /* no use for this yet */
1400
1401   if(imapcode != '*') {
1402     Curl_pgrsSetDownloadSize(data, 0);
1403     state(conn, IMAP_STOP);
1404     return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1405   }
1406
1407   /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1408      the continuation data contained within the curly brackets */
1409   while(*ptr && (*ptr != '{'))
1410     ptr++;
1411
1412   if(*ptr == '{') {
1413     char *endptr;
1414     size = curlx_strtoofft(ptr + 1, &endptr, 10);
1415     if(endptr - ptr > 1 && endptr[0] == '}' &&
1416        endptr[1] == '\r' && endptr[2] == '\0')
1417       parsed = TRUE;
1418   }
1419
1420   if(parsed) {
1421     infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", size);
1422     Curl_pgrsSetDownloadSize(data, size);
1423
1424     if(pp->cache) {
1425       /* At this point there is a bunch of data in the header "cache" that is
1426          actually body content, send it as body and then skip it. Do note
1427          that there may even be additional "headers" after the body. */
1428       size_t chunk = pp->cache_size;
1429
1430       if(chunk > (size_t)size)
1431         /* The conversion from curl_off_t to size_t is always fine here */
1432         chunk = (size_t)size;
1433
1434       result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1435       if(result)
1436         return result;
1437
1438       data->req.bytecount += chunk;
1439       size -= chunk;
1440
1441       infof(data, "Written %" FORMAT_OFF_TU " bytes, %" FORMAT_OFF_TU
1442             " bytes are left for transfer\n", (curl_off_t)chunk, size);
1443
1444       /* Have we used the entire cache or just part of it?*/
1445       if(pp->cache_size > chunk) {
1446         /* Only part of it so shrink the cache to fit the trailing data */
1447         memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1448         pp->cache_size -= chunk;
1449       }
1450       else {
1451         /* Free the cache */
1452         Curl_safefree(pp->cache);
1453
1454         /* Reset the cache size */
1455         pp->cache_size = 0;
1456       }
1457     }
1458
1459     if(!size)
1460       /* The entire data is already transferred! */
1461       Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1462     else {
1463       /* IMAP download */
1464       data->req.maxdownload = size;
1465       Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1466     }
1467   }
1468   else {
1469     /* We don't know how to parse this line */
1470     failf(pp->conn->data, "Failed to parse FETCH response.");
1471     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1472   }
1473
1474   /* End of DO phase */
1475   state(conn, IMAP_STOP);
1476
1477   return result;
1478 }
1479
1480 /* For final FETCH responses performed after the download */
1481 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1482                                             int imapcode,
1483                                             imapstate instate)
1484 {
1485   CURLcode result = CURLE_OK;
1486
1487   (void)instate; /* No use for this yet */
1488
1489   if(imapcode != 'O')
1490     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1491   else
1492     result = CURLE_OK;
1493
1494   /* End of DONE phase */
1495   state(conn, IMAP_STOP);
1496
1497   return result;
1498 }
1499
1500 /* For APPEND responses */
1501 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1502                                        imapstate instate)
1503 {
1504   CURLcode result = CURLE_OK;
1505   struct SessionHandle *data = conn->data;
1506
1507   (void)instate; /* No use for this yet */
1508
1509   if(imapcode != '+') {
1510     result = CURLE_UPLOAD_FAILED;
1511   }
1512   else {
1513     Curl_pgrsSetUploadSize(data, data->set.infilesize);
1514
1515     /* IMAP upload */
1516     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1517   }
1518
1519   /* End of DO phase */
1520   state(conn, IMAP_STOP);
1521
1522   return result;
1523 }
1524
1525 /* For final APPEND responses performed after the upload */
1526 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1527                                              int imapcode,
1528                                              imapstate instate)
1529 {
1530   CURLcode result = CURLE_OK;
1531
1532   (void)instate; /* No use for this yet */
1533
1534   /* Final response, stop and return the final status */
1535   if(imapcode == 'O')
1536     result = CURLE_OK;
1537   else
1538     result = CURLE_UPLOAD_FAILED;
1539
1540   /* End of DONE phase */
1541   state(conn, IMAP_STOP);
1542
1543   return result;
1544 }
1545
1546 /* For custom request responses */
1547 static CURLcode imap_state_custom_resp(struct connectdata *conn,
1548                                        int imapcode,
1549                                        imapstate instate)
1550 {
1551   CURLcode result = CURLE_OK;
1552   char *line = conn->data->state.buffer;
1553   size_t len = strlen(line);
1554
1555   (void)instate; /* No use for this yet */
1556
1557   if(imapcode == '*') {
1558     /* The client which asked for this custom command should know best
1559        how to cope with the result, just send it as body.
1560        Add back the LF character temporarily while saving. */
1561     line[len] = '\n';
1562     result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1563     line[len] = '\0';
1564   }
1565   else {
1566     /* Final response. Stop and return the final status. */
1567     if(imapcode != 'O')
1568       result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1569     else
1570       result = CURLE_OK;
1571
1572     /* End of DO phase */
1573     state(conn, IMAP_STOP);
1574   }
1575
1576   return result;
1577 }
1578
1579 static CURLcode imap_statemach_act(struct connectdata *conn)
1580 {
1581   CURLcode result = CURLE_OK;
1582   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1583   int imapcode;
1584   struct imap_conn *imapc = &conn->proto.imapc;
1585   struct pingpong *pp = &imapc->pp;
1586   size_t nread = 0;
1587
1588   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1589   if(imapc->state == IMAP_UPGRADETLS)
1590     return imap_state_upgrade_tls(conn);
1591
1592   /* Flush any data that needs to be sent */
1593   if(pp->sendleft)
1594     return Curl_pp_flushsend(pp);
1595
1596   do {
1597     /* Read the response from the server */
1598     result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1599     if(result)
1600       return result;
1601
1602     /* Was there an error parsing the response line? */
1603     if(imapcode == -1)
1604       return CURLE_FTP_WEIRD_SERVER_REPLY;
1605
1606     if(!imapcode)
1607       break;
1608
1609     /* We have now received a full IMAP server response */
1610     switch(imapc->state) {
1611     case IMAP_SERVERGREET:
1612       result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1613       break;
1614
1615     case IMAP_CAPABILITY:
1616       result = imap_state_capability_resp(conn, imapcode, imapc->state);
1617       break;
1618
1619     case IMAP_STARTTLS:
1620       result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1621       break;
1622
1623     case IMAP_AUTHENTICATE_PLAIN:
1624       result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1625       break;
1626
1627     case IMAP_AUTHENTICATE_LOGIN:
1628       result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1629       break;
1630
1631     case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1632       result = imap_state_auth_login_password_resp(conn, imapcode,
1633                                                    imapc->state);
1634       break;
1635
1636 #ifndef CURL_DISABLE_CRYPTO_AUTH
1637     case IMAP_AUTHENTICATE_CRAMMD5:
1638       result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1639       break;
1640
1641     case IMAP_AUTHENTICATE_DIGESTMD5:
1642       result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1643       break;
1644
1645     case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1646       result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1647       break;
1648 #endif
1649
1650 #ifdef USE_NTLM
1651     case IMAP_AUTHENTICATE_NTLM:
1652       result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1653       break;
1654
1655     case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1656       result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1657                                                   imapc->state);
1658       break;
1659 #endif
1660
1661     case IMAP_AUTHENTICATE_FINAL:
1662       result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1663       break;
1664
1665     case IMAP_LOGIN:
1666       result = imap_state_login_resp(conn, imapcode, imapc->state);
1667       break;
1668
1669     case IMAP_LIST:
1670       result = imap_state_list_resp(conn, imapcode, imapc->state);
1671       break;
1672
1673     case IMAP_SELECT:
1674       result = imap_state_select_resp(conn, imapcode, imapc->state);
1675       break;
1676
1677     case IMAP_FETCH:
1678       result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1679       break;
1680
1681     case IMAP_FETCH_FINAL:
1682       result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1683       break;
1684
1685     case IMAP_APPEND:
1686       result = imap_state_append_resp(conn, imapcode, imapc->state);
1687       break;
1688
1689     case IMAP_APPEND_FINAL:
1690       result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1691       break;
1692
1693     case IMAP_CUSTOM:
1694       result = imap_state_custom_resp(conn, imapcode, imapc->state);
1695       break;
1696
1697     case IMAP_LOGOUT:
1698       /* fallthrough, just stop! */
1699     default:
1700       /* internal error */
1701       state(conn, IMAP_STOP);
1702       break;
1703     }
1704   } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1705
1706   return result;
1707 }
1708
1709 /* Called repeatedly until done from multi.c */
1710 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1711 {
1712   CURLcode result = CURLE_OK;
1713   struct imap_conn *imapc = &conn->proto.imapc;
1714
1715   if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1716     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1717   else
1718     result = Curl_pp_statemach(&imapc->pp, FALSE);
1719
1720   *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1721
1722   return result;
1723 }
1724
1725 static CURLcode imap_block_statemach(struct connectdata *conn)
1726 {
1727   CURLcode result = CURLE_OK;
1728   struct imap_conn *imapc = &conn->proto.imapc;
1729
1730   while(imapc->state != IMAP_STOP) {
1731     result = Curl_pp_statemach(&imapc->pp, TRUE);
1732     if(result)
1733       break;
1734   }
1735
1736   return result;
1737 }
1738
1739 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1740    required */
1741 static CURLcode imap_init(struct connectdata *conn)
1742 {
1743   struct SessionHandle *data = conn->data;
1744   struct IMAP *imap = data->state.proto.imap;
1745
1746   if(!imap) {
1747     imap = data->state.proto.imap = calloc(sizeof(struct IMAP), 1);
1748     if(!imap)
1749       return CURLE_OUT_OF_MEMORY;
1750   }
1751
1752   return CURLE_OK;
1753 }
1754
1755 /* For the IMAP "protocol connect" and "doing" phases only */
1756 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1757                         int numsocks)
1758 {
1759   return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1760 }
1761
1762 /***********************************************************************
1763  *
1764  * imap_connect() should do everything that is to be considered a part of
1765  * the connection phase.
1766  *
1767  * The variable 'done' points to will be TRUE if the protocol-layer connect
1768  * phase is done when this function returns, or FALSE is not. When called as
1769  * a part of the easy interface, it will always be TRUE.
1770  */
1771 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1772 {
1773   CURLcode result = CURLE_OK;
1774   struct imap_conn *imapc = &conn->proto.imapc;
1775   struct pingpong *pp = &imapc->pp;
1776
1777   *done = FALSE; /* default to not done yet */
1778
1779   /* If there already is a protocol-specific struct allocated for this
1780      sessionhandle, deal with it */
1781   Curl_reset_reqproto(conn);
1782
1783   /* Initialise the IMAP layer */
1784   result = imap_init(conn);
1785   if(result)
1786     return result;
1787
1788   /* We always support persistent connections in IMAP */
1789   conn->bits.close = FALSE;
1790
1791   /* Set the default response time-out */
1792   pp->response_time = RESP_TIMEOUT;
1793   pp->statemach_act = imap_statemach_act;
1794   pp->endofresp = imap_endofresp;
1795   pp->conn = conn;
1796
1797   /* Initialise the pingpong layer */
1798   Curl_pp_init(pp);
1799
1800   /* Start off waiting for the server greeting response */
1801   state(conn, IMAP_SERVERGREET);
1802
1803   /* Start off with an response id of '*' */
1804   strcpy(imapc->resptag, "*");
1805
1806   result = imap_multi_statemach(conn, done);
1807
1808   return result;
1809 }
1810
1811 /***********************************************************************
1812  *
1813  * imap_done()
1814  *
1815  * The DONE function. This does what needs to be done after a single DO has
1816  * performed.
1817  *
1818  * Input argument is already checked for validity.
1819  */
1820 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1821                           bool premature)
1822 {
1823   CURLcode result = CURLE_OK;
1824   struct SessionHandle *data = conn->data;
1825   struct IMAP *imap = data->state.proto.imap;
1826
1827   (void)premature;
1828
1829   if(!imap)
1830     /* When the easy handle is removed from the multi interface while libcurl
1831        is still trying to resolve the host name, the IMAP struct is not yet
1832        initialized. However, the removal action calls Curl_done() which in
1833        turn calls this function, so we simply return success. */
1834     return CURLE_OK;
1835
1836   if(status) {
1837     conn->bits.close = TRUE; /* marked for closure */
1838     result = status;         /* use the already set error code */
1839   }
1840   else if(!data->set.connect_only && !imap->custom) {
1841     /* Handle responses after FETCH or APPEND transfer has finished */
1842     if(!data->set.upload)
1843       state(conn, IMAP_FETCH_FINAL);
1844     else {
1845       /* End the APPEND command first by sending an empty line */
1846       result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
1847       if(!result)
1848         state(conn, IMAP_APPEND_FINAL);
1849     }
1850
1851     /* Run the state-machine
1852
1853        TODO: when the multi interface is used, this _really_ should be using
1854        the imap_multi_statemach function but we have no general support for
1855        non-blocking DONE operations, not in the multi state machine and with
1856        Curl_done() invokes on several places in the code!
1857     */
1858     if(!result)
1859       result = imap_block_statemach(conn);
1860   }
1861
1862   /* Cleanup our per-request based variables */
1863   Curl_safefree(imap->mailbox);
1864   Curl_safefree(imap->uidvalidity);
1865   Curl_safefree(imap->uid);
1866   Curl_safefree(imap->section);
1867   Curl_safefree(imap->custom);
1868   Curl_safefree(imap->custom_params);
1869
1870   /* Clear the transfer mode for the next request */
1871   imap->transfer = FTPTRANSFER_BODY;
1872
1873   return result;
1874 }
1875
1876 /***********************************************************************
1877  *
1878  * imap_perform()
1879  *
1880  * This is the actual DO function for IMAP. Fetch or append a message, or do
1881  * other things according to the options previously setup.
1882  */
1883 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1884                              bool *dophase_done)
1885 {
1886   /* This is IMAP and no proxy */
1887   CURLcode result = CURLE_OK;
1888   struct SessionHandle *data = conn->data;
1889   struct IMAP *imap = data->state.proto.imap;
1890   struct imap_conn *imapc = &conn->proto.imapc;
1891   bool selected = FALSE;
1892
1893   DEBUGF(infof(conn->data, "DO phase starts\n"));
1894
1895   if(conn->data->set.opt_no_body) {
1896     /* Requested no body means no transfer */
1897     imap->transfer = FTPTRANSFER_INFO;
1898   }
1899
1900   *dophase_done = FALSE; /* not done yet */
1901
1902   /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1903      has already been selected on this connection */
1904   if(imap->mailbox && imapc->mailbox &&
1905      !strcmp(imap->mailbox, imapc->mailbox) &&
1906      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1907       !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1908     selected = TRUE;
1909
1910   /* Start the first command in the DO phase */
1911   if(conn->data->set.upload)
1912     /* APPEND can be executed directly */
1913     result = imap_append(conn);
1914   else if(imap->custom && (selected || !imap->mailbox))
1915     /* Custom command using the same mailbox or no mailbox */
1916     result = imap_custom(conn);
1917   else if(!imap->custom && selected)
1918     /* FETCH from the same mailbox */
1919     result = imap_fetch(conn);
1920   else
1921     /* SELECT the mailbox */
1922     result = imap_select(conn);
1923
1924   if(result)
1925     return result;
1926
1927   /* Run the state-machine */
1928   result = imap_multi_statemach(conn, dophase_done);
1929
1930   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1931
1932   if(*dophase_done)
1933     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1934
1935   return result;
1936 }
1937
1938 /***********************************************************************
1939  *
1940  * imap_do()
1941  *
1942  * This function is registered as 'curl_do' function. It decodes the path
1943  * parts etc as a wrapper to the actual DO function (imap_perform).
1944  *
1945  * The input argument is already checked for validity.
1946  */
1947 static CURLcode imap_do(struct connectdata *conn, bool *done)
1948 {
1949   CURLcode result = CURLE_OK;
1950
1951   *done = FALSE; /* default to false */
1952
1953   /* Since connections can be re-used between SessionHandles, there might be a
1954      connection already existing but on a fresh SessionHandle struct. As such
1955      we make sure we have a good IMAP struct to play with. For new connections
1956      the IMAP struct is allocated and setup in the imap_connect() function. */
1957   Curl_reset_reqproto(conn);
1958   result = imap_init(conn);
1959   if(result)
1960     return result;
1961
1962   /* Parse the URL path */
1963   result = imap_parse_url_path(conn);
1964   if(result)
1965     return result;
1966
1967   /* Parse the custom request */
1968   result = imap_parse_custom_request(conn);
1969   if(result)
1970     return result;
1971
1972   result = imap_regular_transfer(conn, done);
1973
1974   return result;
1975 }
1976
1977 /***********************************************************************
1978  *
1979  * imap_logout()
1980  *
1981  * This should be called before calling sclose().  We should then wait for the
1982  * response from the server before returning. The calling code should then try
1983  * to close the connection.
1984  *
1985  */
1986 static CURLcode imap_logout(struct connectdata *conn)
1987 {
1988   CURLcode result = CURLE_OK;
1989
1990   /* Send the LOGOUT command */
1991   result = imap_sendf(conn, "LOGOUT", NULL);
1992   if(result)
1993     return result;
1994
1995   state(conn, IMAP_LOGOUT);
1996
1997   result = imap_block_statemach(conn);
1998
1999   return result;
2000 }
2001
2002 /***********************************************************************
2003  *
2004  * imap_disconnect()
2005  *
2006  * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
2007  * resources. BLOCKING.
2008  */
2009 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2010 {
2011   struct imap_conn *imapc = &conn->proto.imapc;
2012
2013   /* We cannot send quit unconditionally. If this connection is stale or
2014      bad in any way, sending quit and waiting around here will make the
2015      disconnect wait in vain and cause more problems than we need to. */
2016
2017   /* The IMAP session may or may not have been allocated/setup at this
2018      point! */
2019   if(!dead_connection && imapc->pp.conn)
2020     (void)imap_logout(conn); /* ignore errors on LOGOUT */
2021
2022   /* Disconnect from the server */
2023   Curl_pp_disconnect(&imapc->pp);
2024
2025   /* Cleanup the SASL module */
2026   Curl_sasl_cleanup(conn, imapc->authused);
2027
2028   /* Cleanup our connection based variables */
2029   Curl_safefree(imapc->mailbox);
2030   Curl_safefree(imapc->mailbox_uidvalidity);
2031
2032   return CURLE_OK;
2033 }
2034
2035 /***********************************************************************
2036  *
2037  * imap_is_bchar()
2038  *
2039  * Portable test of whether the specified char is a "bchar" as defined in the
2040  * grammar of RFC-5092.
2041  */
2042 static bool imap_is_bchar(char ch)
2043 {
2044   switch(ch) {
2045     /* bchar */
2046     case ':': case '@': case '/':
2047     /* bchar -> achar */
2048     case '&': case '=':
2049     /* bchar -> achar -> uchar -> unreserved */
2050     case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2051     case '7': case '8': case '9':
2052     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2053     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2054     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2055     case 'V': case 'W': case 'X': case 'Y': case 'Z':
2056     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2057     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2058     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2059     case 'v': case 'w': case 'x': case 'y': case 'z':
2060     case '-': case '.': case '_': case '~':
2061     /* bchar -> achar -> uchar -> sub-delims-sh */
2062     case '!': case '$': case '\'': case '(': case ')': case '*':
2063     case '+': case ',':
2064     /* bchar -> achar -> uchar -> pct-encoded */
2065     case '%': /* HEXDIG chars are already included above */
2066       return true;
2067
2068     default:
2069       return false;
2070   }
2071 }
2072
2073 /***********************************************************************
2074  *
2075  * imap_parse_url_path()
2076  *
2077  * Parse the URL path into separate path components.
2078  *
2079  */
2080 static CURLcode imap_parse_url_path(struct connectdata *conn)
2081 {
2082   /* The imap struct is already initialised in imap_connect() */
2083   CURLcode result = CURLE_OK;
2084   struct SessionHandle *data = conn->data;
2085   struct IMAP *imap = data->state.proto.imap;
2086   const char *begin = data->state.path;
2087   const char *ptr = begin;
2088
2089   /* See how much of the URL is a valid path and decode it */
2090   while(imap_is_bchar(*ptr))
2091     ptr++;
2092
2093   if(ptr != begin) {
2094     /* Remove the trailing slash if present */
2095     const char *end = ptr;
2096     if(end > begin && end[-1] == '/')
2097       end--;
2098
2099     result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2100                             TRUE);
2101     if(result)
2102       return result;
2103   }
2104   else
2105     imap->mailbox = NULL;
2106
2107   /* There can be any number of parameters in the form ";NAME=VALUE" */
2108   while(*ptr == ';') {
2109     char *name;
2110     char *value;
2111     size_t valuelen;
2112
2113     /* Find the length of the name parameter */
2114     begin = ++ptr;
2115     while(*ptr && *ptr != '=')
2116       ptr++;
2117
2118     if(!*ptr)
2119       return CURLE_URL_MALFORMAT;
2120
2121     /* Decode the name parameter */
2122     result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2123     if(result)
2124       return result;
2125
2126     /* Find the length of the value parameter */
2127     begin = ++ptr;
2128     while(imap_is_bchar(*ptr))
2129       ptr++;
2130
2131     /* Decode the value parameter */
2132     result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2133     if(result) {
2134       Curl_safefree(name);
2135       return result;
2136     }
2137
2138     DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2139
2140     /* Process the known hierarchical parameters (UIDVALIDITY, UID and SECTION)
2141        stripping of the trailing slash character if it is present.
2142
2143        Note: Unknown parameters trigger a URL_MALFORMAT error. */
2144     if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2145       if(valuelen > 0 && value[valuelen - 1] == '/')
2146         value[valuelen - 1] = '\0';
2147
2148       imap->uidvalidity = value;
2149       value = NULL;
2150     }
2151     else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2152       if(valuelen > 0 && value[valuelen - 1] == '/')
2153         value[valuelen - 1] = '\0';
2154
2155       imap->uid = value;
2156       value = NULL;
2157     }
2158     else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2159       if(valuelen > 0 && value[valuelen - 1] == '/')
2160         value[valuelen - 1] = '\0';
2161
2162       imap->section = value;
2163       value = NULL;
2164     }
2165     else {
2166       Curl_safefree(name);
2167       Curl_safefree(value);
2168
2169       return CURLE_URL_MALFORMAT;
2170     }
2171
2172     Curl_safefree(name);
2173     Curl_safefree(value);
2174   }
2175
2176   /* Any extra stuff at the end of the URL is an error */
2177   if(*ptr)
2178     return CURLE_URL_MALFORMAT;
2179
2180   return CURLE_OK;
2181 }
2182
2183 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2184 {
2185   CURLcode result = CURLE_OK;
2186   struct SessionHandle *data = conn->data;
2187   struct IMAP *imap = data->state.proto.imap;
2188   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2189
2190   if(custom) {
2191     /* URL decode the custom request */
2192     result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2193
2194     /* Extract the parameters if specified */
2195     if(!result) {
2196       const char *params = imap->custom;
2197
2198       while(*params && *params != ' ')
2199         params++;
2200
2201       if(*params) {
2202         imap->custom_params = strdup(params);
2203         imap->custom[params - imap->custom] = '\0';
2204
2205         if(!imap->custom_params)
2206           result = CURLE_OUT_OF_MEMORY;
2207       }
2208     }
2209   }
2210
2211   return result;
2212 }
2213
2214 /* Call this when the DO phase has completed */
2215 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2216 {
2217   struct IMAP *imap = conn->data->state.proto.imap;
2218
2219   (void)connected;
2220
2221   if(imap->transfer != FTPTRANSFER_BODY)
2222     /* no data to transfer */
2223     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2224
2225   return CURLE_OK;
2226 }
2227
2228 /* Called from multi.c while DOing */
2229 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2230 {
2231   CURLcode result = imap_multi_statemach(conn, dophase_done);
2232
2233   if(result)
2234     DEBUGF(infof(conn->data, "DO phase failed\n"));
2235   else {
2236     if(*dophase_done) {
2237       result = imap_dophase_done(conn, FALSE /* not connected */);
2238
2239       DEBUGF(infof(conn->data, "DO phase is complete\n"));
2240     }
2241   }
2242
2243   return result;
2244 }
2245
2246 /***********************************************************************
2247  *
2248  * imap_regular_transfer()
2249  *
2250  * The input argument is already checked for validity.
2251  *
2252  * Performs all commands done before a regular transfer between a local and a
2253  * remote host.
2254  */
2255 static CURLcode imap_regular_transfer(struct connectdata *conn,
2256                                       bool *dophase_done)
2257 {
2258   CURLcode result = CURLE_OK;
2259   bool connected = FALSE;
2260   struct SessionHandle *data = conn->data;
2261
2262   /* Make sure size is unknown at this point */
2263   data->req.size = -1;
2264
2265   Curl_pgrsSetUploadCounter(data, 0);
2266   Curl_pgrsSetDownloadCounter(data, 0);
2267   Curl_pgrsSetUploadSize(data, 0);
2268   Curl_pgrsSetDownloadSize(data, 0);
2269
2270   result = imap_perform(conn, &connected, dophase_done);
2271
2272   if(!result) {
2273     if(!*dophase_done)
2274       /* The DO phase has not completed yet */
2275       return CURLE_OK;
2276
2277     result = imap_dophase_done(conn, connected);
2278   }
2279
2280   return result;
2281 }
2282
2283 static CURLcode imap_setup_connection(struct connectdata *conn)
2284 {
2285   struct SessionHandle *data = conn->data;
2286
2287   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2288     /* Unless we have asked to tunnel IMAP operations through the proxy, we
2289        switch and use HTTP operations only */
2290 #ifndef CURL_DISABLE_HTTP
2291     if(conn->handler == &Curl_handler_imap)
2292       conn->handler = &Curl_handler_imap_proxy;
2293     else {
2294 #ifdef USE_SSL
2295       conn->handler = &Curl_handler_imaps_proxy;
2296 #else
2297       failf(data, "IMAPS not supported!");
2298       return CURLE_UNSUPPORTED_PROTOCOL;
2299 #endif
2300     }
2301
2302     /* We explicitly mark this connection as persistent here as we're doing
2303        IMAP over HTTP and thus we accidentally avoid setting this value
2304        otherwise */
2305     conn->bits.close = FALSE;
2306 #else
2307     failf(data, "IMAP over http proxy requires HTTP support built-in!");
2308     return CURLE_UNSUPPORTED_PROTOCOL;
2309 #endif
2310   }
2311
2312   data->state.path++;   /* don't include the initial slash */
2313
2314   return CURLE_OK;
2315 }
2316
2317 #endif /* CURL_DISABLE_IMAP */