smtp: use the upload buffer size for scratch buffer malloc
[platform/upstream/curl.git] / lib / imap.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2018, 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 https://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  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28  * RFC4959 IMAP Extension for SASL Initial Client Response
29  * RFC5092 IMAP URL Scheme
30  * RFC6749 OAuth 2.0 Authorization Framework
31  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
32  *
33  ***************************************************************************/
34
35 #include "curl_setup.h"
36
37 #ifndef CURL_DISABLE_IMAP
38
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #ifdef HAVE_UTSNAME_H
46 #include <sys/utsname.h>
47 #endif
48 #ifdef HAVE_NETDB_H
49 #include <netdb.h>
50 #endif
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57 #undef in_addr_t
58 #define in_addr_t unsigned long
59 #endif
60
61 #include <curl/curl.h>
62 #include "urldata.h"
63 #include "sendf.h"
64 #include "hostip.h"
65 #include "progress.h"
66 #include "transfer.h"
67 #include "escape.h"
68 #include "http.h" /* for HTTP proxy tunnel stuff */
69 #include "socks.h"
70 #include "imap.h"
71 #include "mime.h"
72 #include "strtoofft.h"
73 #include "strcase.h"
74 #include "vtls/vtls.h"
75 #include "connect.h"
76 #include "strerror.h"
77 #include "select.h"
78 #include "multiif.h"
79 #include "url.h"
80 #include "strcase.h"
81 #include "curl_sasl.h"
82 #include "warnless.h"
83
84 /* The last 3 #include files should be in this order */
85 #include "curl_printf.h"
86 #include "curl_memory.h"
87 #include "memdebug.h"
88
89 /* Local API functions */
90 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
91 static CURLcode imap_do(struct connectdata *conn, bool *done);
92 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
93                           bool premature);
94 static CURLcode imap_connect(struct connectdata *conn, bool *done);
95 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
96 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
97 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
98                         int numsocks);
99 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode imap_setup_connection(struct connectdata *conn);
101 static char *imap_atom(const char *str, bool escape_only);
102 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
103 static CURLcode imap_parse_url_options(struct connectdata *conn);
104 static CURLcode imap_parse_url_path(struct connectdata *conn);
105 static CURLcode imap_parse_custom_request(struct connectdata *conn);
106 static CURLcode imap_perform_authenticate(struct connectdata *conn,
107                                           const char *mech,
108                                           const char *initresp);
109 static CURLcode imap_continue_authenticate(struct connectdata *conn,
110                                            const char *resp);
111 static void imap_get_message(char *buffer, char **outptr);
112
113 /*
114  * IMAP protocol handler.
115  */
116
117 const struct Curl_handler Curl_handler_imap = {
118   "IMAP",                           /* scheme */
119   imap_setup_connection,            /* setup_connection */
120   imap_do,                          /* do_it */
121   imap_done,                        /* done */
122   ZERO_NULL,                        /* do_more */
123   imap_connect,                     /* connect_it */
124   imap_multi_statemach,             /* connecting */
125   imap_doing,                       /* doing */
126   imap_getsock,                     /* proto_getsock */
127   imap_getsock,                     /* doing_getsock */
128   ZERO_NULL,                        /* domore_getsock */
129   ZERO_NULL,                        /* perform_getsock */
130   imap_disconnect,                  /* disconnect */
131   ZERO_NULL,                        /* readwrite */
132   ZERO_NULL,                        /* connection_check */
133   PORT_IMAP,                        /* defport */
134   CURLPROTO_IMAP,                   /* protocol */
135   PROTOPT_CLOSEACTION|              /* flags */
136   PROTOPT_URLOPTIONS
137 };
138
139 #ifdef USE_SSL
140 /*
141  * IMAPS protocol handler.
142  */
143
144 const struct Curl_handler Curl_handler_imaps = {
145   "IMAPS",                          /* scheme */
146   imap_setup_connection,            /* setup_connection */
147   imap_do,                          /* do_it */
148   imap_done,                        /* done */
149   ZERO_NULL,                        /* do_more */
150   imap_connect,                     /* connect_it */
151   imap_multi_statemach,             /* connecting */
152   imap_doing,                       /* doing */
153   imap_getsock,                     /* proto_getsock */
154   imap_getsock,                     /* doing_getsock */
155   ZERO_NULL,                        /* domore_getsock */
156   ZERO_NULL,                        /* perform_getsock */
157   imap_disconnect,                  /* disconnect */
158   ZERO_NULL,                        /* readwrite */
159   ZERO_NULL,                        /* connection_check */
160   PORT_IMAPS,                       /* defport */
161   CURLPROTO_IMAPS,                  /* protocol */
162   PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */
163 };
164 #endif
165
166 #define IMAP_RESP_OK       1
167 #define IMAP_RESP_NOT_OK   2
168 #define IMAP_RESP_PREAUTH  3
169
170 /* SASL parameters for the imap protocol */
171 static const struct SASLproto saslimap = {
172   "imap",                     /* The service name */
173   '+',                        /* Code received when continuation is expected */
174   IMAP_RESP_OK,               /* Code to receive upon authentication success */
175   0,                          /* Maximum initial response length (no max) */
176   imap_perform_authenticate,  /* Send authentication command */
177   imap_continue_authenticate, /* Send authentication continuation */
178   imap_get_message            /* Get SASL response message */
179 };
180
181
182 #ifdef USE_SSL
183 static void imap_to_imaps(struct connectdata *conn)
184 {
185   /* Change the connection handler */
186   conn->handler = &Curl_handler_imaps;
187
188   /* Set the connection's upgraded to TLS flag */
189   conn->tls_upgraded = TRUE;
190 }
191 #else
192 #define imap_to_imaps(x) Curl_nop_stmt
193 #endif
194
195 /***********************************************************************
196  *
197  * imap_matchresp()
198  *
199  * Determines whether the untagged response is related to the specified
200  * command by checking if it is in format "* <command-name> ..." or
201  * "* <number> <command-name> ...".
202  *
203  * The "* " marker is assumed to have already been checked by the caller.
204  */
205 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
206 {
207   const char *end = line + len;
208   size_t cmd_len = strlen(cmd);
209
210   /* Skip the untagged response marker */
211   line += 2;
212
213   /* Do we have a number after the marker? */
214   if(line < end && ISDIGIT(*line)) {
215     /* Skip the number */
216     do
217       line++;
218     while(line < end && ISDIGIT(*line));
219
220     /* Do we have the space character? */
221     if(line == end || *line != ' ')
222       return FALSE;
223
224     line++;
225   }
226
227   /* Does the command name match and is it followed by a space character or at
228      the end of line? */
229   if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
230      (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
231     return TRUE;
232
233   return FALSE;
234 }
235
236 /***********************************************************************
237  *
238  * imap_endofresp()
239  *
240  * Checks whether the given string is a valid tagged, untagged or continuation
241  * response which can be processed by the response handler.
242  */
243 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
244                            int *resp)
245 {
246   struct IMAP *imap = conn->data->req.protop;
247   struct imap_conn *imapc = &conn->proto.imapc;
248   const char *id = imapc->resptag;
249   size_t id_len = strlen(id);
250
251   /* Do we have a tagged command response? */
252   if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
253     line += id_len + 1;
254     len -= id_len + 1;
255
256     if(len >= 2 && !memcmp(line, "OK", 2))
257       *resp = IMAP_RESP_OK;
258     else if(len >= 7 && !memcmp(line, "PREAUTH", 7))
259       *resp = IMAP_RESP_PREAUTH;
260     else
261       *resp = IMAP_RESP_NOT_OK;
262
263     return TRUE;
264   }
265
266   /* Do we have an untagged command response? */
267   if(len >= 2 && !memcmp("* ", line, 2)) {
268     switch(imapc->state) {
269       /* States which are interested in untagged responses */
270       case IMAP_CAPABILITY:
271         if(!imap_matchresp(line, len, "CAPABILITY"))
272           return FALSE;
273         break;
274
275       case IMAP_LIST:
276         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
277           (imap->custom && !imap_matchresp(line, len, imap->custom) &&
278            (!strcasecompare(imap->custom, "STORE") ||
279             !imap_matchresp(line, len, "FETCH")) &&
280            !strcasecompare(imap->custom, "SELECT") &&
281            !strcasecompare(imap->custom, "EXAMINE") &&
282            !strcasecompare(imap->custom, "SEARCH") &&
283            !strcasecompare(imap->custom, "EXPUNGE") &&
284            !strcasecompare(imap->custom, "LSUB") &&
285            !strcasecompare(imap->custom, "UID") &&
286            !strcasecompare(imap->custom, "NOOP")))
287           return FALSE;
288         break;
289
290       case IMAP_SELECT:
291         /* SELECT is special in that its untagged responses do not have a
292            common prefix so accept anything! */
293         break;
294
295       case IMAP_FETCH:
296         if(!imap_matchresp(line, len, "FETCH"))
297           return FALSE;
298         break;
299
300       case IMAP_SEARCH:
301         if(!imap_matchresp(line, len, "SEARCH"))
302           return FALSE;
303         break;
304
305       /* Ignore other untagged responses */
306       default:
307         return FALSE;
308     }
309
310     *resp = '*';
311     return TRUE;
312   }
313
314   /* Do we have a continuation response? This should be a + symbol followed by
315      a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
316      APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
317      some e-mail servers ignore this and only send a single + instead. */
318   if(imap && !imap->custom && ((len == 3 && !memcmp("+", line, 1)) ||
319      (len >= 2 && !memcmp("+ ", line, 2)))) {
320     switch(imapc->state) {
321       /* States which are interested in continuation responses */
322       case IMAP_AUTHENTICATE:
323       case IMAP_APPEND:
324         *resp = '+';
325         break;
326
327       default:
328         failf(conn->data, "Unexpected continuation response");
329         *resp = -1;
330         break;
331     }
332
333     return TRUE;
334   }
335
336   return FALSE; /* Nothing for us */
337 }
338
339 /***********************************************************************
340  *
341  * imap_get_message()
342  *
343  * Gets the authentication message from the response buffer.
344  */
345 static void imap_get_message(char *buffer, char **outptr)
346 {
347   size_t len = strlen(buffer);
348   char *message = NULL;
349
350   if(len > 2) {
351     /* Find the start of the message */
352     len -= 2;
353     for(message = buffer + 2; *message == ' ' || *message == '\t';
354         message++, len--)
355       ;
356
357     /* Find the end of the message */
358     for(; len--;)
359       if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
360          message[len] != '\t')
361         break;
362
363     /* Terminate the message */
364     if(++len) {
365       message[len] = '\0';
366     }
367   }
368   else
369     /* junk input => zero length output */
370     message = &buffer[len];
371
372   *outptr = message;
373 }
374
375 /***********************************************************************
376  *
377  * state()
378  *
379  * This is the ONLY way to change IMAP state!
380  */
381 static void state(struct connectdata *conn, imapstate newstate)
382 {
383   struct imap_conn *imapc = &conn->proto.imapc;
384 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
385   /* for debug purposes */
386   static const char * const names[]={
387     "STOP",
388     "SERVERGREET",
389     "CAPABILITY",
390     "STARTTLS",
391     "UPGRADETLS",
392     "AUTHENTICATE",
393     "LOGIN",
394     "LIST",
395     "SELECT",
396     "FETCH",
397     "FETCH_FINAL",
398     "APPEND",
399     "APPEND_FINAL",
400     "SEARCH",
401     "LOGOUT",
402     /* LAST */
403   };
404
405   if(imapc->state != newstate)
406     infof(conn->data, "IMAP %p state change from %s to %s\n",
407           (void *)imapc, names[imapc->state], names[newstate]);
408 #endif
409
410   imapc->state = newstate;
411 }
412
413 /***********************************************************************
414  *
415  * imap_perform_capability()
416  *
417  * Sends the CAPABILITY command in order to obtain a list of server side
418  * supported capabilities.
419  */
420 static CURLcode imap_perform_capability(struct connectdata *conn)
421 {
422   CURLcode result = CURLE_OK;
423   struct imap_conn *imapc = &conn->proto.imapc;
424
425   imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
426   imapc->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
427   imapc->tls_supported = FALSE;           /* Clear the TLS capability */
428
429   /* Send the CAPABILITY command */
430   result = imap_sendf(conn, "CAPABILITY");
431
432   if(!result)
433     state(conn, IMAP_CAPABILITY);
434
435   return result;
436 }
437
438 /***********************************************************************
439  *
440  * imap_perform_starttls()
441  *
442  * Sends the STARTTLS command to start the upgrade to TLS.
443  */
444 static CURLcode imap_perform_starttls(struct connectdata *conn)
445 {
446   CURLcode result = CURLE_OK;
447
448   /* Send the STARTTLS command */
449   result = imap_sendf(conn, "STARTTLS");
450
451   if(!result)
452     state(conn, IMAP_STARTTLS);
453
454   return result;
455 }
456
457 /***********************************************************************
458  *
459  * imap_perform_upgrade_tls()
460  *
461  * Performs the upgrade to TLS.
462  */
463 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
464 {
465   CURLcode result = CURLE_OK;
466   struct imap_conn *imapc = &conn->proto.imapc;
467
468   /* Start the SSL connection */
469   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
470
471   if(!result) {
472     if(imapc->state != IMAP_UPGRADETLS)
473       state(conn, IMAP_UPGRADETLS);
474
475     if(imapc->ssldone) {
476       imap_to_imaps(conn);
477       result = imap_perform_capability(conn);
478     }
479   }
480
481   return result;
482 }
483
484 /***********************************************************************
485  *
486  * imap_perform_login()
487  *
488  * Sends a clear text LOGIN command to authenticate with.
489  */
490 static CURLcode imap_perform_login(struct connectdata *conn)
491 {
492   CURLcode result = CURLE_OK;
493   char *user;
494   char *passwd;
495
496   /* Check we have a username and password to authenticate with and end the
497      connect phase if we don't */
498   if(!conn->bits.user_passwd) {
499     state(conn, IMAP_STOP);
500
501     return result;
502   }
503
504   /* Make sure the username and password are in the correct atom format */
505   user = imap_atom(conn->user, false);
506   passwd = imap_atom(conn->passwd, false);
507
508   /* Send the LOGIN command */
509   result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
510                       passwd ? passwd : "");
511
512   free(user);
513   free(passwd);
514
515   if(!result)
516     state(conn, IMAP_LOGIN);
517
518   return result;
519 }
520
521 /***********************************************************************
522  *
523  * imap_perform_authenticate()
524  *
525  * Sends an AUTHENTICATE command allowing the client to login with the given
526  * SASL authentication mechanism.
527  */
528 static CURLcode imap_perform_authenticate(struct connectdata *conn,
529                                           const char *mech,
530                                           const char *initresp)
531 {
532   CURLcode result = CURLE_OK;
533
534   if(initresp) {
535     /* Send the AUTHENTICATE command with the initial response */
536     result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
537   }
538   else {
539     /* Send the AUTHENTICATE command */
540     result = imap_sendf(conn, "AUTHENTICATE %s", mech);
541   }
542
543   return result;
544 }
545
546 /***********************************************************************
547  *
548  * imap_continue_authenticate()
549  *
550  * Sends SASL continuation data or cancellation.
551  */
552 static CURLcode imap_continue_authenticate(struct connectdata *conn,
553                                            const char *resp)
554 {
555   struct imap_conn *imapc = &conn->proto.imapc;
556
557   return Curl_pp_sendf(&imapc->pp, "%s", resp);
558 }
559
560 /***********************************************************************
561  *
562  * imap_perform_authentication()
563  *
564  * Initiates the authentication sequence, with the appropriate SASL
565  * authentication mechanism, falling back to clear text should a common
566  * mechanism not be available between the client and server.
567  */
568 static CURLcode imap_perform_authentication(struct connectdata *conn)
569 {
570   CURLcode result = CURLE_OK;
571   struct imap_conn *imapc = &conn->proto.imapc;
572   saslprogress progress;
573
574   /* Check if already authenticated OR if there is enough data to authenticate
575      with and end the connect phase if we don't */
576   if(imapc->preauth ||
577      !Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
578     state(conn, IMAP_STOP);
579     return result;
580   }
581
582   /* Calculate the SASL login details */
583   result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
584
585   if(!result) {
586     if(progress == SASL_INPROGRESS)
587       state(conn, IMAP_AUTHENTICATE);
588     else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
589       /* Perform clear text authentication */
590       result = imap_perform_login(conn);
591     else {
592       /* Other mechanisms not supported */
593       infof(conn->data, "No known authentication mechanisms supported!\n");
594       result = CURLE_LOGIN_DENIED;
595     }
596   }
597
598   return result;
599 }
600
601 /***********************************************************************
602  *
603  * imap_perform_list()
604  *
605  * Sends a LIST command or an alternative custom request.
606  */
607 static CURLcode imap_perform_list(struct connectdata *conn)
608 {
609   CURLcode result = CURLE_OK;
610   struct Curl_easy *data = conn->data;
611   struct IMAP *imap = data->req.protop;
612   char *mailbox;
613
614   if(imap->custom)
615     /* Send the custom request */
616     result = imap_sendf(conn, "%s%s", imap->custom,
617                         imap->custom_params ? imap->custom_params : "");
618   else {
619     /* Make sure the mailbox is in the correct atom format if necessary */
620     mailbox = imap->mailbox ? imap_atom(imap->mailbox, true) : strdup("");
621     if(!mailbox)
622       return CURLE_OUT_OF_MEMORY;
623
624     /* Send the LIST command */
625     result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
626
627     free(mailbox);
628   }
629
630   if(!result)
631     state(conn, IMAP_LIST);
632
633   return result;
634 }
635
636 /***********************************************************************
637  *
638  * imap_perform_select()
639  *
640  * Sends a SELECT command to ask the server to change the selected mailbox.
641  */
642 static CURLcode imap_perform_select(struct connectdata *conn)
643 {
644   CURLcode result = CURLE_OK;
645   struct Curl_easy *data = conn->data;
646   struct IMAP *imap = data->req.protop;
647   struct imap_conn *imapc = &conn->proto.imapc;
648   char *mailbox;
649
650   /* Invalidate old information as we are switching mailboxes */
651   Curl_safefree(imapc->mailbox);
652   Curl_safefree(imapc->mailbox_uidvalidity);
653
654   /* Check we have a mailbox */
655   if(!imap->mailbox) {
656     failf(conn->data, "Cannot SELECT without a mailbox.");
657     return CURLE_URL_MALFORMAT;
658   }
659
660   /* Make sure the mailbox is in the correct atom format */
661   mailbox = imap_atom(imap->mailbox, false);
662   if(!mailbox)
663     return CURLE_OUT_OF_MEMORY;
664
665   /* Send the SELECT command */
666   result = imap_sendf(conn, "SELECT %s", mailbox);
667
668   free(mailbox);
669
670   if(!result)
671     state(conn, IMAP_SELECT);
672
673   return result;
674 }
675
676 /***********************************************************************
677  *
678  * imap_perform_fetch()
679  *
680  * Sends a FETCH command to initiate the download of a message.
681  */
682 static CURLcode imap_perform_fetch(struct connectdata *conn)
683 {
684   CURLcode result = CURLE_OK;
685   struct IMAP *imap = conn->data->req.protop;
686
687   /* Check we have a UID */
688   if(!imap->uid) {
689     failf(conn->data, "Cannot FETCH without a UID.");
690     return CURLE_URL_MALFORMAT;
691   }
692
693   /* Send the FETCH command */
694   if(imap->partial)
695     result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
696                         imap->uid,
697                         imap->section ? imap->section : "",
698                         imap->partial);
699   else
700     result = imap_sendf(conn, "FETCH %s BODY[%s]",
701                         imap->uid,
702                         imap->section ? imap->section : "");
703
704   if(!result)
705     state(conn, IMAP_FETCH);
706
707   return result;
708 }
709
710 /***********************************************************************
711  *
712  * imap_perform_append()
713  *
714  * Sends an APPEND command to initiate the upload of a message.
715  */
716 static CURLcode imap_perform_append(struct connectdata *conn)
717 {
718   CURLcode result = CURLE_OK;
719   struct Curl_easy *data = conn->data;
720   struct IMAP *imap = data->req.protop;
721   char *mailbox;
722
723   /* Check we have a mailbox */
724   if(!imap->mailbox) {
725     failf(data, "Cannot APPEND without a mailbox.");
726     return CURLE_URL_MALFORMAT;
727   }
728
729   /* Prepare the mime data if some. */
730   if(data->set.mimepost.kind != MIMEKIND_NONE) {
731     /* Use the whole structure as data. */
732     data->set.mimepost.flags &= ~MIME_BODY_ONLY;
733
734     /* Add external headers and mime version. */
735     curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
736     result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
737                                        NULL, MIMESTRATEGY_MAIL);
738
739     if(!result)
740       if(!Curl_checkheaders(conn, "Mime-Version"))
741         result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
742                                       "Mime-Version: 1.0");
743
744     /* Make sure we will read the entire mime structure. */
745     if(!result)
746       result = Curl_mime_rewind(&data->set.mimepost);
747
748     if(result)
749       return result;
750
751     data->state.infilesize = Curl_mime_size(&data->set.mimepost);
752
753     /* Read from mime structure. */
754     data->state.fread_func = (curl_read_callback) Curl_mime_read;
755     data->state.in = (void *) &data->set.mimepost;
756   }
757
758   /* Check we know the size of the upload */
759   if(data->state.infilesize < 0) {
760     failf(data, "Cannot APPEND with unknown input file size\n");
761     return CURLE_UPLOAD_FAILED;
762   }
763
764   /* Make sure the mailbox is in the correct atom format */
765   mailbox = imap_atom(imap->mailbox, false);
766   if(!mailbox)
767     return CURLE_OUT_OF_MEMORY;
768
769   /* Send the APPEND command */
770   result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
771                       mailbox, data->state.infilesize);
772
773   free(mailbox);
774
775   if(!result)
776     state(conn, IMAP_APPEND);
777
778   return result;
779 }
780
781 /***********************************************************************
782  *
783  * imap_perform_search()
784  *
785  * Sends a SEARCH command.
786  */
787 static CURLcode imap_perform_search(struct connectdata *conn)
788 {
789   CURLcode result = CURLE_OK;
790   struct IMAP *imap = conn->data->req.protop;
791
792   /* Check we have a query string */
793   if(!imap->query) {
794     failf(conn->data, "Cannot SEARCH without a query string.");
795     return CURLE_URL_MALFORMAT;
796   }
797
798   /* Send the SEARCH command */
799   result = imap_sendf(conn, "SEARCH %s", imap->query);
800
801   if(!result)
802     state(conn, IMAP_SEARCH);
803
804   return result;
805 }
806
807 /***********************************************************************
808  *
809  * imap_perform_logout()
810  *
811  * Performs the logout action prior to sclose() being called.
812  */
813 static CURLcode imap_perform_logout(struct connectdata *conn)
814 {
815   CURLcode result = CURLE_OK;
816
817   /* Send the LOGOUT command */
818   result = imap_sendf(conn, "LOGOUT");
819
820   if(!result)
821     state(conn, IMAP_LOGOUT);
822
823   return result;
824 }
825
826 /* For the initial server greeting */
827 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
828                                             int imapcode,
829                                             imapstate instate)
830 {
831   struct Curl_easy *data = conn->data;
832   (void)instate; /* no use for this yet */
833
834   if(imapcode == IMAP_RESP_PREAUTH) {
835     /* PREAUTH */
836     struct imap_conn *imapc = &conn->proto.imapc;
837     imapc->preauth = TRUE;
838     infof(data, "PREAUTH connection, already authenticated!\n");
839   }
840   else if(imapcode != IMAP_RESP_OK) {
841     failf(data, "Got unexpected imap-server response");
842     return CURLE_WEIRD_SERVER_REPLY;
843   }
844
845   return imap_perform_capability(conn);
846 }
847
848 /* For CAPABILITY responses */
849 static CURLcode imap_state_capability_resp(struct connectdata *conn,
850                                            int imapcode,
851                                            imapstate instate)
852 {
853   CURLcode result = CURLE_OK;
854   struct Curl_easy *data = conn->data;
855   struct imap_conn *imapc = &conn->proto.imapc;
856   const char *line = data->state.buffer;
857   size_t wordlen;
858
859   (void)instate; /* no use for this yet */
860
861   /* Do we have a untagged response? */
862   if(imapcode == '*') {
863     line += 2;
864
865     /* Loop through the data line */
866     for(;;) {
867       while(*line &&
868             (*line == ' ' || *line == '\t' ||
869               *line == '\r' || *line == '\n')) {
870
871         line++;
872       }
873
874       if(!*line)
875         break;
876
877       /* Extract the word */
878       for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
879             line[wordlen] != '\t' && line[wordlen] != '\r' &&
880             line[wordlen] != '\n';)
881         wordlen++;
882
883       /* Does the server support the STARTTLS capability? */
884       if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
885         imapc->tls_supported = TRUE;
886
887       /* Has the server explicitly disabled clear text authentication? */
888       else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
889         imapc->login_disabled = TRUE;
890
891       /* Does the server support the SASL-IR capability? */
892       else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
893         imapc->ir_supported = TRUE;
894
895       /* Do we have a SASL based authentication mechanism? */
896       else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
897         size_t llen;
898         unsigned int mechbit;
899
900         line += 5;
901         wordlen -= 5;
902
903         /* Test the word for a matching authentication mechanism */
904         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
905         if(mechbit && llen == wordlen)
906           imapc->sasl.authmechs |= mechbit;
907       }
908
909       line += wordlen;
910     }
911   }
912   else if(imapcode == IMAP_RESP_OK) {
913     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
914       /* We don't have a SSL/TLS connection yet, but SSL is requested */
915       if(imapc->tls_supported)
916         /* Switch to TLS connection now */
917         result = imap_perform_starttls(conn);
918       else if(data->set.use_ssl == CURLUSESSL_TRY)
919         /* Fallback and carry on with authentication */
920         result = imap_perform_authentication(conn);
921       else {
922         failf(data, "STARTTLS not supported.");
923         result = CURLE_USE_SSL_FAILED;
924       }
925     }
926     else
927       result = imap_perform_authentication(conn);
928   }
929   else
930     result = imap_perform_authentication(conn);
931
932   return result;
933 }
934
935 /* For STARTTLS responses */
936 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
937                                          int imapcode,
938                                          imapstate instate)
939 {
940   CURLcode result = CURLE_OK;
941   struct Curl_easy *data = conn->data;
942
943   (void)instate; /* no use for this yet */
944
945   if(imapcode != IMAP_RESP_OK) {
946     if(data->set.use_ssl != CURLUSESSL_TRY) {
947       failf(data, "STARTTLS denied");
948       result = CURLE_USE_SSL_FAILED;
949     }
950     else
951       result = imap_perform_authentication(conn);
952   }
953   else
954     result = imap_perform_upgrade_tls(conn);
955
956   return result;
957 }
958
959 /* For SASL authentication responses */
960 static CURLcode imap_state_auth_resp(struct connectdata *conn,
961                                      int imapcode,
962                                      imapstate instate)
963 {
964   CURLcode result = CURLE_OK;
965   struct Curl_easy *data = conn->data;
966   struct imap_conn *imapc = &conn->proto.imapc;
967   saslprogress progress;
968
969   (void)instate; /* no use for this yet */
970
971   result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
972   if(!result)
973     switch(progress) {
974     case SASL_DONE:
975       state(conn, IMAP_STOP);  /* Authenticated */
976       break;
977     case SASL_IDLE:            /* No mechanism left after cancellation */
978       if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
979         /* Perform clear text authentication */
980         result = imap_perform_login(conn);
981       else {
982         failf(data, "Authentication cancelled");
983         result = CURLE_LOGIN_DENIED;
984       }
985       break;
986     default:
987       break;
988     }
989
990   return result;
991 }
992
993 /* For LOGIN responses */
994 static CURLcode imap_state_login_resp(struct connectdata *conn,
995                                       int imapcode,
996                                       imapstate instate)
997 {
998   CURLcode result = CURLE_OK;
999   struct Curl_easy *data = conn->data;
1000
1001   (void)instate; /* no use for this yet */
1002
1003   if(imapcode != IMAP_RESP_OK) {
1004     failf(data, "Access denied. %c", imapcode);
1005     result = CURLE_LOGIN_DENIED;
1006   }
1007   else
1008     /* End of connect phase */
1009     state(conn, IMAP_STOP);
1010
1011   return result;
1012 }
1013
1014 /* For LIST and SEARCH responses */
1015 static CURLcode imap_state_listsearch_resp(struct connectdata *conn,
1016                                            int imapcode,
1017                                            imapstate instate)
1018 {
1019   CURLcode result = CURLE_OK;
1020   char *line = conn->data->state.buffer;
1021   size_t len = strlen(line);
1022
1023   (void)instate; /* No use for this yet */
1024
1025   if(imapcode == '*') {
1026     /* Temporarily add the LF character back and send as body to the client */
1027     line[len] = '\n';
1028     result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1029     line[len] = '\0';
1030   }
1031   else if(imapcode != IMAP_RESP_OK)
1032     result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1033   else
1034     /* End of DO phase */
1035     state(conn, IMAP_STOP);
1036
1037   return result;
1038 }
1039
1040 /* For SELECT responses */
1041 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1042                                        imapstate instate)
1043 {
1044   CURLcode result = CURLE_OK;
1045   struct Curl_easy *data = conn->data;
1046   struct IMAP *imap = conn->data->req.protop;
1047   struct imap_conn *imapc = &conn->proto.imapc;
1048   const char *line = data->state.buffer;
1049   char tmp[20];
1050
1051   (void)instate; /* no use for this yet */
1052
1053   if(imapcode == '*') {
1054     /* See if this is an UIDVALIDITY response */
1055     if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1056       Curl_safefree(imapc->mailbox_uidvalidity);
1057       imapc->mailbox_uidvalidity = strdup(tmp);
1058     }
1059   }
1060   else if(imapcode == IMAP_RESP_OK) {
1061     /* Check if the UIDVALIDITY has been specified and matches */
1062     if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1063        !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1064       failf(conn->data, "Mailbox UIDVALIDITY has changed");
1065       result = CURLE_REMOTE_FILE_NOT_FOUND;
1066     }
1067     else {
1068       /* Note the currently opened mailbox on this connection */
1069       imapc->mailbox = strdup(imap->mailbox);
1070
1071       if(imap->custom)
1072         result = imap_perform_list(conn);
1073       else if(imap->query)
1074         result = imap_perform_search(conn);
1075       else
1076         result = imap_perform_fetch(conn);
1077     }
1078   }
1079   else {
1080     failf(data, "Select failed");
1081     result = CURLE_LOGIN_DENIED;
1082   }
1083
1084   return result;
1085 }
1086
1087 /* For the (first line of the) FETCH responses */
1088 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1089                                       imapstate instate)
1090 {
1091   CURLcode result = CURLE_OK;
1092   struct Curl_easy *data = conn->data;
1093   struct imap_conn *imapc = &conn->proto.imapc;
1094   struct pingpong *pp = &imapc->pp;
1095   const char *ptr = data->state.buffer;
1096   bool parsed = FALSE;
1097   curl_off_t size = 0;
1098
1099   (void)instate; /* no use for this yet */
1100
1101   if(imapcode != '*') {
1102     Curl_pgrsSetDownloadSize(data, -1);
1103     state(conn, IMAP_STOP);
1104     return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1105   }
1106
1107   /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1108      the continuation data contained within the curly brackets */
1109   while(*ptr && (*ptr != '{'))
1110     ptr++;
1111
1112   if(*ptr == '{') {
1113     char *endptr;
1114     if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
1115       if(endptr - ptr > 1 && endptr[0] == '}' &&
1116          endptr[1] == '\r' && endptr[2] == '\0')
1117         parsed = TRUE;
1118     }
1119   }
1120
1121   if(parsed) {
1122     infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1123           size);
1124     Curl_pgrsSetDownloadSize(data, size);
1125
1126     if(pp->cache) {
1127       /* At this point there is a bunch of data in the header "cache" that is
1128          actually body content, send it as body and then skip it. Do note
1129          that there may even be additional "headers" after the body. */
1130       size_t chunk = pp->cache_size;
1131
1132       if(chunk > (size_t)size)
1133         /* The conversion from curl_off_t to size_t is always fine here */
1134         chunk = (size_t)size;
1135
1136       if(!chunk) {
1137         /* no size, we're done with the data */
1138         state(conn, IMAP_STOP);
1139         return CURLE_OK;
1140       }
1141       result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1142       if(result)
1143         return result;
1144
1145       data->req.bytecount += chunk;
1146
1147       infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1148             " bytes, %" CURL_FORMAT_CURL_OFF_TU
1149             " bytes are left for transfer\n", (curl_off_t)chunk,
1150             size - chunk);
1151
1152       /* Have we used the entire cache or just part of it?*/
1153       if(pp->cache_size > chunk) {
1154         /* Only part of it so shrink the cache to fit the trailing data */
1155         memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1156         pp->cache_size -= chunk;
1157       }
1158       else {
1159         /* Free the cache */
1160         Curl_safefree(pp->cache);
1161
1162         /* Reset the cache size */
1163         pp->cache_size = 0;
1164       }
1165     }
1166
1167     if(data->req.bytecount == size)
1168       /* The entire data is already transferred! */
1169       Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1170     else {
1171       /* IMAP download */
1172       data->req.maxdownload = size;
1173       Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1174     }
1175   }
1176   else {
1177     /* We don't know how to parse this line */
1178     failf(pp->conn->data, "Failed to parse FETCH response.");
1179     result = CURLE_WEIRD_SERVER_REPLY;
1180   }
1181
1182   /* End of DO phase */
1183   state(conn, IMAP_STOP);
1184
1185   return result;
1186 }
1187
1188 /* For final FETCH responses performed after the download */
1189 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1190                                             int imapcode,
1191                                             imapstate instate)
1192 {
1193   CURLcode result = CURLE_OK;
1194
1195   (void)instate; /* No use for this yet */
1196
1197   if(imapcode != IMAP_RESP_OK)
1198     result = CURLE_WEIRD_SERVER_REPLY;
1199   else
1200     /* End of DONE phase */
1201     state(conn, IMAP_STOP);
1202
1203   return result;
1204 }
1205
1206 /* For APPEND responses */
1207 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1208                                        imapstate instate)
1209 {
1210   CURLcode result = CURLE_OK;
1211   struct Curl_easy *data = conn->data;
1212
1213   (void)instate; /* No use for this yet */
1214
1215   if(imapcode != '+') {
1216     result = CURLE_UPLOAD_FAILED;
1217   }
1218   else {
1219     /* Set the progress upload size */
1220     Curl_pgrsSetUploadSize(data, data->state.infilesize);
1221
1222     /* IMAP upload */
1223     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1224
1225     /* End of DO phase */
1226     state(conn, IMAP_STOP);
1227   }
1228
1229   return result;
1230 }
1231
1232 /* For final APPEND responses performed after the upload */
1233 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1234                                              int imapcode,
1235                                              imapstate instate)
1236 {
1237   CURLcode result = CURLE_OK;
1238
1239   (void)instate; /* No use for this yet */
1240
1241   if(imapcode != IMAP_RESP_OK)
1242     result = CURLE_UPLOAD_FAILED;
1243   else
1244     /* End of DONE phase */
1245     state(conn, IMAP_STOP);
1246
1247   return result;
1248 }
1249
1250 static CURLcode imap_statemach_act(struct connectdata *conn)
1251 {
1252   CURLcode result = CURLE_OK;
1253   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1254   int imapcode;
1255   struct imap_conn *imapc = &conn->proto.imapc;
1256   struct pingpong *pp = &imapc->pp;
1257   size_t nread = 0;
1258
1259   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1260   if(imapc->state == IMAP_UPGRADETLS)
1261     return imap_perform_upgrade_tls(conn);
1262
1263   /* Flush any data that needs to be sent */
1264   if(pp->sendleft)
1265     return Curl_pp_flushsend(pp);
1266
1267   do {
1268     /* Read the response from the server */
1269     result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1270     if(result)
1271       return result;
1272
1273     /* Was there an error parsing the response line? */
1274     if(imapcode == -1)
1275       return CURLE_WEIRD_SERVER_REPLY;
1276
1277     if(!imapcode)
1278       break;
1279
1280     /* We have now received a full IMAP server response */
1281     switch(imapc->state) {
1282     case IMAP_SERVERGREET:
1283       result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1284       break;
1285
1286     case IMAP_CAPABILITY:
1287       result = imap_state_capability_resp(conn, imapcode, imapc->state);
1288       break;
1289
1290     case IMAP_STARTTLS:
1291       result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1292       break;
1293
1294     case IMAP_AUTHENTICATE:
1295       result = imap_state_auth_resp(conn, imapcode, imapc->state);
1296       break;
1297
1298     case IMAP_LOGIN:
1299       result = imap_state_login_resp(conn, imapcode, imapc->state);
1300       break;
1301
1302     case IMAP_LIST:
1303       result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
1304       break;
1305
1306     case IMAP_SELECT:
1307       result = imap_state_select_resp(conn, imapcode, imapc->state);
1308       break;
1309
1310     case IMAP_FETCH:
1311       result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1312       break;
1313
1314     case IMAP_FETCH_FINAL:
1315       result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1316       break;
1317
1318     case IMAP_APPEND:
1319       result = imap_state_append_resp(conn, imapcode, imapc->state);
1320       break;
1321
1322     case IMAP_APPEND_FINAL:
1323       result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1324       break;
1325
1326     case IMAP_SEARCH:
1327       result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
1328       break;
1329
1330     case IMAP_LOGOUT:
1331       /* fallthrough, just stop! */
1332     default:
1333       /* internal error */
1334       state(conn, IMAP_STOP);
1335       break;
1336     }
1337   } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1338
1339   return result;
1340 }
1341
1342 /* Called repeatedly until done from multi.c */
1343 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1344 {
1345   CURLcode result = CURLE_OK;
1346   struct imap_conn *imapc = &conn->proto.imapc;
1347
1348   if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1349     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1350     if(result || !imapc->ssldone)
1351       return result;
1352   }
1353
1354   result = Curl_pp_statemach(&imapc->pp, FALSE);
1355   *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1356
1357   return result;
1358 }
1359
1360 static CURLcode imap_block_statemach(struct connectdata *conn)
1361 {
1362   CURLcode result = CURLE_OK;
1363   struct imap_conn *imapc = &conn->proto.imapc;
1364
1365   while(imapc->state != IMAP_STOP && !result)
1366     result = Curl_pp_statemach(&imapc->pp, TRUE);
1367
1368   return result;
1369 }
1370
1371 /* Allocate and initialize the struct IMAP for the current Curl_easy if
1372    required */
1373 static CURLcode imap_init(struct connectdata *conn)
1374 {
1375   CURLcode result = CURLE_OK;
1376   struct Curl_easy *data = conn->data;
1377   struct IMAP *imap;
1378
1379   imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1380   if(!imap)
1381     result = CURLE_OUT_OF_MEMORY;
1382
1383   return result;
1384 }
1385
1386 /* For the IMAP "protocol connect" and "doing" phases only */
1387 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1388                         int numsocks)
1389 {
1390   return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1391 }
1392
1393 /***********************************************************************
1394  *
1395  * imap_connect()
1396  *
1397  * This function should do everything that is to be considered a part of the
1398  * connection phase.
1399  *
1400  * The variable 'done' points to will be TRUE if the protocol-layer connect
1401  * phase is done when this function returns, or FALSE if not.
1402  */
1403 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1404 {
1405   CURLcode result = CURLE_OK;
1406   struct imap_conn *imapc = &conn->proto.imapc;
1407   struct pingpong *pp = &imapc->pp;
1408
1409   *done = FALSE; /* default to not done yet */
1410
1411   /* We always support persistent connections in IMAP */
1412   connkeep(conn, "IMAP default");
1413
1414   /* Set the default response time-out */
1415   pp->response_time = RESP_TIMEOUT;
1416   pp->statemach_act = imap_statemach_act;
1417   pp->endofresp = imap_endofresp;
1418   pp->conn = conn;
1419
1420   /* Set the default preferred authentication type and mechanism */
1421   imapc->preftype = IMAP_TYPE_ANY;
1422   Curl_sasl_init(&imapc->sasl, &saslimap);
1423
1424   /* Initialise the pingpong layer */
1425   Curl_pp_init(pp);
1426
1427   /* Parse the URL options */
1428   result = imap_parse_url_options(conn);
1429   if(result)
1430     return result;
1431
1432   /* Start off waiting for the server greeting response */
1433   state(conn, IMAP_SERVERGREET);
1434
1435   /* Start off with an response id of '*' */
1436   strcpy(imapc->resptag, "*");
1437
1438   result = imap_multi_statemach(conn, done);
1439
1440   return result;
1441 }
1442
1443 /***********************************************************************
1444  *
1445  * imap_done()
1446  *
1447  * The DONE function. This does what needs to be done after a single DO has
1448  * performed.
1449  *
1450  * Input argument is already checked for validity.
1451  */
1452 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1453                           bool premature)
1454 {
1455   CURLcode result = CURLE_OK;
1456   struct Curl_easy *data = conn->data;
1457   struct IMAP *imap = data->req.protop;
1458
1459   (void)premature;
1460
1461   if(!imap)
1462     return CURLE_OK;
1463
1464   if(status) {
1465     connclose(conn, "IMAP done with bad status"); /* marked for closure */
1466     result = status;         /* use the already set error code */
1467   }
1468   else if(!data->set.connect_only && !imap->custom &&
1469           (imap->uid || data->set.upload ||
1470           data->set.mimepost.kind != MIMEKIND_NONE)) {
1471     /* Handle responses after FETCH or APPEND transfer has finished */
1472     if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
1473       state(conn, IMAP_FETCH_FINAL);
1474     else {
1475       /* End the APPEND command first by sending an empty line */
1476       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1477       if(!result)
1478         state(conn, IMAP_APPEND_FINAL);
1479     }
1480
1481     /* Run the state-machine
1482
1483        TODO: when the multi interface is used, this _really_ should be using
1484        the imap_multi_statemach function but we have no general support for
1485        non-blocking DONE operations!
1486     */
1487     if(!result)
1488       result = imap_block_statemach(conn);
1489   }
1490
1491   /* Cleanup our per-request based variables */
1492   Curl_safefree(imap->mailbox);
1493   Curl_safefree(imap->uidvalidity);
1494   Curl_safefree(imap->uid);
1495   Curl_safefree(imap->section);
1496   Curl_safefree(imap->partial);
1497   Curl_safefree(imap->query);
1498   Curl_safefree(imap->custom);
1499   Curl_safefree(imap->custom_params);
1500
1501   /* Clear the transfer mode for the next request */
1502   imap->transfer = FTPTRANSFER_BODY;
1503
1504   return result;
1505 }
1506
1507 /***********************************************************************
1508  *
1509  * imap_perform()
1510  *
1511  * This is the actual DO function for IMAP. Fetch or append a message, or do
1512  * other things according to the options previously setup.
1513  */
1514 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1515                              bool *dophase_done)
1516 {
1517   /* This is IMAP and no proxy */
1518   CURLcode result = CURLE_OK;
1519   struct Curl_easy *data = conn->data;
1520   struct IMAP *imap = data->req.protop;
1521   struct imap_conn *imapc = &conn->proto.imapc;
1522   bool selected = FALSE;
1523
1524   DEBUGF(infof(conn->data, "DO phase starts\n"));
1525
1526   if(conn->data->set.opt_no_body) {
1527     /* Requested no body means no transfer */
1528     imap->transfer = FTPTRANSFER_INFO;
1529   }
1530
1531   *dophase_done = FALSE; /* not done yet */
1532
1533   /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1534      has already been selected on this connection */
1535   if(imap->mailbox && imapc->mailbox &&
1536      strcasecompare(imap->mailbox, imapc->mailbox) &&
1537      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1538       strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1539     selected = TRUE;
1540
1541   /* Start the first command in the DO phase */
1542   if(conn->data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
1543     /* APPEND can be executed directly */
1544     result = imap_perform_append(conn);
1545   else if(imap->custom && (selected || !imap->mailbox))
1546     /* Custom command using the same mailbox or no mailbox */
1547     result = imap_perform_list(conn);
1548   else if(!imap->custom && selected && imap->uid)
1549     /* FETCH from the same mailbox */
1550     result = imap_perform_fetch(conn);
1551   else if(!imap->custom && selected && imap->query)
1552     /* SEARCH the current mailbox */
1553     result = imap_perform_search(conn);
1554   else if(imap->mailbox && !selected &&
1555          (imap->custom || imap->uid || imap->query))
1556     /* SELECT the mailbox */
1557     result = imap_perform_select(conn);
1558   else
1559     /* LIST */
1560     result = imap_perform_list(conn);
1561
1562   if(result)
1563     return result;
1564
1565   /* Run the state-machine */
1566   result = imap_multi_statemach(conn, dophase_done);
1567
1568   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1569
1570   if(*dophase_done)
1571     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1572
1573   return result;
1574 }
1575
1576 /***********************************************************************
1577  *
1578  * imap_do()
1579  *
1580  * This function is registered as 'curl_do' function. It decodes the path
1581  * parts etc as a wrapper to the actual DO function (imap_perform).
1582  *
1583  * The input argument is already checked for validity.
1584  */
1585 static CURLcode imap_do(struct connectdata *conn, bool *done)
1586 {
1587   CURLcode result = CURLE_OK;
1588
1589   *done = FALSE; /* default to false */
1590
1591   /* Parse the URL path */
1592   result = imap_parse_url_path(conn);
1593   if(result)
1594     return result;
1595
1596   /* Parse the custom request */
1597   result = imap_parse_custom_request(conn);
1598   if(result)
1599     return result;
1600
1601   result = imap_regular_transfer(conn, done);
1602
1603   return result;
1604 }
1605
1606 /***********************************************************************
1607  *
1608  * imap_disconnect()
1609  *
1610  * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1611  * resources. BLOCKING.
1612  */
1613 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1614 {
1615   struct imap_conn *imapc = &conn->proto.imapc;
1616
1617   /* We cannot send quit unconditionally. If this connection is stale or
1618      bad in any way, sending quit and waiting around here will make the
1619      disconnect wait in vain and cause more problems than we need to. */
1620
1621   /* The IMAP session may or may not have been allocated/setup at this
1622      point! */
1623   if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
1624     if(!imap_perform_logout(conn))
1625       (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1626
1627   /* Disconnect from the server */
1628   Curl_pp_disconnect(&imapc->pp);
1629
1630   /* Cleanup the SASL module */
1631   Curl_sasl_cleanup(conn, imapc->sasl.authused);
1632
1633   /* Cleanup our connection based variables */
1634   Curl_safefree(imapc->mailbox);
1635   Curl_safefree(imapc->mailbox_uidvalidity);
1636
1637   return CURLE_OK;
1638 }
1639
1640 /* Call this when the DO phase has completed */
1641 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1642 {
1643   struct IMAP *imap = conn->data->req.protop;
1644
1645   (void)connected;
1646
1647   if(imap->transfer != FTPTRANSFER_BODY)
1648     /* no data to transfer */
1649     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1650
1651   return CURLE_OK;
1652 }
1653
1654 /* Called from multi.c while DOing */
1655 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1656 {
1657   CURLcode result = imap_multi_statemach(conn, dophase_done);
1658
1659   if(result)
1660     DEBUGF(infof(conn->data, "DO phase failed\n"));
1661   else if(*dophase_done) {
1662     result = imap_dophase_done(conn, FALSE /* not connected */);
1663
1664     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1665   }
1666
1667   return result;
1668 }
1669
1670 /***********************************************************************
1671  *
1672  * imap_regular_transfer()
1673  *
1674  * The input argument is already checked for validity.
1675  *
1676  * Performs all commands done before a regular transfer between a local and a
1677  * remote host.
1678  */
1679 static CURLcode imap_regular_transfer(struct connectdata *conn,
1680                                       bool *dophase_done)
1681 {
1682   CURLcode result = CURLE_OK;
1683   bool connected = FALSE;
1684   struct Curl_easy *data = conn->data;
1685
1686   /* Make sure size is unknown at this point */
1687   data->req.size = -1;
1688
1689   /* Set the progress data */
1690   Curl_pgrsSetUploadCounter(data, 0);
1691   Curl_pgrsSetDownloadCounter(data, 0);
1692   Curl_pgrsSetUploadSize(data, -1);
1693   Curl_pgrsSetDownloadSize(data, -1);
1694
1695   /* Carry out the perform */
1696   result = imap_perform(conn, &connected, dophase_done);
1697
1698   /* Perform post DO phase operations if necessary */
1699   if(!result && *dophase_done)
1700     result = imap_dophase_done(conn, connected);
1701
1702   return result;
1703 }
1704
1705 static CURLcode imap_setup_connection(struct connectdata *conn)
1706 {
1707   struct Curl_easy *data = conn->data;
1708
1709   /* Initialise the IMAP layer */
1710   CURLcode result = imap_init(conn);
1711   if(result)
1712     return result;
1713
1714   /* Clear the TLS upgraded flag */
1715   conn->tls_upgraded = FALSE;
1716   data->state.path++;   /* don't include the initial slash */
1717
1718   return CURLE_OK;
1719 }
1720
1721 /***********************************************************************
1722  *
1723  * imap_sendf()
1724  *
1725  * Sends the formatted string as an IMAP command to the server.
1726  *
1727  * Designed to never block.
1728  */
1729 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
1730 {
1731   CURLcode result = CURLE_OK;
1732   struct imap_conn *imapc = &conn->proto.imapc;
1733   char *taggedfmt;
1734   va_list ap;
1735
1736   DEBUGASSERT(fmt);
1737
1738   /* Calculate the next command ID wrapping at 3 digits */
1739   imapc->cmdid = (imapc->cmdid + 1) % 1000;
1740
1741   /* Calculate the tag based on the connection ID and command ID */
1742   snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1743            'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
1744
1745   /* Prefix the format with the tag */
1746   taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
1747   if(!taggedfmt)
1748     return CURLE_OUT_OF_MEMORY;
1749
1750   /* Send the data with the tag */
1751   va_start(ap, fmt);
1752   result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
1753   va_end(ap);
1754
1755   free(taggedfmt);
1756
1757   return result;
1758 }
1759
1760 /***********************************************************************
1761  *
1762  * imap_atom()
1763  *
1764  * Checks the input string for characters that need escaping and returns an
1765  * atom ready for sending to the server.
1766  *
1767  * The returned string needs to be freed.
1768  *
1769  */
1770 static char *imap_atom(const char *str, bool escape_only)
1771 {
1772   /* !checksrc! disable PARENBRACE 1 */
1773   const char atom_specials[] = "(){ %*]";
1774   const char *p1;
1775   char *p2;
1776   size_t backsp_count = 0;
1777   size_t quote_count = 0;
1778   bool others_exists = FALSE;
1779   size_t newlen = 0;
1780   char *newstr = NULL;
1781
1782   if(!str)
1783     return NULL;
1784
1785   /* Look for "atom-specials", counting the backslash and quote characters as
1786      these will need escapping */
1787   p1 = str;
1788   while(*p1) {
1789     if(*p1 == '\\')
1790       backsp_count++;
1791     else if(*p1 == '"')
1792       quote_count++;
1793     else if(!escape_only) {
1794       const char *p3 = atom_specials;
1795
1796       while(*p3 && !others_exists) {
1797         if(*p1 == *p3)
1798           others_exists = TRUE;
1799
1800         p3++;
1801       }
1802     }
1803
1804     p1++;
1805   }
1806
1807   /* Does the input contain any "atom-special" characters? */
1808   if(!backsp_count && !quote_count && !others_exists)
1809     return strdup(str);
1810
1811   /* Calculate the new string length */
1812   newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2);
1813
1814   /* Allocate the new string */
1815   newstr = (char *) malloc((newlen + 1) * sizeof(char));
1816   if(!newstr)
1817     return NULL;
1818
1819   /* Surround the string in quotes if necessary */
1820   p2 = newstr;
1821   if(!escape_only) {
1822     newstr[0] = '"';
1823     newstr[newlen - 1] = '"';
1824     p2++;
1825   }
1826
1827   /* Copy the string, escaping backslash and quote characters along the way */
1828   p1 = str;
1829   while(*p1) {
1830     if(*p1 == '\\' || *p1 == '"') {
1831       *p2 = '\\';
1832       p2++;
1833     }
1834
1835    *p2 = *p1;
1836
1837     p1++;
1838     p2++;
1839   }
1840
1841   /* Terminate the string */
1842   newstr[newlen] = '\0';
1843
1844   return newstr;
1845 }
1846
1847 /***********************************************************************
1848  *
1849  * imap_is_bchar()
1850  *
1851  * Portable test of whether the specified char is a "bchar" as defined in the
1852  * grammar of RFC-5092.
1853  */
1854 static bool imap_is_bchar(char ch)
1855 {
1856   switch(ch) {
1857     /* bchar */
1858     case ':': case '@': case '/':
1859     /* bchar -> achar */
1860     case '&': case '=':
1861     /* bchar -> achar -> uchar -> unreserved */
1862     case '0': case '1': case '2': case '3': case '4': case '5': case '6':
1863     case '7': case '8': case '9':
1864     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
1865     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
1866     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
1867     case 'V': case 'W': case 'X': case 'Y': case 'Z':
1868     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
1869     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
1870     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
1871     case 'v': case 'w': case 'x': case 'y': case 'z':
1872     case '-': case '.': case '_': case '~':
1873     /* bchar -> achar -> uchar -> sub-delims-sh */
1874     case '!': case '$': case '\'': case '(': case ')': case '*':
1875     case '+': case ',':
1876     /* bchar -> achar -> uchar -> pct-encoded */
1877     case '%': /* HEXDIG chars are already included above */
1878       return true;
1879
1880     default:
1881       return false;
1882   }
1883 }
1884
1885 /***********************************************************************
1886  *
1887  * imap_parse_url_options()
1888  *
1889  * Parse the URL login options.
1890  */
1891 static CURLcode imap_parse_url_options(struct connectdata *conn)
1892 {
1893   CURLcode result = CURLE_OK;
1894   struct imap_conn *imapc = &conn->proto.imapc;
1895   const char *ptr = conn->options;
1896
1897   imapc->sasl.resetprefs = TRUE;
1898
1899   while(!result && ptr && *ptr) {
1900     const char *key = ptr;
1901     const char *value;
1902
1903     while(*ptr && *ptr != '=')
1904         ptr++;
1905
1906     value = ptr + 1;
1907
1908     while(*ptr && *ptr != ';')
1909       ptr++;
1910
1911     if(strncasecompare(key, "AUTH=", 5))
1912       result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
1913                                                value, ptr - value);
1914     else
1915       result = CURLE_URL_MALFORMAT;
1916
1917     if(*ptr == ';')
1918       ptr++;
1919   }
1920
1921   switch(imapc->sasl.prefmech) {
1922   case SASL_AUTH_NONE:
1923     imapc->preftype = IMAP_TYPE_NONE;
1924     break;
1925   case SASL_AUTH_DEFAULT:
1926     imapc->preftype = IMAP_TYPE_ANY;
1927     break;
1928   default:
1929     imapc->preftype = IMAP_TYPE_SASL;
1930     break;
1931   }
1932
1933   return result;
1934 }
1935
1936 /***********************************************************************
1937  *
1938  * imap_parse_url_path()
1939  *
1940  * Parse the URL path into separate path components.
1941  *
1942  */
1943 static CURLcode imap_parse_url_path(struct connectdata *conn)
1944 {
1945   /* The imap struct is already initialised in imap_connect() */
1946   CURLcode result = CURLE_OK;
1947   struct Curl_easy *data = conn->data;
1948   struct IMAP *imap = data->req.protop;
1949   const char *begin = data->state.path;
1950   const char *ptr = begin;
1951
1952   /* See how much of the URL is a valid path and decode it */
1953   while(imap_is_bchar(*ptr))
1954     ptr++;
1955
1956   if(ptr != begin) {
1957     /* Remove the trailing slash if present */
1958     const char *end = ptr;
1959     if(end > begin && end[-1] == '/')
1960       end--;
1961
1962     result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
1963                             TRUE);
1964     if(result)
1965       return result;
1966   }
1967   else
1968     imap->mailbox = NULL;
1969
1970   /* There can be any number of parameters in the form ";NAME=VALUE" */
1971   while(*ptr == ';') {
1972     char *name;
1973     char *value;
1974     size_t valuelen;
1975
1976     /* Find the length of the name parameter */
1977     begin = ++ptr;
1978     while(*ptr && *ptr != '=')
1979       ptr++;
1980
1981     if(!*ptr)
1982       return CURLE_URL_MALFORMAT;
1983
1984     /* Decode the name parameter */
1985     result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
1986     if(result)
1987       return result;
1988
1989     /* Find the length of the value parameter */
1990     begin = ++ptr;
1991     while(imap_is_bchar(*ptr))
1992       ptr++;
1993
1994     /* Decode the value parameter */
1995     result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
1996     if(result) {
1997       free(name);
1998       return result;
1999     }
2000
2001     DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2002
2003     /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2004        PARTIAL) stripping of the trailing slash character if it is present.
2005
2006        Note: Unknown parameters trigger a URL_MALFORMAT error. */
2007     if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
2008       if(valuelen > 0 && value[valuelen - 1] == '/')
2009         value[valuelen - 1] = '\0';
2010
2011       imap->uidvalidity = value;
2012       value = NULL;
2013     }
2014     else if(strcasecompare(name, "UID") && !imap->uid) {
2015       if(valuelen > 0 && value[valuelen - 1] == '/')
2016         value[valuelen - 1] = '\0';
2017
2018       imap->uid = value;
2019       value = NULL;
2020     }
2021     else if(strcasecompare(name, "SECTION") && !imap->section) {
2022       if(valuelen > 0 && value[valuelen - 1] == '/')
2023         value[valuelen - 1] = '\0';
2024
2025       imap->section = value;
2026       value = NULL;
2027     }
2028     else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
2029       if(valuelen > 0 && value[valuelen - 1] == '/')
2030         value[valuelen - 1] = '\0';
2031
2032       imap->partial = value;
2033       value = NULL;
2034     }
2035     else {
2036       free(name);
2037       free(value);
2038
2039       return CURLE_URL_MALFORMAT;
2040     }
2041
2042     free(name);
2043     free(value);
2044   }
2045
2046   /* Does the URL contain a query parameter? Only valid when we have a mailbox
2047      and no UID as per RFC-5092 */
2048   if(imap->mailbox && !imap->uid && *ptr == '?') {
2049     /* Find the length of the query parameter */
2050     begin = ++ptr;
2051     while(imap_is_bchar(*ptr))
2052       ptr++;
2053
2054     /* Decode the query parameter */
2055     result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2056                             TRUE);
2057     if(result)
2058       return result;
2059   }
2060
2061   /* Any extra stuff at the end of the URL is an error */
2062   if(*ptr)
2063     return CURLE_URL_MALFORMAT;
2064
2065   return CURLE_OK;
2066 }
2067
2068 /***********************************************************************
2069  *
2070  * imap_parse_custom_request()
2071  *
2072  * Parse the custom request.
2073  */
2074 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2075 {
2076   CURLcode result = CURLE_OK;
2077   struct Curl_easy *data = conn->data;
2078   struct IMAP *imap = data->req.protop;
2079   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2080
2081   if(custom) {
2082     /* URL decode the custom request */
2083     result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2084
2085     /* Extract the parameters if specified */
2086     if(!result) {
2087       const char *params = imap->custom;
2088
2089       while(*params && *params != ' ')
2090         params++;
2091
2092       if(*params) {
2093         imap->custom_params = strdup(params);
2094         imap->custom[params - imap->custom] = '\0';
2095
2096         if(!imap->custom_params)
2097           result = CURLE_OUT_OF_MEMORY;
2098       }
2099     }
2100   }
2101
2102   return result;
2103 }
2104
2105 #endif /* CURL_DISABLE_IMAP */