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 */
92static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
93static CURLcode imap_do(struct connectdata *conn, bool *done);
94static CURLcode imap_done(struct connectdata *conn, CURLcode status,
95                          bool premature);
96static CURLcode imap_connect(struct connectdata *conn, bool *done);
97static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
98static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
99static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
100                        int numsocks);
101static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
102static CURLcode imap_setup_connection(struct connectdata *conn);
103static char *imap_atom(const char *str);
104static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
105static CURLcode imap_parse_url_options(struct connectdata *conn);
106static CURLcode imap_parse_url_path(struct connectdata *conn);
107static CURLcode imap_parse_custom_request(struct connectdata *conn);
108static 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
117const 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
142const 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
169static 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
194static 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
217static 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 */
235static 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 */
273static 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 */
388static 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 */
417static 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 */
466static 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 */
490static 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 */
509static 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 */
536static 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 */
574static 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 */
607static 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 */
656static 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 */
691static 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 */
731static 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 */
765static 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 */
806static 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 */
832static 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 */
846static 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 */
866static 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 */
963static 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 */
987static 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 */
1021static 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 */
1055static 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 */
1090static 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 */
1140static 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 */
1187static 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 */
1214static 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) */
1249static 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 */
1300static 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 */
1335static 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 */
1381static 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 */
1402static 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 */
1423static 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 */
1448static 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 */
1495static 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 */
1590static 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 */
1608static 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 */
1634static 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 */
1652static 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
1676static 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 */
1815static 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
1832static 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 */
1845static 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 */
1859static 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 */
1875static 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 */
1924static 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 */
1990static 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 */
2061static 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 */
2089static 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 */
2117static 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 */
2131static 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 */
2155static 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
2181static 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 */
2226static 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 */
2267static 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 */
2340static 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 */
2377static 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 */
2456static 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 */
2587static 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 */
2624static 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 */
2704