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