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