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