Revert "Update to 7.40.1"
[platform/upstream/curl.git] / lib / imap.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * RFC2195 CRAM-MD5 authentication
22  * RFC2595 Using TLS with IMAP, POP3 and ACAP
23  * RFC2831 DIGEST-MD5 authentication
24  * RFC3501 IMAPv4 protocol
25  * RFC4422 Simple Authentication and Security Layer (SASL)
26  * RFC4616 PLAIN authentication
27  * RFC4959 IMAP Extension for SASL Initial Client Response
28  * RFC5092 IMAP URL Scheme
29  * RFC6749 OAuth 2.0 Authorization Framework
30  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
31  *
32  ***************************************************************************/
33
34 #include "curl_setup.h"
35
36 #ifndef CURL_DISABLE_IMAP
37
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 #include <arpa/inet.h>
43 #endif
44 #ifdef HAVE_UTSNAME_H
45 #include <sys/utsname.h>
46 #endif
47 #ifdef HAVE_NETDB_H
48 #include <netdb.h>
49 #endif
50 #ifdef __VMS
51 #include <in.h>
52 #include <inet.h>
53 #endif
54
55 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
56 #undef in_addr_t
57 #define in_addr_t unsigned long
58 #endif
59
60 #include <curl/curl.h>
61 #include "urldata.h"
62 #include "sendf.h"
63 #include "if2ip.h"
64 #include "hostip.h"
65 #include "progress.h"
66 #include "transfer.h"
67 #include "escape.h"
68 #include "http.h" /* for HTTP proxy tunnel stuff */
69 #include "socks.h"
70 #include "imap.h"
71
72 #include "strtoofft.h"
73 #include "strequal.h"
74 #include "vtls/vtls.h"
75 #include "connect.h"
76 #include "strerror.h"
77 #include "select.h"
78 #include "multiif.h"
79 #include "url.h"
80 #include "rawstr.h"
81 #include "curl_sasl.h"
82 #include "warnless.h"
83
84 #define _MPRINTF_REPLACE /* use our functions only */
85 #include <curl/mprintf.h>
86
87 #include "curl_memory.h"
88 /* The last #include file should be: */
89 #include "memdebug.h"
90
91 /* Local API functions */
92 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
93 static CURLcode imap_do(struct connectdata *conn, bool *done);
94 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
95                           bool premature);
96 static CURLcode imap_connect(struct connectdata *conn, bool *done);
97 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
98 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
99 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
100                         int numsocks);
101 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
102 static CURLcode imap_setup_connection(struct connectdata *conn);
103 static char *imap_atom(const char *str);
104 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
105 static CURLcode imap_parse_url_options(struct connectdata *conn);
106 static CURLcode imap_parse_url_path(struct connectdata *conn);
107 static CURLcode imap_parse_custom_request(struct connectdata *conn);
108 static CURLcode imap_calc_sasl_details(struct connectdata *conn,
109                                        const char **mech,
110                                        char **initresp, size_t *len,
111                                        imapstate *state1, imapstate *state2);
112
113 /*
114  * IMAP protocol handler.
115  */
116
117 const struct Curl_handler Curl_handler_imap = {
118   "IMAP",                           /* scheme */
119   imap_setup_connection,            /* setup_connection */
120   imap_do,                          /* do_it */
121   imap_done,                        /* done */
122   ZERO_NULL,                        /* do_more */
123   imap_connect,                     /* connect_it */
124   imap_multi_statemach,             /* connecting */
125   imap_doing,                       /* doing */
126   imap_getsock,                     /* proto_getsock */
127   imap_getsock,                     /* doing_getsock */
128   ZERO_NULL,                        /* domore_getsock */
129   ZERO_NULL,                        /* perform_getsock */
130   imap_disconnect,                  /* disconnect */
131   ZERO_NULL,                        /* readwrite */
132   PORT_IMAP,                        /* defport */
133   CURLPROTO_IMAP,                   /* protocol */
134   PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD  /* flags */
135 };
136
137 #ifdef USE_SSL
138 /*
139  * IMAPS protocol handler.
140  */
141
142 const struct Curl_handler Curl_handler_imaps = {
143   "IMAPS",                          /* scheme */
144   imap_setup_connection,            /* setup_connection */
145   imap_do,                          /* do_it */
146   imap_done,                        /* done */
147   ZERO_NULL,                        /* do_more */
148   imap_connect,                     /* connect_it */
149   imap_multi_statemach,             /* connecting */
150   imap_doing,                       /* doing */
151   imap_getsock,                     /* proto_getsock */
152   imap_getsock,                     /* doing_getsock */
153   ZERO_NULL,                        /* domore_getsock */
154   ZERO_NULL,                        /* perform_getsock */
155   imap_disconnect,                  /* disconnect */
156   ZERO_NULL,                        /* readwrite */
157   PORT_IMAPS,                       /* defport */
158   CURLPROTO_IMAPS,                  /* protocol */
159   PROTOPT_CLOSEACTION | PROTOPT_SSL |
160   PROTOPT_NEEDSPWD                  /* flags */
161 };
162 #endif
163
164 #ifndef CURL_DISABLE_HTTP
165 /*
166  * HTTP-proxyed IMAP protocol handler.
167  */
168
169 static const struct Curl_handler Curl_handler_imap_proxy = {
170   "IMAP",                               /* scheme */
171   Curl_http_setup_conn,                 /* setup_connection */
172   Curl_http,                            /* do_it */
173   Curl_http_done,                       /* done */
174   ZERO_NULL,                            /* do_more */
175   ZERO_NULL,                            /* connect_it */
176   ZERO_NULL,                            /* connecting */
177   ZERO_NULL,                            /* doing */
178   ZERO_NULL,                            /* proto_getsock */
179   ZERO_NULL,                            /* doing_getsock */
180   ZERO_NULL,                            /* domore_getsock */
181   ZERO_NULL,                            /* perform_getsock */
182   ZERO_NULL,                            /* disconnect */
183   ZERO_NULL,                            /* readwrite */
184   PORT_IMAP,                            /* defport */
185   CURLPROTO_HTTP,                       /* protocol */
186   PROTOPT_NONE                          /* flags */
187 };
188
189 #ifdef USE_SSL
190 /*
191  * HTTP-proxyed IMAPS protocol handler.
192  */
193
194 static const struct Curl_handler Curl_handler_imaps_proxy = {
195   "IMAPS",                              /* scheme */
196   Curl_http_setup_conn,                 /* setup_connection */
197   Curl_http,                            /* do_it */
198   Curl_http_done,                       /* done */
199   ZERO_NULL,                            /* do_more */
200   ZERO_NULL,                            /* connect_it */
201   ZERO_NULL,                            /* connecting */
202   ZERO_NULL,                            /* doing */
203   ZERO_NULL,                            /* proto_getsock */
204   ZERO_NULL,                            /* doing_getsock */
205   ZERO_NULL,                            /* domore_getsock */
206   ZERO_NULL,                            /* perform_getsock */
207   ZERO_NULL,                            /* disconnect */
208   ZERO_NULL,                            /* readwrite */
209   PORT_IMAPS,                           /* defport */
210   CURLPROTO_HTTP,                       /* protocol */
211   PROTOPT_NONE                          /* flags */
212 };
213 #endif
214 #endif
215
216 #ifdef USE_SSL
217 static void imap_to_imaps(struct connectdata *conn)
218 {
219   conn->handler = &Curl_handler_imaps;
220 }
221 #else
222 #define imap_to_imaps(x) Curl_nop_stmt
223 #endif
224
225 /***********************************************************************
226  *
227  * imap_matchresp()
228  *
229  * Determines whether the untagged response is related to the specified
230  * command by checking if it is in format "* <command-name> ..." or
231  * "* <number> <command-name> ...".
232  *
233  * The "* " marker is assumed to have already been checked by the caller.
234  */
235 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
236 {
237   const char *end = line + len;
238   size_t cmd_len = strlen(cmd);
239
240   /* Skip the untagged response marker */
241   line += 2;
242
243   /* Do we have a number after the marker? */
244   if(line < end && ISDIGIT(*line)) {
245     /* Skip the number */
246     do
247       line++;
248     while(line < end && ISDIGIT(*line));
249
250     /* Do we have the space character? */
251     if(line == end || *line != ' ')
252       return FALSE;
253
254     line++;
255   }
256
257   /* Does the command name match and is it followed by a space character or at
258      the end of line? */
259   if(line + cmd_len <= end && Curl_raw_nequal(line, cmd, cmd_len) &&
260      (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
261     return TRUE;
262
263   return FALSE;
264 }
265
266 /***********************************************************************
267  *
268  * imap_endofresp()
269  *
270  * Checks whether the given string is a valid tagged, untagged or continuation
271  * response which can be processed by the response handler.
272  */
273 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
274                            int *resp)
275 {
276   struct IMAP *imap = conn->data->req.protop;
277   struct imap_conn *imapc = &conn->proto.imapc;
278   const char *id = imapc->resptag;
279   size_t id_len = strlen(id);
280
281   /* Do we have a tagged command response? */
282   if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
283     line += id_len + 1;
284     len -= id_len + 1;
285
286     if(len >= 2 && !memcmp(line, "OK", 2))
287       *resp = 'O';
288     else if(len >= 2 && !memcmp(line, "NO", 2))
289       *resp = 'N';
290     else if(len >= 3 && !memcmp(line, "BAD", 3))
291       *resp = 'B';
292     else {
293       failf(conn->data, "Bad tagged response");
294       *resp = -1;
295     }
296
297     return TRUE;
298   }
299
300   /* Do we have an untagged command response? */
301   if(len >= 2 && !memcmp("* ", line, 2)) {
302     switch(imapc->state) {
303       /* States which are interested in untagged responses */
304       case IMAP_CAPABILITY:
305         if(!imap_matchresp(line, len, "CAPABILITY"))
306           return FALSE;
307         break;
308
309       case IMAP_LIST:
310         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
311           (imap->custom && !imap_matchresp(line, len, imap->custom) &&
312            (strcmp(imap->custom, "STORE") ||
313             !imap_matchresp(line, len, "FETCH")) &&
314            strcmp(imap->custom, "SELECT") &&
315            strcmp(imap->custom, "EXAMINE") &&
316            strcmp(imap->custom, "SEARCH") &&
317            strcmp(imap->custom, "EXPUNGE") &&
318            strcmp(imap->custom, "LSUB") &&
319            strcmp(imap->custom, "UID") &&
320            strcmp(imap->custom, "NOOP")))
321           return FALSE;
322         break;
323
324       case IMAP_SELECT:
325         /* SELECT is special in that its untagged responses do not have a
326            common prefix so accept anything! */
327         break;
328
329       case IMAP_FETCH:
330         if(!imap_matchresp(line, len, "FETCH"))
331           return FALSE;
332         break;
333
334       case IMAP_SEARCH:
335         if(!imap_matchresp(line, len, "SEARCH"))
336           return FALSE;
337         break;
338
339       /* Ignore other untagged responses */
340       default:
341         return FALSE;
342     }
343
344     *resp = '*';
345     return TRUE;
346   }
347
348   /* Do we have a continuation response? This should be a + symbol followed by
349      a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
350      APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
351      some e-mail servers ignore this and only send a single + instead. */
352   if((len == 3 && !memcmp("+", line, 1)) ||
353      (len >= 2 && !memcmp("+ ", line, 2))) {
354     switch(imapc->state) {
355       /* States which are interested in continuation responses */
356       case IMAP_AUTHENTICATE_PLAIN:
357       case IMAP_AUTHENTICATE_LOGIN:
358       case IMAP_AUTHENTICATE_LOGIN_PASSWD:
359       case IMAP_AUTHENTICATE_CRAMMD5:
360       case IMAP_AUTHENTICATE_DIGESTMD5:
361       case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
362       case IMAP_AUTHENTICATE_NTLM:
363       case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
364       case IMAP_AUTHENTICATE_XOAUTH2:
365       case IMAP_AUTHENTICATE_FINAL:
366       case IMAP_APPEND:
367         *resp = '+';
368         break;
369
370       default:
371         failf(conn->data, "Unexpected continuation response");
372         *resp = -1;
373         break;
374     }
375
376     return TRUE;
377   }
378
379   return FALSE; /* Nothing for us */
380 }
381
382 /***********************************************************************
383  *
384  * imap_get_message()
385  *
386  * Gets the authentication message from the response buffer.
387  */
388 static void imap_get_message(char *buffer, char** outptr)
389 {
390   size_t len = 0;
391   char* message = NULL;
392
393   /* Find the start of the message */
394   for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
395     ;
396
397   /* Find the end of the message */
398   for(len = strlen(message); len--;)
399     if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
400         message[len] != '\t')
401       break;
402
403   /* Terminate the message */
404   if(++len) {
405     message[len] = '\0';
406   }
407
408   *outptr = message;
409 }
410
411 /***********************************************************************
412  *
413  * state()
414  *
415  * This is the ONLY way to change IMAP state!
416  */
417 static void state(struct connectdata *conn, imapstate newstate)
418 {
419   struct imap_conn *imapc = &conn->proto.imapc;
420 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
421   /* for debug purposes */
422   static const char * const names[]={
423     "STOP",
424     "SERVERGREET",
425     "CAPABILITY",
426     "STARTTLS",
427     "UPGRADETLS",
428     "AUTHENTICATE_PLAIN",
429     "AUTHENTICATE_LOGIN",
430     "AUTHENTICATE_LOGIN_PASSWD",
431     "AUTHENTICATE_CRAMMD5",
432     "AUTHENTICATE_DIGESTMD5",
433     "AUTHENTICATE_DIGESTMD5_RESP",
434     "AUTHENTICATE_NTLM",
435     "AUTHENTICATE_NTLM_TYPE2MSG",
436     "AUTHENTICATE_XOAUTH2",
437     "AUTHENTICATE_CANCEL",
438     "AUTHENTICATE_FINAL",
439     "LOGIN",
440     "LIST",
441     "SELECT",
442     "FETCH",
443     "FETCH_FINAL",
444     "APPEND",
445     "APPEND_FINAL",
446     "SEARCH",
447     "LOGOUT",
448     /* LAST */
449   };
450
451   if(imapc->state != newstate)
452     infof(conn->data, "IMAP %p state change from %s to %s\n",
453           (void *)imapc, names[imapc->state], names[newstate]);
454 #endif
455
456   imapc->state = newstate;
457 }
458
459 /***********************************************************************
460  *
461  * imap_perform_capability()
462  *
463  * Sends the CAPABILITY command in order to obtain a list of server side
464  * supported capabilities.
465  */
466 static CURLcode imap_perform_capability(struct connectdata *conn)
467 {
468   CURLcode result = CURLE_OK;
469   struct imap_conn *imapc = &conn->proto.imapc;
470
471   imapc->authmechs = 0;         /* No known authentication mechanisms yet */
472   imapc->authused = 0;          /* Clear the authentication mechanism used */
473   imapc->tls_supported = FALSE; /* Clear the TLS capability */
474
475   /* Send the CAPABILITY command */
476   result = imap_sendf(conn, "CAPABILITY");
477
478   if(!result)
479     state(conn, IMAP_CAPABILITY);
480
481   return result;
482 }
483
484 /***********************************************************************
485  *
486  * imap_perform_starttls()
487  *
488  * Sends the STARTTLS command to start the upgrade to TLS.
489  */
490 static CURLcode imap_perform_starttls(struct connectdata *conn)
491 {
492   CURLcode result = CURLE_OK;
493
494   /* Send the STARTTLS command */
495   result = imap_sendf(conn, "STARTTLS");
496
497   if(!result)
498     state(conn, IMAP_STARTTLS);
499
500   return result;
501 }
502
503 /***********************************************************************
504  *
505  * imap_perform_upgrade_tls()
506  *
507  * Performs the upgrade to TLS.
508  */
509 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
510 {
511   CURLcode result = CURLE_OK;
512   struct imap_conn *imapc = &conn->proto.imapc;
513
514   /* Start the SSL connection */
515   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
516
517   if(!result) {
518     if(imapc->state != IMAP_UPGRADETLS)
519       state(conn, IMAP_UPGRADETLS);
520
521     if(imapc->ssldone) {
522       imap_to_imaps(conn);
523       result = imap_perform_capability(conn);
524     }
525   }
526
527   return result;
528 }
529
530 /***********************************************************************
531  *
532  * imap_perform_login()
533  *
534  * Sends a clear text LOGIN command to authenticate with.
535  */
536 static CURLcode imap_perform_login(struct connectdata *conn)
537 {
538   CURLcode result = CURLE_OK;
539   char *user;
540   char *passwd;
541
542   /* Check we have a username and password to authenticate with and end the
543      connect phase if we don't */
544   if(!conn->bits.user_passwd) {
545     state(conn, IMAP_STOP);
546
547     return result;
548   }
549
550   /* Make sure the username and password are in the correct atom format */
551   user = imap_atom(conn->user);
552   passwd = imap_atom(conn->passwd);
553
554   /* Send the LOGIN command */
555   result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
556                       passwd ? passwd : "");
557
558   Curl_safefree(user);
559   Curl_safefree(passwd);
560
561   if(!result)
562     state(conn, IMAP_LOGIN);
563
564   return result;
565 }
566
567 /***********************************************************************
568  *
569  * imap_perform_authenticate()
570  *
571  * Sends an AUTHENTICATE command allowing the client to login with the given
572  * SASL authentication mechanism.
573  */
574 static CURLcode imap_perform_authenticate(struct connectdata *conn,
575                                           const char *mech,
576                                           const char *initresp,
577                                           imapstate state1, imapstate state2)
578 {
579   CURLcode result = CURLE_OK;
580
581   if(initresp) {
582     /* Send the AUTHENTICATE command with the initial response */
583     result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
584
585     if(!result)
586       state(conn, state2);
587   }
588   else {
589     /* Send the AUTHENTICATE command */
590     result = imap_sendf(conn, "AUTHENTICATE %s", mech);
591
592     if(!result)
593       state(conn, state1);
594   }
595
596   return result;
597 }
598
599 /***********************************************************************
600  *
601  * imap_perform_authentication()
602  *
603  * Initiates the authentication sequence, with the appropriate SASL
604  * authentication mechanism, falling back to clear text should a common
605  * mechanism not be available between the client and server.
606  */
607 static CURLcode imap_perform_authentication(struct connectdata *conn)
608 {
609   CURLcode result = CURLE_OK;
610   struct imap_conn *imapc = &conn->proto.imapc;
611   const char *mech = NULL;
612   char *initresp = NULL;
613   size_t len = 0;
614   imapstate state1 = IMAP_STOP;
615   imapstate state2 = IMAP_STOP;
616
617   /* Check we have a username and password to authenticate with and end the
618      connect phase if we don't */
619   if(!conn->bits.user_passwd) {
620     state(conn, IMAP_STOP);
621
622     return result;
623   }
624
625   /* Calculate the SASL login details */
626   result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
627                                   &state2);
628
629   if(!result) {
630     if(mech && (imapc->preftype & IMAP_TYPE_SASL)) {
631       /* Perform SASL based authentication */
632       result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
633
634       Curl_safefree(initresp);
635     }
636     else if((!imapc->login_disabled) &&
637             (imapc->preftype & IMAP_TYPE_CLEARTEXT))
638       /* Perform clear text authentication */
639       result = imap_perform_login(conn);
640     else {
641       /* Other mechanisms not supported */
642       infof(conn->data, "No known authentication mechanisms supported!\n");
643       result = CURLE_LOGIN_DENIED;
644     }
645   }
646
647   return result;
648 }
649
650 /***********************************************************************
651  *
652  * imap_perform_list()
653  *
654  * Sends a LIST command or an alternative custom request.
655  */
656 static CURLcode imap_perform_list(struct connectdata *conn)
657 {
658   CURLcode result = CURLE_OK;
659   struct SessionHandle *data = conn->data;
660   struct IMAP *imap = data->req.protop;
661   char *mailbox;
662
663   if(imap->custom)
664     /* Send the custom request */
665     result = imap_sendf(conn, "%s%s", imap->custom,
666                         imap->custom_params ? imap->custom_params : "");
667   else {
668     /* Make sure the mailbox is in the correct atom format */
669     mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
670     if(!mailbox)
671       return CURLE_OUT_OF_MEMORY;
672
673     /* Send the LIST command */
674     result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
675
676     Curl_safefree(mailbox);
677   }
678
679   if(!result)
680     state(conn, IMAP_LIST);
681
682   return result;
683 }
684
685 /***********************************************************************
686  *
687  * imap_perform_select()
688  *
689  * Sends a SELECT command to ask the server to change the selected mailbox.
690  */
691 static CURLcode imap_perform_select(struct connectdata *conn)
692 {
693   CURLcode result = CURLE_OK;
694   struct SessionHandle *data = conn->data;
695   struct IMAP *imap = data->req.protop;
696   struct imap_conn *imapc = &conn->proto.imapc;
697   char *mailbox;
698
699   /* Invalidate old information as we are switching mailboxes */
700   Curl_safefree(imapc->mailbox);
701   Curl_safefree(imapc->mailbox_uidvalidity);
702
703   /* Check we have a mailbox */
704   if(!imap->mailbox) {
705     failf(conn->data, "Cannot SELECT without a mailbox.");
706     return CURLE_URL_MALFORMAT;
707   }
708
709   /* Make sure the mailbox is in the correct atom format */
710   mailbox = imap_atom(imap->mailbox);
711   if(!mailbox)
712     return CURLE_OUT_OF_MEMORY;
713
714   /* Send the SELECT command */
715   result = imap_sendf(conn, "SELECT %s", mailbox);
716
717   Curl_safefree(mailbox);
718
719   if(!result)
720     state(conn, IMAP_SELECT);
721
722   return result;
723 }
724
725 /***********************************************************************
726  *
727  * imap_perform_fetch()
728  *
729  * Sends a FETCH command to initiate the download of a message.
730  */
731 static CURLcode imap_perform_fetch(struct connectdata *conn)
732 {
733   CURLcode result = CURLE_OK;
734   struct IMAP *imap = conn->data->req.protop;
735
736   /* Check we have a UID */
737   if(!imap->uid) {
738     failf(conn->data, "Cannot FETCH without a UID.");
739     return CURLE_URL_MALFORMAT;
740   }
741
742   /* Send the FETCH command */
743   if(imap->partial)
744     result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
745                         imap->uid,
746                         imap->section ? imap->section : "",
747                         imap->partial);
748   else
749     result = imap_sendf(conn, "FETCH %s BODY[%s]",
750                         imap->uid,
751                         imap->section ? imap->section : "");
752
753   if(!result)
754     state(conn, IMAP_FETCH);
755
756   return result;
757 }
758
759 /***********************************************************************
760  *
761  * imap_perform_append()
762  *
763  * Sends an APPEND command to initiate the upload of a message.
764  */
765 static CURLcode imap_perform_append(struct connectdata *conn)
766 {
767   CURLcode result = CURLE_OK;
768   struct IMAP *imap = conn->data->req.protop;
769   char *mailbox;
770
771   /* Check we have a mailbox */
772   if(!imap->mailbox) {
773     failf(conn->data, "Cannot APPEND without a mailbox.");
774     return CURLE_URL_MALFORMAT;
775   }
776
777   /* Check we know the size of the upload */
778   if(conn->data->state.infilesize < 0) {
779     failf(conn->data, "Cannot APPEND with unknown input file size\n");
780     return CURLE_UPLOAD_FAILED;
781   }
782
783   /* Make sure the mailbox is in the correct atom format */
784   mailbox = imap_atom(imap->mailbox);
785   if(!mailbox)
786     return CURLE_OUT_OF_MEMORY;
787
788   /* Send the APPEND command */
789   result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
790                       mailbox, conn->data->state.infilesize);
791
792   Curl_safefree(mailbox);
793
794   if(!result)
795     state(conn, IMAP_APPEND);
796
797   return result;
798 }
799
800 /***********************************************************************
801  *
802  * imap_perform_search()
803  *
804  * Sends a SEARCH command.
805  */
806 static CURLcode imap_perform_search(struct connectdata *conn)
807 {
808   CURLcode result = CURLE_OK;
809   struct IMAP *imap = conn->data->req.protop;
810
811   /* Check we have a query string */
812   if(!imap->query) {
813     failf(conn->data, "Cannot SEARCH without a query string.");
814     return CURLE_URL_MALFORMAT;
815   }
816
817   /* Send the SEARCH command */
818   result = imap_sendf(conn, "SEARCH %s", imap->query);
819
820   if(!result)
821     state(conn, IMAP_SEARCH);
822
823   return result;
824 }
825
826 /***********************************************************************
827  *
828  * imap_perform_logout()
829  *
830  * Performs the logout action prior to sclose() being called.
831  */
832 static CURLcode imap_perform_logout(struct connectdata *conn)
833 {
834   CURLcode result = CURLE_OK;
835
836   /* Send the LOGOUT command */
837   result = imap_sendf(conn, "LOGOUT");
838
839   if(!result)
840     state(conn, IMAP_LOGOUT);
841
842   return result;
843 }
844
845 /* For the initial server greeting */
846 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
847                                             int imapcode,
848                                             imapstate instate)
849 {
850   CURLcode result = CURLE_OK;
851   struct SessionHandle *data = conn->data;
852
853   (void)instate; /* no use for this yet */
854
855   if(imapcode != 'O') {
856     failf(data, "Got unexpected imap-server response");
857     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
858   }
859   else
860     result = imap_perform_capability(conn);
861
862   return result;
863 }
864
865 /* For CAPABILITY responses */
866 static CURLcode imap_state_capability_resp(struct connectdata *conn,
867                                            int imapcode,
868                                            imapstate instate)
869 {
870   CURLcode result = CURLE_OK;
871   struct SessionHandle *data = conn->data;
872   struct imap_conn *imapc = &conn->proto.imapc;
873   const char *line = data->state.buffer;
874   size_t wordlen;
875
876   (void)instate; /* no use for this yet */
877
878   /* Do we have a untagged response? */
879   if(imapcode == '*') {
880     line += 2;
881
882     /* Loop through the data line */
883     for(;;) {
884       while(*line &&
885             (*line == ' ' || *line == '\t' ||
886               *line == '\r' || *line == '\n')) {
887
888         line++;
889       }
890
891       if(!*line)
892         break;
893
894       /* Extract the word */
895       for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
896             line[wordlen] != '\t' && line[wordlen] != '\r' &&
897             line[wordlen] != '\n';)
898         wordlen++;
899
900       /* Does the server support the STARTTLS capability? */
901       if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
902         imapc->tls_supported = TRUE;
903
904       /* Has the server explicitly disabled clear text authentication? */
905       else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
906         imapc->login_disabled = TRUE;
907
908       /* Does the server support the SASL-IR capability? */
909       else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
910         imapc->ir_supported = TRUE;
911
912       /* Do we have a SASL based authentication mechanism? */
913       else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
914         line += 5;
915         wordlen -= 5;
916
917         /* Test the word for a matching authentication mechanism */
918         if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
919           imapc->authmechs |= SASL_MECH_LOGIN;
920         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
921           imapc->authmechs |= SASL_MECH_PLAIN;
922         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
923           imapc->authmechs |= SASL_MECH_CRAM_MD5;
924         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
925           imapc->authmechs |= SASL_MECH_DIGEST_MD5;
926         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
927           imapc->authmechs |= SASL_MECH_GSSAPI;
928         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
929           imapc->authmechs |= SASL_MECH_EXTERNAL;
930         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
931           imapc->authmechs |= SASL_MECH_NTLM;
932         else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
933           imapc->authmechs |= SASL_MECH_XOAUTH2;
934       }
935
936       line += wordlen;
937     }
938   }
939   else if(imapcode == 'O') {
940     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
941       /* We don't have a SSL/TLS connection yet, but SSL is requested */
942       if(imapc->tls_supported)
943         /* Switch to TLS connection now */
944         result = imap_perform_starttls(conn);
945       else if(data->set.use_ssl == CURLUSESSL_TRY)
946         /* Fallback and carry on with authentication */
947         result = imap_perform_authentication(conn);
948       else {
949         failf(data, "STARTTLS not supported.");
950         result = CURLE_USE_SSL_FAILED;
951       }
952     }
953     else
954       result = imap_perform_authentication(conn);
955   }
956   else
957     result = imap_perform_authentication(conn);
958
959   return result;
960 }
961
962 /* For STARTTLS responses */
963 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
964                                          int imapcode,
965                                          imapstate instate)
966 {
967   CURLcode result = CURLE_OK;
968   struct SessionHandle *data = conn->data;
969
970   (void)instate; /* no use for this yet */
971
972   if(imapcode != 'O') {
973     if(data->set.use_ssl != CURLUSESSL_TRY) {
974       failf(data, "STARTTLS denied. %c", imapcode);
975       result = CURLE_USE_SSL_FAILED;
976     }
977     else
978       result = imap_perform_authentication(conn);
979   }
980   else
981     result = imap_perform_upgrade_tls(conn);
982
983   return result;
984 }
985
986 /* For AUTHENTICATE PLAIN (without initial response) responses */
987 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
988                                            int imapcode,
989                                            imapstate instate)
990 {
991   CURLcode result = CURLE_OK;
992   struct SessionHandle *data = conn->data;
993   size_t len = 0;
994   char *plainauth = NULL;
995
996   (void)instate; /* no use for this yet */
997
998   if(imapcode != '+') {
999     failf(data, "Access denied. %c", imapcode);
1000     result = CURLE_LOGIN_DENIED;
1001   }
1002   else {
1003     /* Create the authorisation message */
1004     result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
1005                                             &plainauth, &len);
1006     if(!result && plainauth) {
1007       /* Send the message */
1008       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
1009
1010       if(!result)
1011         state(conn, IMAP_AUTHENTICATE_FINAL);
1012     }
1013   }
1014
1015   Curl_safefree(plainauth);
1016
1017   return result;
1018 }
1019
1020 /* For AUTHENTICATE LOGIN (without initial response) responses */
1021 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
1022                                            int imapcode,
1023                                            imapstate instate)
1024 {
1025   CURLcode result = CURLE_OK;
1026   struct SessionHandle *data = conn->data;
1027   size_t len = 0;
1028   char *authuser = NULL;
1029
1030   (void)instate; /* no use for this yet */
1031
1032   if(imapcode != '+') {
1033     failf(data, "Access denied: %d", imapcode);
1034     result = CURLE_LOGIN_DENIED;
1035   }
1036   else {
1037     /* Create the user message */
1038     result = Curl_sasl_create_login_message(data, conn->user,
1039                                             &authuser, &len);
1040     if(!result && authuser) {
1041       /* Send the user */
1042       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
1043
1044       if(!result)
1045         state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
1046     }
1047   }
1048
1049   Curl_safefree(authuser);
1050
1051   return result;
1052 }
1053
1054 /* For AUTHENTICATE LOGIN user entry responses */
1055 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
1056                                                     int imapcode,
1057                                                     imapstate instate)
1058 {
1059   CURLcode result = CURLE_OK;
1060   struct SessionHandle *data = conn->data;
1061   size_t len = 0;
1062   char *authpasswd = NULL;
1063
1064   (void)instate; /* no use for this yet */
1065
1066   if(imapcode != '+') {
1067     failf(data, "Access denied: %d", imapcode);
1068     result = CURLE_LOGIN_DENIED;
1069   }
1070   else {
1071     /* Create the password message */
1072     result = Curl_sasl_create_login_message(data, conn->passwd,
1073                                             &authpasswd, &len);
1074     if(!result && authpasswd) {
1075       /* Send the password */
1076       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
1077
1078       if(!result)
1079         state(conn, IMAP_AUTHENTICATE_FINAL);
1080     }
1081   }
1082
1083   Curl_safefree(authpasswd);
1084
1085   return result;
1086 }
1087
1088 #ifndef CURL_DISABLE_CRYPTO_AUTH
1089 /* For AUTHENTICATE CRAM-MD5 responses */
1090 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
1091                                           int imapcode,
1092                                           imapstate instate)
1093 {
1094   CURLcode result = CURLE_OK;
1095   struct SessionHandle *data = conn->data;
1096   char *chlg = NULL;
1097   char *chlg64 = NULL;
1098   char *rplyb64 = NULL;
1099   size_t len = 0;
1100
1101   (void)instate; /* no use for this yet */
1102
1103   if(imapcode != '+') {
1104     failf(data, "Access denied: %d", imapcode);
1105     return CURLE_LOGIN_DENIED;
1106   }
1107
1108   /* Get the challenge message */
1109   imap_get_message(data->state.buffer, &chlg64);
1110
1111   /* Decode the challenge message */
1112   result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
1113   if(result) {
1114     /* Send the cancellation */
1115     result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1116
1117     if(!result)
1118       state(conn, IMAP_AUTHENTICATE_CANCEL);
1119   }
1120   else {
1121     /* Create the response message */
1122     result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
1123                                                conn->passwd, &rplyb64, &len);
1124     if(!result && rplyb64) {
1125       /* Send the response */
1126       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1127
1128       if(!result)
1129         state(conn, IMAP_AUTHENTICATE_FINAL);
1130     }
1131   }
1132
1133   Curl_safefree(chlg);
1134   Curl_safefree(rplyb64);
1135
1136   return result;
1137 }
1138
1139 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
1140 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
1141                                             int imapcode,
1142                                             imapstate instate)
1143 {
1144   CURLcode result = CURLE_OK;
1145   struct SessionHandle *data = conn->data;
1146   char *chlg64 = NULL;
1147   char *rplyb64 = NULL;
1148   size_t len = 0;
1149
1150   (void)instate; /* no use for this yet */
1151
1152   if(imapcode != '+') {
1153     failf(data, "Access denied: %d", imapcode);
1154     return CURLE_LOGIN_DENIED;
1155   }
1156
1157   /* Get the challenge message */
1158   imap_get_message(data->state.buffer, &chlg64);
1159
1160   /* Create the response message */
1161   result = Curl_sasl_create_digest_md5_message(data, chlg64,
1162                                                conn->user, conn->passwd,
1163                                                "imap", &rplyb64, &len);
1164   if(result) {
1165     if(result == CURLE_BAD_CONTENT_ENCODING) {
1166       /* Send the cancellation */
1167       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1168
1169       if(!result)
1170         state(conn, IMAP_AUTHENTICATE_CANCEL);
1171     }
1172   }
1173   else {
1174     /* Send the response */
1175     result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
1176
1177     if(!result)
1178       state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
1179   }
1180
1181   Curl_safefree(rplyb64);
1182
1183   return result;
1184 }
1185
1186 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
1187 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
1188                                                  int imapcode,
1189                                                  imapstate instate)
1190 {
1191   CURLcode result = CURLE_OK;
1192   struct SessionHandle *data = conn->data;
1193
1194   (void)instate; /* no use for this yet */
1195
1196   if(imapcode != '+') {
1197     failf(data, "Authentication failed: %d", imapcode);
1198     result = CURLE_LOGIN_DENIED;
1199   }
1200   else {
1201     /* Send an empty response */
1202     result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1203
1204     if(!result)
1205       state(conn, IMAP_AUTHENTICATE_FINAL);
1206   }
1207
1208   return result;
1209 }
1210 #endif
1211
1212 #ifdef USE_NTLM
1213 /* For AUTHENTICATE NTLM (without initial response) responses */
1214 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
1215                                           int imapcode,
1216                                           imapstate instate)
1217 {
1218   CURLcode result = CURLE_OK;
1219   struct SessionHandle *data = conn->data;
1220   size_t len = 0;
1221   char *type1msg = NULL;
1222
1223   (void)instate; /* no use for this yet */
1224
1225   if(imapcode != '+') {
1226     failf(data, "Access denied: %d", imapcode);
1227     result = CURLE_LOGIN_DENIED;
1228   }
1229   else {
1230     /* Create the type-1 message */
1231     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1232                                                  &conn->ntlm,
1233                                                  &type1msg, &len);
1234     if(!result && type1msg) {
1235       /* Send the message */
1236       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
1237
1238       if(!result)
1239         state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
1240     }
1241   }
1242
1243   Curl_safefree(type1msg);
1244
1245   return result;
1246 }
1247
1248 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1249 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1250                                                    int imapcode,
1251                                                    imapstate instate)
1252 {
1253   CURLcode result = CURLE_OK;
1254   struct SessionHandle *data = conn->data;
1255   char *type2msg = NULL;
1256   char *type3msg = NULL;
1257   size_t len = 0;
1258
1259   (void)instate; /* no use for this yet */
1260
1261   if(imapcode != '+') {
1262     failf(data, "Access denied: %d", imapcode);
1263     result = CURLE_LOGIN_DENIED;
1264   }
1265   else {
1266     /* Get the challenge message */
1267     imap_get_message(data->state.buffer, &type2msg);
1268
1269     /* Decode the type-2 message */
1270     result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1271     if(result) {
1272       /* Send the cancellation */
1273       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "*");
1274
1275       if(!result)
1276         state(conn, IMAP_AUTHENTICATE_CANCEL);
1277     }
1278     else {
1279       /* Create the type-3 message */
1280       result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1281                                                    conn->passwd, &conn->ntlm,
1282                                                    &type3msg, &len);
1283       if(!result && type3msg) {
1284         /* Send the message */
1285         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
1286
1287         if(!result)
1288           state(conn, IMAP_AUTHENTICATE_FINAL);
1289       }
1290     }
1291   }
1292
1293   Curl_safefree(type3msg);
1294
1295   return result;
1296 }
1297 #endif
1298
1299 /* For AUTHENTICATE XOAUTH2 (without initial response) responses */
1300 static CURLcode imap_state_auth_xoauth2_resp(struct connectdata *conn,
1301                                              int imapcode,
1302                                              imapstate instate)
1303 {
1304   CURLcode result = CURLE_OK;
1305   struct SessionHandle *data = conn->data;
1306   size_t len = 0;
1307   char *xoauth = NULL;
1308
1309   (void)instate; /* no use for this yet */
1310
1311   if(imapcode != '+') {
1312     failf(data, "Access denied: %d", imapcode);
1313     result = CURLE_LOGIN_DENIED;
1314   }
1315   else {
1316     /* Create the authorisation message */
1317     result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1318                                               conn->xoauth2_bearer,
1319                                               &xoauth, &len);
1320     if(!result && xoauth) {
1321       /* Send the message */
1322       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", xoauth);
1323
1324       if(!result)
1325         state(conn, IMAP_AUTHENTICATE_FINAL);
1326     }
1327   }
1328
1329   Curl_safefree(xoauth);
1330
1331   return result;
1332 }
1333
1334 /* For AUTHENTICATE cancellation responses */
1335 static CURLcode imap_state_auth_cancel_resp(struct connectdata *conn,
1336                                             int imapcode,
1337                                             imapstate instate)
1338 {
1339   CURLcode result = CURLE_OK;
1340   struct SessionHandle *data = conn->data;
1341   struct imap_conn *imapc = &conn->proto.imapc;
1342   const char *mech = NULL;
1343   char *initresp = NULL;
1344   size_t len = 0;
1345   imapstate state1 = IMAP_STOP;
1346   imapstate state2 = IMAP_STOP;
1347
1348   (void)imapcode;
1349   (void)instate; /* no use for this yet */
1350
1351   /* Remove the offending mechanism from the supported list */
1352   imapc->authmechs ^= imapc->authused;
1353
1354   /* Calculate alternative SASL login details */
1355   result = imap_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1356                                   &state2);
1357
1358   if(!result) {
1359     /* Do we have any mechanisms left or can we fallback to clear text? */
1360     if(mech) {
1361       /* Retry SASL based authentication */
1362       result = imap_perform_authenticate(conn, mech, initresp, state1, state2);
1363
1364       Curl_safefree(initresp);
1365     }
1366     else if((!imapc->login_disabled) &&
1367             (imapc->preftype & IMAP_TYPE_CLEARTEXT))
1368       /* Perform clear text authentication */
1369       result = imap_perform_login(conn);
1370     else {
1371       failf(data, "Authentication cancelled");
1372
1373       result = CURLE_LOGIN_DENIED;
1374     }
1375   }
1376
1377   return result;
1378 }
1379
1380 /* For final responses in the AUTHENTICATE sequence */
1381 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1382                                            int imapcode,
1383                                            imapstate instate)
1384 {
1385   CURLcode result = CURLE_OK;
1386   struct SessionHandle *data = conn->data;
1387
1388   (void)instate; /* no use for this yet */
1389
1390   if(imapcode != 'O') {
1391     failf(data, "Authentication failed: %d", imapcode);
1392     result = CURLE_LOGIN_DENIED;
1393   }
1394   else
1395     /* End of connect phase */
1396     state(conn, IMAP_STOP);
1397
1398   return result;
1399 }
1400
1401 /* For LOGIN responses */
1402 static CURLcode imap_state_login_resp(struct connectdata *conn,
1403                                       int imapcode,
1404                                       imapstate instate)
1405 {
1406   CURLcode result = CURLE_OK;
1407   struct SessionHandle *data = conn->data;
1408
1409   (void)instate; /* no use for this yet */
1410
1411   if(imapcode != 'O') {
1412     failf(data, "Access denied. %c", imapcode);
1413     result = CURLE_LOGIN_DENIED;
1414   }
1415   else
1416     /* End of connect phase */
1417     state(conn, IMAP_STOP);
1418
1419   return result;
1420 }
1421
1422 /* For LIST responses */
1423 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
1424                                      imapstate instate)
1425 {
1426   CURLcode result = CURLE_OK;
1427   char *line = conn->data->state.buffer;
1428   size_t len = strlen(line);
1429
1430   (void)instate; /* No use for this yet */
1431
1432   if(imapcode == '*') {
1433     /* Temporarily add the LF character back and send as body to the client */
1434     line[len] = '\n';
1435     result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1436     line[len] = '\0';
1437   }
1438   else if(imapcode != 'O')
1439     result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1440   else
1441     /* End of DO phase */
1442     state(conn, IMAP_STOP);
1443
1444   return result;
1445 }
1446
1447 /* For SELECT responses */
1448 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1449                                        imapstate instate)
1450 {
1451   CURLcode result = CURLE_OK;
1452   struct SessionHandle *data = conn->data;
1453   struct IMAP *imap = conn->data->req.protop;
1454   struct imap_conn *imapc = &conn->proto.imapc;
1455   const char *line = data->state.buffer;
1456   char tmp[20];
1457
1458   (void)instate; /* no use for this yet */
1459
1460   if(imapcode == '*') {
1461     /* See if this is an UIDVALIDITY response */
1462     if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1463       Curl_safefree(imapc->mailbox_uidvalidity);
1464       imapc->mailbox_uidvalidity = strdup(tmp);
1465     }
1466   }
1467   else if(imapcode == 'O') {
1468     /* Check if the UIDVALIDITY has been specified and matches */
1469     if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1470        strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1471       failf(conn->data, "Mailbox UIDVALIDITY has changed");
1472       result = CURLE_REMOTE_FILE_NOT_FOUND;
1473     }
1474     else {
1475       /* Note the currently opened mailbox on this connection */
1476       imapc->mailbox = strdup(imap->mailbox);
1477
1478       if(imap->custom)
1479         result = imap_perform_list(conn);
1480       else if(imap->query)
1481         result = imap_perform_search(conn);
1482       else
1483         result = imap_perform_fetch(conn);
1484     }
1485   }
1486   else {
1487     failf(data, "Select failed");
1488     result = CURLE_LOGIN_DENIED;
1489   }
1490
1491   return result;
1492 }
1493
1494 /* For the (first line of the) FETCH responses */
1495 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1496                                       imapstate instate)
1497 {
1498   CURLcode result = CURLE_OK;
1499   struct SessionHandle *data = conn->data;
1500   struct imap_conn *imapc = &conn->proto.imapc;
1501   struct pingpong *pp = &imapc->pp;
1502   const char *ptr = data->state.buffer;
1503   bool parsed = FALSE;
1504   curl_off_t size;
1505
1506   (void)instate; /* no use for this yet */
1507
1508   if(imapcode != '*') {
1509     Curl_pgrsSetDownloadSize(data, 0);
1510     state(conn, IMAP_STOP);
1511     return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1512   }
1513
1514   /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1515      the continuation data contained within the curly brackets */
1516   while(*ptr && (*ptr != '{'))
1517     ptr++;
1518
1519   if(*ptr == '{') {
1520     char *endptr;
1521     size = curlx_strtoofft(ptr + 1, &endptr, 10);
1522     if(endptr - ptr > 1 && endptr[0] == '}' &&
1523        endptr[1] == '\r' && endptr[2] == '\0')
1524       parsed = TRUE;
1525   }
1526
1527   if(parsed) {
1528     infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1529           size);
1530     Curl_pgrsSetDownloadSize(data, size);
1531
1532     if(pp->cache) {
1533       /* At this point there is a bunch of data in the header "cache" that is
1534          actually body content, send it as body and then skip it. Do note
1535          that there may even be additional "headers" after the body. */
1536       size_t chunk = pp->cache_size;
1537
1538       if(chunk > (size_t)size)
1539         /* The conversion from curl_off_t to size_t is always fine here */
1540         chunk = (size_t)size;
1541
1542       result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1543       if(result)
1544         return result;
1545
1546       data->req.bytecount += chunk;
1547
1548       infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1549             " bytes, %" CURL_FORMAT_CURL_OFF_TU
1550             " bytes are left for transfer\n", (curl_off_t)chunk,
1551             size - chunk);
1552
1553       /* Have we used the entire cache or just part of it?*/
1554       if(pp->cache_size > chunk) {
1555         /* Only part of it so shrink the cache to fit the trailing data */
1556         memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1557         pp->cache_size -= chunk;
1558       }
1559       else {
1560         /* Free the cache */
1561         Curl_safefree(pp->cache);
1562
1563         /* Reset the cache size */
1564         pp->cache_size = 0;
1565       }
1566     }
1567
1568     if(data->req.bytecount == size)
1569       /* The entire data is already transferred! */
1570       Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1571     else {
1572       /* IMAP download */
1573       data->req.maxdownload = size;
1574       Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1575     }
1576   }
1577   else {
1578     /* We don't know how to parse this line */
1579     failf(pp->conn->data, "Failed to parse FETCH response.");
1580     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1581   }
1582
1583   /* End of DO phase */
1584   state(conn, IMAP_STOP);
1585
1586   return result;
1587 }
1588
1589 /* For final FETCH responses performed after the download */
1590 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1591                                             int imapcode,
1592                                             imapstate instate)
1593 {
1594   CURLcode result = CURLE_OK;
1595
1596   (void)instate; /* No use for this yet */
1597
1598   if(imapcode != 'O')
1599     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code */
1600   else
1601     /* End of DONE phase */
1602     state(conn, IMAP_STOP);
1603
1604   return result;
1605 }
1606
1607 /* For APPEND responses */
1608 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1609                                        imapstate instate)
1610 {
1611   CURLcode result = CURLE_OK;
1612   struct SessionHandle *data = conn->data;
1613
1614   (void)instate; /* No use for this yet */
1615
1616   if(imapcode != '+') {
1617     result = CURLE_UPLOAD_FAILED;
1618   }
1619   else {
1620     /* Set the progress upload size */
1621     Curl_pgrsSetUploadSize(data, data->state.infilesize);
1622
1623     /* IMAP upload */
1624     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1625
1626     /* End of DO phase */
1627     state(conn, IMAP_STOP);
1628   }
1629
1630   return result;
1631 }
1632
1633 /* For final APPEND responses performed after the upload */
1634 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1635                                              int imapcode,
1636                                              imapstate instate)
1637 {
1638   CURLcode result = CURLE_OK;
1639
1640   (void)instate; /* No use for this yet */
1641
1642   if(imapcode != 'O')
1643     result = CURLE_UPLOAD_FAILED;
1644   else
1645     /* End of DONE phase */
1646     state(conn, IMAP_STOP);
1647
1648   return result;
1649 }
1650
1651 /* For SEARCH responses */
1652 static CURLcode imap_state_search_resp(struct connectdata *conn, int imapcode,
1653                                        imapstate instate)
1654 {
1655   CURLcode result = CURLE_OK;
1656   char *line = conn->data->state.buffer;
1657   size_t len = strlen(line);
1658
1659   (void)instate; /* No use for this yet */
1660
1661   if(imapcode == '*') {
1662     /* Temporarily add the LF character back and send as body to the client */
1663     line[len] = '\n';
1664     result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1665     line[len] = '\0';
1666   }
1667   else if(imapcode != 'O')
1668     result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1669   else
1670     /* End of DO phase */
1671     state(conn, IMAP_STOP);
1672
1673   return result;
1674 }
1675
1676 static CURLcode imap_statemach_act(struct connectdata *conn)
1677 {
1678   CURLcode result = CURLE_OK;
1679   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1680   int imapcode;
1681   struct imap_conn *imapc = &conn->proto.imapc;
1682   struct pingpong *pp = &imapc->pp;
1683   size_t nread = 0;
1684
1685   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1686   if(imapc->state == IMAP_UPGRADETLS)
1687     return imap_perform_upgrade_tls(conn);
1688
1689   /* Flush any data that needs to be sent */
1690   if(pp->sendleft)
1691     return Curl_pp_flushsend(pp);
1692
1693   do {
1694     /* Read the response from the server */
1695     result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1696     if(result)
1697       return result;
1698
1699     /* Was there an error parsing the response line? */
1700     if(imapcode == -1)
1701       return CURLE_FTP_WEIRD_SERVER_REPLY;
1702
1703     if(!imapcode)
1704       break;
1705
1706     /* We have now received a full IMAP server response */
1707     switch(imapc->state) {
1708     case IMAP_SERVERGREET:
1709       result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1710       break;
1711
1712     case IMAP_CAPABILITY:
1713       result = imap_state_capability_resp(conn, imapcode, imapc->state);
1714       break;
1715
1716     case IMAP_STARTTLS:
1717       result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1718       break;
1719
1720     case IMAP_AUTHENTICATE_PLAIN:
1721       result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1722       break;
1723
1724     case IMAP_AUTHENTICATE_LOGIN:
1725       result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1726       break;
1727
1728     case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1729       result = imap_state_auth_login_password_resp(conn, imapcode,
1730                                                    imapc->state);
1731       break;
1732
1733 #ifndef CURL_DISABLE_CRYPTO_AUTH
1734     case IMAP_AUTHENTICATE_CRAMMD5:
1735       result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1736       break;
1737
1738     case IMAP_AUTHENTICATE_DIGESTMD5:
1739       result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1740       break;
1741
1742     case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1743       result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1744       break;
1745 #endif
1746
1747 #ifdef USE_NTLM
1748     case IMAP_AUTHENTICATE_NTLM:
1749       result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1750       break;
1751
1752     case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1753       result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1754                                                   imapc->state);
1755       break;
1756 #endif
1757
1758     case IMAP_AUTHENTICATE_XOAUTH2:
1759       result = imap_state_auth_xoauth2_resp(conn, imapcode, imapc->state);
1760       break;
1761
1762     case IMAP_AUTHENTICATE_CANCEL:
1763       result = imap_state_auth_cancel_resp(conn, imapcode, imapc->state);
1764       break;
1765
1766     case IMAP_AUTHENTICATE_FINAL:
1767       result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1768       break;
1769
1770     case IMAP_LOGIN:
1771       result = imap_state_login_resp(conn, imapcode, imapc->state);
1772       break;
1773
1774     case IMAP_LIST:
1775       result = imap_state_list_resp(conn, imapcode, imapc->state);
1776       break;
1777
1778     case IMAP_SELECT:
1779       result = imap_state_select_resp(conn, imapcode, imapc->state);
1780       break;
1781
1782     case IMAP_FETCH:
1783       result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1784       break;
1785
1786     case IMAP_FETCH_FINAL:
1787       result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1788       break;
1789
1790     case IMAP_APPEND:
1791       result = imap_state_append_resp(conn, imapcode, imapc->state);
1792       break;
1793
1794     case IMAP_APPEND_FINAL:
1795       result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1796       break;
1797
1798     case IMAP_SEARCH:
1799       result = imap_state_search_resp(conn, imapcode, imapc->state);
1800       break;
1801
1802     case IMAP_LOGOUT:
1803       /* fallthrough, just stop! */
1804     default:
1805       /* internal error */
1806       state(conn, IMAP_STOP);
1807       break;
1808     }
1809   } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1810
1811   return result;
1812 }
1813
1814 /* Called repeatedly until done from multi.c */
1815 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1816 {
1817   CURLcode result = CURLE_OK;
1818   struct imap_conn *imapc = &conn->proto.imapc;
1819
1820   if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1821     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1822     if(result || !imapc->ssldone)
1823       return result;
1824   }
1825
1826   result = Curl_pp_statemach(&imapc->pp, FALSE);
1827   *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1828
1829   return result;
1830 }
1831
1832 static CURLcode imap_block_statemach(struct connectdata *conn)
1833 {
1834   CURLcode result = CURLE_OK;
1835   struct imap_conn *imapc = &conn->proto.imapc;
1836
1837   while(imapc->state != IMAP_STOP && !result)
1838     result = Curl_pp_statemach(&imapc->pp, TRUE);
1839
1840   return result;
1841 }
1842
1843 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1844    required */
1845 static CURLcode imap_init(struct connectdata *conn)
1846 {
1847   CURLcode result = CURLE_OK;
1848   struct SessionHandle *data = conn->data;
1849   struct IMAP *imap;
1850
1851   imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1852   if(!imap)
1853     result = CURLE_OUT_OF_MEMORY;
1854
1855   return result;
1856 }
1857
1858 /* For the IMAP "protocol connect" and "doing" phases only */
1859 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1860                         int numsocks)
1861 {
1862   return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1863 }
1864
1865 /***********************************************************************
1866  *
1867  * imap_connect()
1868  *
1869  * This function should do everything that is to be considered a part of the
1870  * connection phase.
1871  *
1872  * The variable 'done' points to will be TRUE if the protocol-layer connect
1873  * phase is done when this function returns, or FALSE if not.
1874  */
1875 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1876 {
1877   CURLcode result = CURLE_OK;
1878   struct imap_conn *imapc = &conn->proto.imapc;
1879   struct pingpong *pp = &imapc->pp;
1880
1881   *done = FALSE; /* default to not done yet */
1882
1883   /* We always support persistent connections in IMAP */
1884   connkeep(conn, "IMAP default");
1885
1886   /* Set the default response time-out */
1887   pp->response_time = RESP_TIMEOUT;
1888   pp->statemach_act = imap_statemach_act;
1889   pp->endofresp = imap_endofresp;
1890   pp->conn = conn;
1891
1892   /* Set the default preferred authentication type and mechanism */
1893   imapc->preftype = IMAP_TYPE_ANY;
1894   imapc->prefmech = SASL_AUTH_ANY;
1895
1896   /* Initialise the pingpong layer */
1897   Curl_pp_init(pp);
1898
1899   /* Parse the URL options */
1900   result = imap_parse_url_options(conn);
1901   if(result)
1902     return result;
1903
1904   /* Start off waiting for the server greeting response */
1905   state(conn, IMAP_SERVERGREET);
1906
1907   /* Start off with an response id of '*' */
1908   strcpy(imapc->resptag, "*");
1909
1910   result = imap_multi_statemach(conn, done);
1911
1912   return result;
1913 }
1914
1915 /***********************************************************************
1916  *
1917  * imap_done()
1918  *
1919  * The DONE function. This does what needs to be done after a single DO has
1920  * performed.
1921  *
1922  * Input argument is already checked for validity.
1923  */
1924 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1925                           bool premature)
1926 {
1927   CURLcode result = CURLE_OK;
1928   struct SessionHandle *data = conn->data;
1929   struct IMAP *imap = data->req.protop;
1930
1931   (void)premature;
1932
1933   if(!imap)
1934     /* When the easy handle is removed from the multi interface while libcurl
1935        is still trying to resolve the host name, the IMAP struct is not yet
1936        initialized. However, the removal action calls Curl_done() which in
1937        turn calls this function, so we simply return success. */
1938     return CURLE_OK;
1939
1940   if(status) {
1941     connclose(conn, "IMAP done with bad status"); /* marked for closure */
1942     result = status;         /* use the already set error code */
1943   }
1944   else if(!data->set.connect_only && !imap->custom &&
1945           (imap->uid || data->set.upload)) {
1946     /* Handle responses after FETCH or APPEND transfer has finished */
1947     if(!data->set.upload)
1948       state(conn, IMAP_FETCH_FINAL);
1949     else {
1950       /* End the APPEND command first by sending an empty line */
1951       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1952       if(!result)
1953         state(conn, IMAP_APPEND_FINAL);
1954     }
1955
1956     /* Run the state-machine
1957
1958        TODO: when the multi interface is used, this _really_ should be using
1959        the imap_multi_statemach function but we have no general support for
1960        non-blocking DONE operations, not in the multi state machine and with
1961        Curl_done() invokes on several places in the code!
1962     */
1963     if(!result)
1964       result = imap_block_statemach(conn);
1965   }
1966
1967   /* Cleanup our per-request based variables */
1968   Curl_safefree(imap->mailbox);
1969   Curl_safefree(imap->uidvalidity);
1970   Curl_safefree(imap->uid);
1971   Curl_safefree(imap->section);
1972   Curl_safefree(imap->partial);
1973   Curl_safefree(imap->query);
1974   Curl_safefree(imap->custom);
1975   Curl_safefree(imap->custom_params);
1976
1977   /* Clear the transfer mode for the next request */
1978   imap->transfer = FTPTRANSFER_BODY;
1979
1980   return result;
1981 }
1982
1983 /***********************************************************************
1984  *
1985  * imap_perform()
1986  *
1987  * This is the actual DO function for IMAP. Fetch or append a message, or do
1988  * other things according to the options previously setup.
1989  */
1990 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1991                              bool *dophase_done)
1992 {
1993   /* This is IMAP and no proxy */
1994   CURLcode result = CURLE_OK;
1995   struct SessionHandle *data = conn->data;
1996   struct IMAP *imap = data->req.protop;
1997   struct imap_conn *imapc = &conn->proto.imapc;
1998   bool selected = FALSE;
1999
2000   DEBUGF(infof(conn->data, "DO phase starts\n"));
2001
2002   if(conn->data->set.opt_no_body) {
2003     /* Requested no body means no transfer */
2004     imap->transfer = FTPTRANSFER_INFO;
2005   }
2006
2007   *dophase_done = FALSE; /* not done yet */
2008
2009   /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
2010      has already been selected on this connection */
2011   if(imap->mailbox && imapc->mailbox &&
2012      !strcmp(imap->mailbox, imapc->mailbox) &&
2013      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
2014       !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
2015     selected = TRUE;
2016
2017   /* Start the first command in the DO phase */
2018   if(conn->data->set.upload)
2019     /* APPEND can be executed directly */
2020     result = imap_perform_append(conn);
2021   else if(imap->custom && (selected || !imap->mailbox))
2022     /* Custom command using the same mailbox or no mailbox */
2023     result = imap_perform_list(conn);
2024   else if(!imap->custom && selected && imap->uid)
2025     /* FETCH from the same mailbox */
2026     result = imap_perform_fetch(conn);
2027   else if(!imap->custom && selected && imap->query)
2028     /* SEARCH the current mailbox */
2029     result = imap_perform_search(conn);
2030   else if(imap->mailbox && !selected &&
2031          (imap->custom || imap->uid || imap->query))
2032     /* SELECT the mailbox */
2033     result = imap_perform_select(conn);
2034   else
2035     /* LIST */
2036     result = imap_perform_list(conn);
2037
2038   if(result)
2039     return result;
2040
2041   /* Run the state-machine */
2042   result = imap_multi_statemach(conn, dophase_done);
2043
2044   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
2045
2046   if(*dophase_done)
2047     DEBUGF(infof(conn->data, "DO phase is complete\n"));
2048
2049   return result;
2050 }
2051
2052 /***********************************************************************
2053  *
2054  * imap_do()
2055  *
2056  * This function is registered as 'curl_do' function. It decodes the path
2057  * parts etc as a wrapper to the actual DO function (imap_perform).
2058  *
2059  * The input argument is already checked for validity.
2060  */
2061 static CURLcode imap_do(struct connectdata *conn, bool *done)
2062 {
2063   CURLcode result = CURLE_OK;
2064
2065   *done = FALSE; /* default to false */
2066
2067   /* Parse the URL path */
2068   result = imap_parse_url_path(conn);
2069   if(result)
2070     return result;
2071
2072   /* Parse the custom request */
2073   result = imap_parse_custom_request(conn);
2074   if(result)
2075     return result;
2076
2077   result = imap_regular_transfer(conn, done);
2078
2079   return result;
2080 }
2081
2082 /***********************************************************************
2083  *
2084  * imap_disconnect()
2085  *
2086  * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
2087  * resources. BLOCKING.
2088  */
2089 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
2090 {
2091   struct imap_conn *imapc = &conn->proto.imapc;
2092
2093   /* We cannot send quit unconditionally. If this connection is stale or
2094      bad in any way, sending quit and waiting around here will make the
2095      disconnect wait in vain and cause more problems than we need to. */
2096
2097   /* The IMAP session may or may not have been allocated/setup at this
2098      point! */
2099   if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
2100     if(!imap_perform_logout(conn))
2101       (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
2102
2103   /* Disconnect from the server */
2104   Curl_pp_disconnect(&imapc->pp);
2105
2106   /* Cleanup the SASL module */
2107   Curl_sasl_cleanup(conn, imapc->authused);
2108
2109   /* Cleanup our connection based variables */
2110   Curl_safefree(imapc->mailbox);
2111   Curl_safefree(imapc->mailbox_uidvalidity);
2112
2113   return CURLE_OK;
2114 }
2115
2116 /* Call this when the DO phase has completed */
2117 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
2118 {
2119   struct IMAP *imap = conn->data->req.protop;
2120
2121   (void)connected;
2122
2123   if(imap->transfer != FTPTRANSFER_BODY)
2124     /* no data to transfer */
2125     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2126
2127   return CURLE_OK;
2128 }
2129
2130 /* Called from multi.c while DOing */
2131 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
2132 {
2133   CURLcode result = imap_multi_statemach(conn, dophase_done);
2134
2135   if(result)
2136     DEBUGF(infof(conn->data, "DO phase failed\n"));
2137   else if(*dophase_done) {
2138     result = imap_dophase_done(conn, FALSE /* not connected */);
2139
2140     DEBUGF(infof(conn->data, "DO phase is complete\n"));
2141   }
2142
2143   return result;
2144 }
2145
2146 /***********************************************************************
2147  *
2148  * imap_regular_transfer()
2149  *
2150  * The input argument is already checked for validity.
2151  *
2152  * Performs all commands done before a regular transfer between a local and a
2153  * remote host.
2154  */
2155 static CURLcode imap_regular_transfer(struct connectdata *conn,
2156                                       bool *dophase_done)
2157 {
2158   CURLcode result = CURLE_OK;
2159   bool connected = FALSE;
2160   struct SessionHandle *data = conn->data;
2161
2162   /* Make sure size is unknown at this point */
2163   data->req.size = -1;
2164
2165   /* Set the progress data */
2166   Curl_pgrsSetUploadCounter(data, 0);
2167   Curl_pgrsSetDownloadCounter(data, 0);
2168   Curl_pgrsSetUploadSize(data, 0);
2169   Curl_pgrsSetDownloadSize(data, 0);
2170
2171   /* Carry out the perform */
2172   result = imap_perform(conn, &connected, dophase_done);
2173
2174   /* Perform post DO phase operations if necessary */
2175   if(!result && *dophase_done)
2176     result = imap_dophase_done(conn, connected);
2177
2178   return result;
2179 }
2180
2181 static CURLcode imap_setup_connection(struct connectdata *conn)
2182 {
2183   struct SessionHandle *data = conn->data;
2184
2185   /* Initialise the IMAP layer */
2186   CURLcode result = imap_init(conn);
2187   if(result)
2188     return result;
2189
2190   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
2191     /* Unless we have asked to tunnel IMAP operations through the proxy, we
2192        switch and use HTTP operations only */
2193 #ifndef CURL_DISABLE_HTTP
2194     if(conn->handler == &Curl_handler_imap)
2195       conn->handler = &Curl_handler_imap_proxy;
2196     else {
2197 #ifdef USE_SSL
2198       conn->handler = &Curl_handler_imaps_proxy;
2199 #else
2200       failf(data, "IMAPS not supported!");
2201       return CURLE_UNSUPPORTED_PROTOCOL;
2202 #endif
2203     }
2204
2205     /* set it up as an HTTP connection instead */
2206     return conn->handler->setup_connection(conn);
2207 #else
2208     failf(data, "IMAP over http proxy requires HTTP support built-in!");
2209     return CURLE_UNSUPPORTED_PROTOCOL;
2210 #endif
2211   }
2212
2213   data->state.path++;   /* don't include the initial slash */
2214
2215   return CURLE_OK;
2216 }
2217
2218 /***********************************************************************
2219  *
2220  * imap_sendf()
2221  *
2222  * Sends the formated string as an IMAP command to the server.
2223  *
2224  * Designed to never block.
2225  */
2226 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
2227 {
2228   CURLcode result = CURLE_OK;
2229   struct imap_conn *imapc = &conn->proto.imapc;
2230   char *taggedfmt;
2231   va_list ap;
2232
2233   DEBUGASSERT(fmt);
2234
2235   /* Calculate the next command ID wrapping at 3 digits */
2236   imapc->cmdid = (imapc->cmdid + 1) % 1000;
2237
2238   /* Calculate the tag based on the connection ID and command ID */
2239   snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
2240            'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
2241
2242   /* Prefix the format with the tag */
2243   taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
2244   if(!taggedfmt)
2245     return CURLE_OUT_OF_MEMORY;
2246
2247   /* Send the data with the tag */
2248   va_start(ap, fmt);
2249   result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
2250   va_end(ap);
2251
2252   Curl_safefree(taggedfmt);
2253
2254   return result;
2255 }
2256
2257 /***********************************************************************
2258  *
2259  * imap_atom()
2260  *
2261  * Checks the input string for characters that need escaping and returns an
2262  * atom ready for sending to the server.
2263  *
2264  * The returned string needs to be freed.
2265  *
2266  */
2267 static char *imap_atom(const char *str)
2268 {
2269   const char *p1;
2270   char *p2;
2271   size_t backsp_count = 0;
2272   size_t quote_count = 0;
2273   bool space_exists = FALSE;
2274   size_t newlen = 0;
2275   char *newstr = NULL;
2276
2277   if(!str)
2278     return NULL;
2279
2280   /* Count any unescapped characters */
2281   p1 = str;
2282   while(*p1) {
2283     if(*p1 == '\\')
2284       backsp_count++;
2285     else if(*p1 == '"')
2286       quote_count++;
2287     else if(*p1 == ' ')
2288       space_exists = TRUE;
2289
2290     p1++;
2291   }
2292
2293   /* Does the input contain any unescapped characters? */
2294   if(!backsp_count && !quote_count && !space_exists)
2295     return strdup(str);
2296
2297   /* Calculate the new string length */
2298   newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
2299
2300   /* Allocate the new string */
2301   newstr = (char *) malloc((newlen + 1) * sizeof(char));
2302   if(!newstr)
2303     return NULL;
2304
2305   /* Surround the string in quotes if necessary */
2306   p2 = newstr;
2307   if(space_exists) {
2308     newstr[0] = '"';
2309     newstr[newlen - 1] = '"';
2310     p2++;
2311   }
2312
2313   /* Copy the string, escaping backslash and quote characters along the way */
2314   p1 = str;
2315   while(*p1) {
2316     if(*p1 == '\\' || *p1 == '"') {
2317       *p2 = '\\';
2318       p2++;
2319     }
2320
2321    *p2 = *p1;
2322
2323     p1++;
2324     p2++;
2325   }
2326
2327   /* Terminate the string */
2328   newstr[newlen] = '\0';
2329
2330   return newstr;
2331 }
2332
2333 /***********************************************************************
2334  *
2335  * imap_is_bchar()
2336  *
2337  * Portable test of whether the specified char is a "bchar" as defined in the
2338  * grammar of RFC-5092.
2339  */
2340 static bool imap_is_bchar(char ch)
2341 {
2342   switch(ch) {
2343     /* bchar */
2344     case ':': case '@': case '/':
2345     /* bchar -> achar */
2346     case '&': case '=':
2347     /* bchar -> achar -> uchar -> unreserved */
2348     case '0': case '1': case '2': case '3': case '4': case '5': case '6':
2349     case '7': case '8': case '9':
2350     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
2351     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
2352     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
2353     case 'V': case 'W': case 'X': case 'Y': case 'Z':
2354     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
2355     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
2356     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
2357     case 'v': case 'w': case 'x': case 'y': case 'z':
2358     case '-': case '.': case '_': case '~':
2359     /* bchar -> achar -> uchar -> sub-delims-sh */
2360     case '!': case '$': case '\'': case '(': case ')': case '*':
2361     case '+': case ',':
2362     /* bchar -> achar -> uchar -> pct-encoded */
2363     case '%': /* HEXDIG chars are already included above */
2364       return true;
2365
2366     default:
2367       return false;
2368   }
2369 }
2370
2371 /***********************************************************************
2372  *
2373  * imap_parse_url_options()
2374  *
2375  * Parse the URL login options.
2376  */
2377 static CURLcode imap_parse_url_options(struct connectdata *conn)
2378 {
2379   CURLcode result = CURLE_OK;
2380   struct imap_conn *imapc = &conn->proto.imapc;
2381   const char *options = conn->options;
2382   const char *ptr = options;
2383   bool reset = TRUE;
2384
2385   while(ptr && *ptr) {
2386     const char *key = ptr;
2387
2388     while(*ptr && *ptr != '=')
2389         ptr++;
2390
2391     if(strnequal(key, "AUTH", 4)) {
2392       size_t len = 0;
2393       const char *value = ++ptr;
2394
2395       if(reset) {
2396         reset = FALSE;
2397         imapc->preftype = IMAP_TYPE_NONE;
2398         imapc->prefmech = SASL_AUTH_NONE;
2399       }
2400
2401       while(*ptr && *ptr != ';') {
2402         ptr++;
2403         len++;
2404       }
2405
2406       if(strnequal(value, "*", len)) {
2407         imapc->preftype = IMAP_TYPE_ANY;
2408         imapc->prefmech = SASL_AUTH_ANY;
2409       }
2410       else if(strnequal(value, SASL_MECH_STRING_LOGIN, len)) {
2411         imapc->preftype = IMAP_TYPE_SASL;
2412         imapc->prefmech |= SASL_MECH_LOGIN;
2413       }
2414       else if(strnequal(value, SASL_MECH_STRING_PLAIN, len)) {
2415         imapc->preftype = IMAP_TYPE_SASL;
2416         imapc->prefmech |= SASL_MECH_PLAIN;
2417       }
2418       else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len)) {
2419         imapc->preftype = IMAP_TYPE_SASL;
2420         imapc->prefmech |= SASL_MECH_CRAM_MD5;
2421       }
2422       else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len)) {
2423         imapc->preftype = IMAP_TYPE_SASL;
2424         imapc->prefmech |= SASL_MECH_DIGEST_MD5;
2425       }
2426       else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len)) {
2427         imapc->preftype = IMAP_TYPE_SASL;
2428         imapc->prefmech |= SASL_MECH_GSSAPI;
2429       }
2430       else if(strnequal(value, SASL_MECH_STRING_NTLM, len)) {
2431         imapc->preftype = IMAP_TYPE_SASL;
2432         imapc->prefmech |= SASL_MECH_NTLM;
2433       }
2434       else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len)) {
2435         imapc->preftype = IMAP_TYPE_SASL;
2436         imapc->prefmech |= SASL_MECH_XOAUTH2;
2437       }
2438
2439       if(*ptr == ';')
2440         ptr++;
2441     }
2442     else
2443       result = CURLE_URL_MALFORMAT;
2444   }
2445
2446   return result;
2447 }
2448
2449 /***********************************************************************
2450  *
2451  * imap_parse_url_path()
2452  *
2453  * Parse the URL path into separate path components.
2454  *
2455  */
2456 static CURLcode imap_parse_url_path(struct connectdata *conn)
2457 {
2458   /* The imap struct is already initialised in imap_connect() */
2459   CURLcode result = CURLE_OK;
2460   struct SessionHandle *data = conn->data;
2461   struct IMAP *imap = data->req.protop;
2462   const char *begin = data->state.path;
2463   const char *ptr = begin;
2464
2465   /* See how much of the URL is a valid path and decode it */
2466   while(imap_is_bchar(*ptr))
2467     ptr++;
2468
2469   if(ptr != begin) {
2470     /* Remove the trailing slash if present */
2471     const char *end = ptr;
2472     if(end > begin && end[-1] == '/')
2473       end--;
2474
2475     result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
2476                             TRUE);
2477     if(result)
2478       return result;
2479   }
2480   else
2481     imap->mailbox = NULL;
2482
2483   /* There can be any number of parameters in the form ";NAME=VALUE" */
2484   while(*ptr == ';') {
2485     char *name;
2486     char *value;
2487     size_t valuelen;
2488
2489     /* Find the length of the name parameter */
2490     begin = ++ptr;
2491     while(*ptr && *ptr != '=')
2492       ptr++;
2493
2494     if(!*ptr)
2495       return CURLE_URL_MALFORMAT;
2496
2497     /* Decode the name parameter */
2498     result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
2499     if(result)
2500       return result;
2501
2502     /* Find the length of the value parameter */
2503     begin = ++ptr;
2504     while(imap_is_bchar(*ptr))
2505       ptr++;
2506
2507     /* Decode the value parameter */
2508     result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
2509     if(result) {
2510       Curl_safefree(name);
2511       return result;
2512     }
2513
2514     DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2515
2516     /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2517        PARTIAL) stripping of the trailing slash character if it is present.
2518
2519        Note: Unknown parameters trigger a URL_MALFORMAT error. */
2520     if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
2521       if(valuelen > 0 && value[valuelen - 1] == '/')
2522         value[valuelen - 1] = '\0';
2523
2524       imap->uidvalidity = value;
2525       value = NULL;
2526     }
2527     else if(Curl_raw_equal(name, "UID") && !imap->uid) {
2528       if(valuelen > 0 && value[valuelen - 1] == '/')
2529         value[valuelen - 1] = '\0';
2530
2531       imap->uid = value;
2532       value = NULL;
2533     }
2534     else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
2535       if(valuelen > 0 && value[valuelen - 1] == '/')
2536         value[valuelen - 1] = '\0';
2537
2538       imap->section = value;
2539       value = NULL;
2540     }
2541     else if(Curl_raw_equal(name, "PARTIAL") && !imap->partial) {
2542       if(valuelen > 0 && value[valuelen - 1] == '/')
2543         value[valuelen - 1] = '\0';
2544
2545       imap->partial = value;
2546       value = NULL;
2547     }
2548     else {
2549       Curl_safefree(name);
2550       Curl_safefree(value);
2551
2552       return CURLE_URL_MALFORMAT;
2553     }
2554
2555     Curl_safefree(name);
2556     Curl_safefree(value);
2557   }
2558
2559   /* Does the URL contain a query parameter? Only valid when we have a mailbox
2560      and no UID as per RFC-5092 */
2561   if(imap->mailbox && !imap->uid && *ptr == '?') {
2562     /* Find the length of the query parameter */
2563     begin = ++ptr;
2564     while(imap_is_bchar(*ptr))
2565       ptr++;
2566
2567     /* Decode the query parameter */
2568     result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2569                             TRUE);
2570     if(result)
2571       return result;
2572   }
2573
2574   /* Any extra stuff at the end of the URL is an error */
2575   if(*ptr)
2576     return CURLE_URL_MALFORMAT;
2577
2578   return CURLE_OK;
2579 }
2580
2581 /***********************************************************************
2582  *
2583  * imap_parse_custom_request()
2584  *
2585  * Parse the custom request.
2586  */
2587 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2588 {
2589   CURLcode result = CURLE_OK;
2590   struct SessionHandle *data = conn->data;
2591   struct IMAP *imap = data->req.protop;
2592   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2593
2594   if(custom) {
2595     /* URL decode the custom request */
2596     result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2597
2598     /* Extract the parameters if specified */
2599     if(!result) {
2600       const char *params = imap->custom;
2601
2602       while(*params && *params != ' ')
2603         params++;
2604
2605       if(*params) {
2606         imap->custom_params = strdup(params);
2607         imap->custom[params - imap->custom] = '\0';
2608
2609         if(!imap->custom_params)
2610           result = CURLE_OUT_OF_MEMORY;
2611       }
2612     }
2613   }
2614
2615   return result;
2616 }
2617
2618 /***********************************************************************
2619  *
2620  * imap_calc_sasl_details()
2621  *
2622  * Calculate the required login details for SASL authentication.
2623  */
2624 static CURLcode imap_calc_sasl_details(struct connectdata *conn,
2625                                        const char **mech,
2626                                        char **initresp, size_t *len,
2627                                        imapstate *state1, imapstate *state2)
2628 {
2629   CURLcode result = CURLE_OK;
2630   struct SessionHandle *data = conn->data;
2631   struct imap_conn *imapc = &conn->proto.imapc;
2632
2633   /* Calculate the supported authentication mechanism, by decreasing order of
2634      security, as well as the initial response where appropriate */
2635 #ifndef CURL_DISABLE_CRYPTO_AUTH
2636   if((imapc->authmechs & SASL_MECH_DIGEST_MD5) &&
2637      (imapc->prefmech & SASL_MECH_DIGEST_MD5)) {
2638     *mech = SASL_MECH_STRING_DIGEST_MD5;
2639     *state1 = IMAP_AUTHENTICATE_DIGESTMD5;
2640     imapc->authused = SASL_MECH_DIGEST_MD5;
2641   }
2642   else if((imapc->authmechs & SASL_MECH_CRAM_MD5) &&
2643           (imapc->prefmech & SASL_MECH_CRAM_MD5)) {
2644     *mech = SASL_MECH_STRING_CRAM_MD5;
2645     *state1 = IMAP_AUTHENTICATE_CRAMMD5;
2646     imapc->authused = SASL_MECH_CRAM_MD5;
2647   }
2648   else
2649 #endif
2650 #ifdef USE_NTLM
2651     if((imapc->authmechs & SASL_MECH_NTLM) &&
2652        (imapc->prefmech & SASL_MECH_NTLM)) {
2653     *mech = SASL_MECH_STRING_NTLM;
2654     *state1 = IMAP_AUTHENTICATE_NTLM;
2655     *state2 = IMAP_AUTHENTICATE_NTLM_TYPE2MSG;
2656     imapc->authused = SASL_MECH_NTLM;
2657
2658     if(imapc->ir_supported || data->set.sasl_ir)
2659       result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2660                                                    &conn->ntlm,
2661                                                    initresp, len);
2662   }
2663   else
2664 #endif
2665   if(((imapc->authmechs & SASL_MECH_XOAUTH2) &&
2666       (imapc->prefmech & SASL_MECH_XOAUTH2) &&
2667       (imapc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2668     *mech = SASL_MECH_STRING_XOAUTH2;
2669     *state1 = IMAP_AUTHENTICATE_XOAUTH2;
2670     *state2 = IMAP_AUTHENTICATE_FINAL;
2671     imapc->authused = SASL_MECH_XOAUTH2;
2672
2673     if(imapc->ir_supported || data->set.sasl_ir)
2674       result = Curl_sasl_create_xoauth2_message(data, conn->user,
2675                                                 conn->xoauth2_bearer,
2676                                                 initresp, len);
2677   }
2678   else if((imapc->authmechs & SASL_MECH_LOGIN) &&
2679           (imapc->prefmech & SASL_MECH_LOGIN)) {
2680     *mech = SASL_MECH_STRING_LOGIN;
2681     *state1 = IMAP_AUTHENTICATE_LOGIN;
2682     *state2 = IMAP_AUTHENTICATE_LOGIN_PASSWD;
2683     imapc->authused = SASL_MECH_LOGIN;
2684
2685     if(imapc->ir_supported || data->set.sasl_ir)
2686       result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2687   }
2688   else if((imapc->authmechs & SASL_MECH_PLAIN) &&
2689           (imapc->prefmech & SASL_MECH_PLAIN)) {
2690     *mech = SASL_MECH_STRING_PLAIN;
2691     *state1 = IMAP_AUTHENTICATE_PLAIN;
2692     *state2 = IMAP_AUTHENTICATE_FINAL;
2693     imapc->authused = SASL_MECH_PLAIN;
2694
2695     if(imapc->ir_supported || data->set.sasl_ir)
2696       result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2697                                               initresp, len);
2698   }
2699
2700   return result;
2701 }
2702
2703 #endif /* CURL_DISABLE_IMAP */