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