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