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