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