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