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