1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2011, 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 * RFC3501 IMAPv4 protocol
22 * RFC5092 IMAP URL Scheme
23 *
24 ***************************************************************************/
25
26#include "setup.h"
27
28#ifndef CURL_DISABLE_IMAP
29
30#ifdef HAVE_UNISTD_H
31#include <unistd.h>
32#endif
33
34#ifdef HAVE_SYS_SOCKET_H
35#include <sys/socket.h>
36#endif
37#ifdef HAVE_NETINET_IN_H
38#include <netinet/in.h>
39#endif
40#ifdef HAVE_ARPA_INET_H
41#include <arpa/inet.h>
42#endif
43#ifdef HAVE_UTSNAME_H
44#include <sys/utsname.h>
45#endif
46#ifdef HAVE_NETDB_H
47#include <netdb.h>
48#endif
49#ifdef __VMS
50#include <in.h>
51#include <inet.h>
52#endif
53
54#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
55#undef in_addr_t
56#define in_addr_t unsigned long
57#endif
58
59#include <curl/curl.h>
60#include "urldata.h"
61#include "sendf.h"
62#include "if2ip.h"
63#include "hostip.h"
64#include "progress.h"
65#include "transfer.h"
66#include "escape.h"
67#include "http.h" /* for HTTP proxy tunnel stuff */
68#include "socks.h"
69#include "imap.h"
70
71#include "strtoofft.h"
72#include "strequal.h"
73#include "sslgen.h"
74#include "connect.h"
75#include "strerror.h"
76#include "select.h"
77#include "multiif.h"
78#include "url.h"
79#include "rawstr.h"
80#include "strtoofft.h"
81#include "http_proxy.h"
82
83#define _MPRINTF_REPLACE /* use our functions only */
84#include <curl/mprintf.h>
85
86#include "curl_memory.h"
87/* The last #include file should be: */
88#include "memdebug.h"
89
90/* Local API functions */
91static CURLcode imap_parse_url_path(struct connectdata *conn);
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,
95                          CURLcode, 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,
100                        curl_socket_t *socks,
101                        int numsocks);
102static CURLcode imap_doing(struct connectdata *conn,
103                           bool *dophase_done);
104static CURLcode imap_setup_connection(struct connectdata * conn);
105static CURLcode imap_state_upgrade_tls(struct connectdata *conn);
106
107/*
108 * IMAP protocol handler.
109 */
110
111const struct Curl_handler Curl_handler_imap = {
112  "IMAP",                           /* scheme */
113  imap_setup_connection,            /* setup_connection */
114  imap_do,                          /* do_it */
115  imap_done,                        /* done */
116  ZERO_NULL,                        /* do_more */
117  imap_connect,                     /* connect_it */
118  imap_multi_statemach,             /* connecting */
119  imap_doing,                       /* doing */
120  imap_getsock,                     /* proto_getsock */
121  imap_getsock,                     /* doing_getsock */
122  ZERO_NULL,                        /* domore_getsock */
123  ZERO_NULL,                        /* perform_getsock */
124  imap_disconnect,                  /* disconnect */
125  ZERO_NULL,                        /* readwrite */
126  PORT_IMAP,                        /* defport */
127  CURLPROTO_IMAP,                   /* protocol */
128  PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD /* flags */
129};
130
131
132#ifdef USE_SSL
133/*
134 * IMAPS protocol handler.
135 */
136
137const struct Curl_handler Curl_handler_imaps = {
138  "IMAPS",                          /* scheme */
139  imap_setup_connection,            /* setup_connection */
140  imap_do,                          /* do_it */
141  imap_done,                        /* done */
142  ZERO_NULL,                        /* do_more */
143  imap_connect,                     /* connect_it */
144  imap_multi_statemach,             /* connecting */
145  imap_doing,                       /* doing */
146  imap_getsock,                     /* proto_getsock */
147  imap_getsock,                     /* doing_getsock */
148  ZERO_NULL,                        /* domore_getsock */
149  ZERO_NULL,                        /* perform_getsock */
150  imap_disconnect,                  /* disconnect */
151  ZERO_NULL,                        /* readwrite */
152  PORT_IMAPS,                       /* defport */
153  CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
154  PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD /* flags */
155};
156#endif
157
158#ifndef CURL_DISABLE_HTTP
159/*
160 * HTTP-proxyed IMAP protocol handler.
161 */
162
163static const struct Curl_handler Curl_handler_imap_proxy = {
164  "IMAP",                               /* scheme */
165  ZERO_NULL,                            /* setup_connection */
166  Curl_http,                            /* do_it */
167  Curl_http_done,                       /* done */
168  ZERO_NULL,                            /* do_more */
169  ZERO_NULL,                            /* connect_it */
170  ZERO_NULL,                            /* connecting */
171  ZERO_NULL,                            /* doing */
172  ZERO_NULL,                            /* proto_getsock */
173  ZERO_NULL,                            /* doing_getsock */
174  ZERO_NULL,                            /* domore_getsock */
175  ZERO_NULL,                            /* perform_getsock */
176  ZERO_NULL,                            /* disconnect */
177  ZERO_NULL,                            /* readwrite */
178  PORT_IMAP,                            /* defport */
179  CURLPROTO_HTTP,                       /* protocol */
180  PROTOPT_NONE                          /* flags */
181};
182
183
184#ifdef USE_SSL
185/*
186 * HTTP-proxyed IMAPS protocol handler.
187 */
188
189static const struct Curl_handler Curl_handler_imaps_proxy = {
190  "IMAPS",                              /* scheme */
191  ZERO_NULL,                            /* setup_connection */
192  Curl_http,                            /* do_it */
193  Curl_http_done,                       /* done */
194  ZERO_NULL,                            /* do_more */
195  ZERO_NULL,                            /* connect_it */
196  ZERO_NULL,                            /* connecting */
197  ZERO_NULL,                            /* doing */
198  ZERO_NULL,                            /* proto_getsock */
199  ZERO_NULL,                            /* doing_getsock */
200  ZERO_NULL,                            /* domore_getsock */
201  ZERO_NULL,                            /* perform_getsock */
202  ZERO_NULL,                            /* disconnect */
203  ZERO_NULL,                            /* readwrite */
204  PORT_IMAPS,                           /* defport */
205  CURLPROTO_HTTP,                       /* protocol */
206  PROTOPT_NONE                          /* flags */
207};
208#endif
209#endif
210
211/***********************************************************************
212 *
213 * imapsendf()
214 *
215 * Sends the formated string as an IMAP command to a server
216 *
217 * Designed to never block.
218 */
219static CURLcode imapsendf(struct connectdata *conn,
220                          const char *idstr, /* id to wait for at the
221                                                completion of this command */
222                          const char *fmt, ...)
223{
224  CURLcode res;
225  struct imap_conn *imapc = &conn->proto.imapc;
226  va_list ap;
227  va_start(ap, fmt);
228
229  imapc->idstr = idstr; /* this is the thing */
230
231  res = Curl_pp_vsendf(&imapc->pp, fmt, ap);
232
233  va_end(ap);
234
235  return res;
236}
237
238static const char *getcmdid(struct connectdata *conn)
239{
240  static const char * const ids[]= {
241    "A",
242    "B",
243    "C",
244    "D"
245  };
246
247  struct imap_conn *imapc = &conn->proto.imapc;
248
249  /* get the next id, but wrap at end of table */
250  imapc->cmdid = (int)((imapc->cmdid+1) % (sizeof(ids)/sizeof(ids[0])));
251
252  return ids[imapc->cmdid];
253}
254
255/* For the IMAP "protocol connect" and "doing" phases only */
256static int imap_getsock(struct connectdata *conn,
257                        curl_socket_t *socks,
258                        int numsocks)
259{
260  return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
261}
262
263/* function that checks for an imap status code at the start of the
264   given string */
265static int imap_endofresp(struct pingpong *pp, int *resp)
266{
267  char *line = pp->linestart_resp;
268  size_t len = pp->nread_resp;
269  struct imap_conn *imapc = &pp->conn->proto.imapc;
270  const char *id = imapc->idstr;
271  size_t id_len = strlen(id);
272
273  if(len >= id_len + 3) {
274    if(!memcmp(id, line, id_len) && (line[id_len] == ' ') ) {
275      /* end of response */
276      *resp = line[id_len+1]; /* O, N or B */
277      return TRUE;
278    }
279    else if((imapc->state == IMAP_FETCH) &&
280            !memcmp("* ", line, 2) ) {
281      /* FETCH response we're interested in */
282      *resp = '*';
283      return TRUE;
284    }
285  }
286  return FALSE; /* nothing for us */
287}
288
289/* This is the ONLY way to change IMAP state! */
290static void state(struct connectdata *conn,
291                  imapstate newstate)
292{
293#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
294  /* for debug purposes */
295  static const char * const names[]={
296    "STOP",
297    "SERVERGREET",
298    "LOGIN",
299    "STARTTLS",
300    "UPGRADETLS",
301    "SELECT",
302    "FETCH",
303    "LOGOUT",
304    /* LAST */
305  };
306#endif
307  struct imap_conn *imapc = &conn->proto.imapc;
308#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
309  if(imapc->state != newstate)
310    infof(conn->data, "IMAP %p state change from %s to %s\n",
311          imapc, names[imapc->state], names[newstate]);
312#endif
313  imapc->state = newstate;
314}
315
316static CURLcode imap_state_login(struct connectdata *conn)
317{
318  CURLcode result;
319  struct FTP *imap = conn->data->state.proto.imap;
320  const char *str;
321
322  str = getcmdid(conn);
323
324  /* send USER and password */
325  result = imapsendf(conn, str, "%s LOGIN %s %s", str,
326                     imap->user?imap->user:"",
327                     imap->passwd?imap->passwd:"");
328  if(result)
329    return result;
330
331  state(conn, IMAP_LOGIN);
332
333  return CURLE_OK;
334}
335
336#ifdef USE_SSL
337static void imap_to_imaps(struct connectdata *conn)
338{
339  conn->handler = &Curl_handler_imaps;
340}
341#else
342#define imap_to_imaps(x) Curl_nop_stmt
343#endif
344
345/* for STARTTLS responses */
346static CURLcode imap_state_starttls_resp(struct connectdata *conn,
347                                         int imapcode,
348                                         imapstate instate)
349{
350  CURLcode result = CURLE_OK;
351  struct SessionHandle *data = conn->data;
352  (void)instate; /* no use for this yet */
353
354  if(imapcode != 'O') {
355    failf(data, "STARTTLS denied. %c", imapcode);
356    result = CURLE_LOGIN_DENIED;
357  }
358  else {
359    if(data->state.used_interface == Curl_if_multi) {
360      state(conn, IMAP_UPGRADETLS);
361      return imap_state_upgrade_tls(conn);
362    }
363    else {
364      result = Curl_ssl_connect(conn, FIRSTSOCKET);
365      if(CURLE_OK == result) {
366        imap_to_imaps(conn);
367        result = imap_state_login(conn);
368      }
369    }
370  }
371  state(conn, IMAP_STOP);
372  return result;
373}
374
375static CURLcode imap_state_upgrade_tls(struct connectdata *conn)
376{
377  struct imap_conn *imapc = &conn->proto.imapc;
378  CURLcode result;
379
380  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
381
382  if(imapc->ssldone) {
383    imap_to_imaps(conn);
384    result = imap_state_login(conn);
385    state(conn, IMAP_STOP);
386  }
387
388  return result;
389}
390
391/* for LOGIN responses */
392static CURLcode imap_state_login_resp(struct connectdata *conn,
393                                      int imapcode,
394                                      imapstate instate)
395{
396  CURLcode result = CURLE_OK;
397  struct SessionHandle *data = conn->data;
398  (void)instate; /* no use for this yet */
399
400  if(imapcode != 'O') {
401    failf(data, "Access denied. %c", imapcode);
402    result = CURLE_LOGIN_DENIED;
403  }
404
405  state(conn, IMAP_STOP);
406  return result;
407}
408
409/* for the (first line of) FETCH BODY[TEXT] response */
410static CURLcode imap_state_fetch_resp(struct connectdata *conn,
411                                      int imapcode,
412                                      imapstate instate)
413{
414  CURLcode result = CURLE_OK;
415  struct SessionHandle *data = conn->data;
416  struct imap_conn *imapc = &conn->proto.imapc;
417  struct FTP *imap = data->state.proto.imap;
418  struct pingpong *pp = &imapc->pp;
419  const char *ptr = data->state.buffer;
420  (void)instate; /* no use for this yet */
421
422  if('*' != imapcode) {
423    Curl_pgrsSetDownloadSize(data, 0);
424    state(conn, IMAP_STOP);
425    return CURLE_OK;
426  }
427
428  /* Something like this comes "* 1 FETCH (BODY[TEXT] {2021}\r" */
429  while(*ptr && (*ptr != '{'))
430    ptr++;
431
432  if(*ptr == '{') {
433    curl_off_t filesize = curlx_strtoofft(ptr+1, NULL, 10);
434    if(filesize)
435      Curl_pgrsSetDownloadSize(data, filesize);
436
437    infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", filesize);
438
439    if(pp->cache) {
440      /* At this point there is a bunch of data in the header "cache" that is
441         actually body content, send it as body and then skip it. Do note
442         that there may even be additional "headers" after the body. */
443      size_t chunk = pp->cache_size;
444
445      if(chunk > (size_t)filesize)
446        /* the conversion from curl_off_t to size_t is always fine here */
447        chunk = (size_t)filesize;
448
449      result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
450      if(result)
451        return result;
452
453      filesize -= chunk;
454
455      /* we've now used parts of or the entire cache */
456      if(pp->cache_size > chunk) {
457        /* part of, move the trailing data to the start and reduce the size */
458        memmove(pp->cache, pp->cache+chunk,
459                pp->cache_size - chunk);
460        pp->cache_size -= chunk;
461      }
462      else {
463        /* cache is drained */
464        free(pp->cache);
465        pp->cache = NULL;
466        pp->cache_size = 0;
467      }
468    }
469
470    infof(data, "Filesize left: %" FORMAT_OFF_T "\n", filesize);
471
472    if(!filesize)
473      /* the entire data is already transferred! */
474      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
475    else
476      /* IMAP download */
477      Curl_setup_transfer(conn, FIRSTSOCKET, filesize, FALSE,
478                          imap->bytecountp, -1, NULL); /* no upload here */
479
480    data->req.maxdownload = filesize;
481  }
482  else
483    /* We don't know how to parse this line */
484    result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
485
486  state(conn, IMAP_STOP);
487  return result;
488}
489
490/* start the DO phase */
491static CURLcode imap_select(struct connectdata *conn)
492{
493  CURLcode result = CURLE_OK;
494  struct imap_conn *imapc = &conn->proto.imapc;
495  const char *str;
496
497  str = getcmdid(conn);
498
499  result = imapsendf(conn, str, "%s SELECT %s", str,
500                     imapc->mailbox?imapc->mailbox:"");
501  if(result)
502    return result;
503
504  state(conn, IMAP_SELECT);
505  return result;
506}
507
508static CURLcode imap_fetch(struct connectdata *conn)
509{
510  CURLcode result = CURLE_OK;
511  const char *str;
512
513  str = getcmdid(conn);
514
515  /* TODO: make this select the correct mail
516   * Use "1 body[text]" to get the full mail body of mail 1
517   */
518  result = imapsendf(conn, str, "%s FETCH 1 BODY[TEXT]", str);
519  if(result)
520    return result;
521
522  /*
523   * When issued, the server will respond with a single line similar to
524   * '* 1 FETCH (BODY[TEXT] {2021}'
525   *
526   * Identifying the fetch and how many bytes of contents we can expect. We
527   * must extract that number before continuing to "download as usual".
528   */
529
530  state(conn, IMAP_FETCH);
531  return result;
532}
533
534/* for SELECT responses */
535static CURLcode imap_state_select_resp(struct connectdata *conn,
536                                       int imapcode,
537                                       imapstate instate)
538{
539  CURLcode result = CURLE_OK;
540  struct SessionHandle *data = conn->data;
541  (void)instate; /* no use for this yet */
542
543  if(imapcode != 'O') {
544    failf(data, "Select failed");
545    result = CURLE_LOGIN_DENIED;
546  }
547  else
548    result = imap_fetch(conn);
549  return result;
550}
551
552static CURLcode imap_statemach_act(struct connectdata *conn)
553{
554  CURLcode result;
555  curl_socket_t sock = conn->sock[FIRSTSOCKET];
556  struct SessionHandle *data=conn->data;
557  int imapcode;
558  struct imap_conn *imapc = &conn->proto.imapc;
559  struct pingpong *pp = &imapc->pp;
560  size_t nread = 0;
561
562  /* busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
563  if(imapc->state == IMAP_UPGRADETLS)
564    return imap_state_upgrade_tls(conn);
565
566  if(pp->sendleft)
567    return Curl_pp_flushsend(pp);
568
569  /* we read a piece of response */
570  result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
571  if(result)
572    return result;
573
574  if(imapcode)
575  /* we have now received a full IMAP server response */
576  switch(imapc->state) {
577  case IMAP_SERVERGREET:
578    if(imapcode != 'O') {
579      failf(data, "Got unexpected imap-server response");
580      return CURLE_FTP_WEIRD_SERVER_REPLY;
581    }
582
583    if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
584      /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
585         to TLS connection now */
586      const char *str;
587
588      str = getcmdid(conn);
589      result = imapsendf(conn, str, "%s STARTTLS", str);
590      state(conn, IMAP_STARTTLS);
591    }
592    else
593      result = imap_state_login(conn);
594    if(result)
595      return result;
596    break;
597
598  case IMAP_LOGIN:
599    result = imap_state_login_resp(conn, imapcode, imapc->state);
600    break;
601
602  case IMAP_STARTTLS:
603    result = imap_state_starttls_resp(conn, imapcode, imapc->state);
604    break;
605
606  case IMAP_FETCH:
607    result = imap_state_fetch_resp(conn, imapcode, imapc->state);
608    break;
609
610  case IMAP_SELECT:
611    result = imap_state_select_resp(conn, imapcode, imapc->state);
612    break;
613
614  case IMAP_LOGOUT:
615    /* fallthrough, just stop! */
616  default:
617    /* internal error */
618    state(conn, IMAP_STOP);
619    break;
620  }
621
622  return result;
623}
624
625/* called repeatedly until done from multi.c */
626static CURLcode imap_multi_statemach(struct connectdata *conn,
627                                         bool *done)
628{
629  struct imap_conn *imapc = &conn->proto.imapc;
630  CURLcode result;
631
632  if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
633    result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
634  else
635    result = Curl_pp_multi_statemach(&imapc->pp);
636
637  *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
638
639  return result;
640}
641
642static CURLcode imap_easy_statemach(struct connectdata *conn)
643{
644  struct imap_conn *imapc = &conn->proto.imapc;
645  struct pingpong *pp = &imapc->pp;
646  CURLcode result = CURLE_OK;
647
648  while(imapc->state != IMAP_STOP) {
649    result = Curl_pp_easy_statemach(pp);
650    if(result)
651      break;
652  }
653
654  return result;
655}
656
657/*
658 * Allocate and initialize the struct IMAP for the current SessionHandle.  If
659 * need be.
660 */
661static CURLcode imap_init(struct connectdata *conn)
662{
663  struct SessionHandle *data = conn->data;
664  struct FTP *imap = data->state.proto.imap;
665  if(!imap) {
666    imap = data->state.proto.imap = calloc(sizeof(struct FTP), 1);
667    if(!imap)
668      return CURLE_OUT_OF_MEMORY;
669  }
670
671  /* get some initial data into the imap struct */
672  imap->bytecountp = &data->req.bytecount;
673
674  /* No need to duplicate user+password, the connectdata struct won't change
675     during a session, but we re-init them here since on subsequent inits
676     since the conn struct may have changed or been replaced.
677  */
678  imap->user = conn->user;
679  imap->passwd = conn->passwd;
680
681  return CURLE_OK;
682}
683
684/*
685 * imap_connect() should do everything that is to be considered a part of
686 * the connection phase.
687 *
688 * The variable 'done' points to will be TRUE if the protocol-layer connect
689 * phase is done when this function returns, or FALSE is not. When called as
690 * a part of the easy interface, it will always be TRUE.
691 */
692static CURLcode imap_connect(struct connectdata *conn,
693                                 bool *done) /* see description above */
694{
695  CURLcode result;
696  struct imap_conn *imapc = &conn->proto.imapc;
697  struct SessionHandle *data=conn->data;
698  struct pingpong *pp = &imapc->pp;
699
700  *done = FALSE; /* default to not done yet */
701
702  /* If there already is a protocol-specific struct allocated for this
703     sessionhandle, deal with it */
704  Curl_reset_reqproto(conn);
705
706  result = imap_init(conn);
707  if(CURLE_OK != result)
708    return result;
709
710  /* We always support persistent connections on imap */
711  conn->bits.close = FALSE;
712
713  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
714  pp->statemach_act = imap_statemach_act;
715  pp->endofresp = imap_endofresp;
716  pp->conn = conn;
717
718  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
719    /* for IMAP over HTTP proxy */
720    struct HTTP http_proxy;
721    struct FTP *imap_save;
722
723    /* BLOCKING */
724    /* We want "seamless" IMAP operations through HTTP proxy tunnel */
725
726    /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
727     * conn->proto.http; we want IMAP through HTTP and we have to change the
728     * member temporarily for connecting to the HTTP proxy. After
729     * Curl_proxyCONNECT we have to set back the member to the original struct
730     * IMAP pointer
731     */
732    imap_save = data->state.proto.imap;
733    memset(&http_proxy, 0, sizeof(http_proxy));
734    data->state.proto.http = &http_proxy;
735
736    result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
737                               conn->host.name, conn->remote_port);
738
739    data->state.proto.imap = imap_save;
740
741    if(CURLE_OK != result)
742      return result;
743  }
744
745  if((conn->handler->flags & PROTOPT_SSL) &&
746     data->state.used_interface != Curl_if_multi) {
747    /* BLOCKING */
748    result = Curl_ssl_connect(conn, FIRSTSOCKET);
749    if(result)
750      return result;
751  }
752
753  Curl_pp_init(pp); /* init generic pingpong data */
754
755  /* When we connect, we start in the state where we await the server greeting
756     response */
757  state(conn, IMAP_SERVERGREET);
758  imapc->idstr = "*"; /* we start off waiting for a '*' response */
759
760  if(data->state.used_interface == Curl_if_multi)
761    result = imap_multi_statemach(conn, done);
762  else {
763    result = imap_easy_statemach(conn);
764    if(!result)
765      *done = TRUE;
766  }
767
768  return result;
769}
770
771/***********************************************************************
772 *
773 * imap_done()
774 *
775 * The DONE function. This does what needs to be done after a single DO has
776 * performed.
777 *
778 * Input argument is already checked for validity.
779 */
780static CURLcode imap_done(struct connectdata *conn, CURLcode status,
781                          bool premature)
782{
783  struct SessionHandle *data = conn->data;
784  struct FTP *imap = data->state.proto.imap;
785  CURLcode result=CURLE_OK;
786  (void)premature;
787
788  if(!imap)
789    /* When the easy handle is removed from the multi while libcurl is still
790     * trying to resolve the host name, it seems that the imap struct is not
791     * yet initialized, but the removal action calls Curl_done() which calls
792     * this function. So we simply return success if no imap pointer is set.
793     */
794    return CURLE_OK;
795
796  if(status) {
797    conn->bits.close = TRUE; /* marked for closure */
798    result = status;      /* use the already set error code */
799  }
800
801  /* clear these for next connection */
802  imap->transfer = FTPTRANSFER_BODY;
803
804  return result;
805}
806
807/***********************************************************************
808 *
809 * imap_perform()
810 *
811 * This is the actual DO function for IMAP. Get a file/directory according to
812 * the options previously setup.
813 */
814
815static
816CURLcode imap_perform(struct connectdata *conn,
817                     bool *connected,  /* connect status after PASV / PORT */
818                     bool *dophase_done)
819{
820  /* this is IMAP and no proxy */
821  CURLcode result=CURLE_OK;
822
823  DEBUGF(infof(conn->data, "DO phase starts\n"));
824
825  if(conn->data->set.opt_no_body) {
826    /* requested no body means no transfer... */
827    struct FTP *imap = conn->data->state.proto.imap;
828    imap->transfer = FTPTRANSFER_INFO;
829  }
830
831  *dophase_done = FALSE; /* not done yet */
832
833  /* start the first command in the DO phase */
834  result = imap_select(conn);
835  if(result)
836    return result;
837
838  /* run the state-machine */
839  if(conn->data->state.used_interface == Curl_if_multi)
840    result = imap_multi_statemach(conn, dophase_done);
841  else {
842    result = imap_easy_statemach(conn);
843    *dophase_done = TRUE; /* with the easy interface we are done here */
844  }
845  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
846
847  if(*dophase_done)
848    DEBUGF(infof(conn->data, "DO phase is complete\n"));
849
850  return result;
851}
852
853/***********************************************************************
854 *
855 * imap_do()
856 *
857 * This function is registered as 'curl_do' function. It decodes the path
858 * parts etc as a wrapper to the actual DO function (imap_perform).
859 *
860 * The input argument is already checked for validity.
861 */
862static CURLcode imap_do(struct connectdata *conn, bool *done)
863{
864  CURLcode retcode = CURLE_OK;
865
866  *done = FALSE; /* default to false */
867
868  /*
869    Since connections can be re-used between SessionHandles, this might be a
870    connection already existing but on a fresh SessionHandle struct so we must
871    make sure we have a good 'struct IMAP' to play with. For new connections,
872    the struct IMAP is allocated and setup in the imap_connect() function.
873  */
874  Curl_reset_reqproto(conn);
875  retcode = imap_init(conn);
876  if(retcode)
877    return retcode;
878
879  retcode = imap_parse_url_path(conn);
880  if(retcode)
881    return retcode;
882
883  retcode = imap_regular_transfer(conn, done);
884
885  return retcode;
886}
887
888/***********************************************************************
889 *
890 * imap_logout()
891 *
892 * This should be called before calling sclose().  We should then wait for the
893 * response from the server before returning. The calling code should then try
894 * to close the connection.
895 *
896 */
897static CURLcode imap_logout(struct connectdata *conn)
898{
899  CURLcode result = CURLE_OK;
900  const char *str;
901
902  str = getcmdid(conn);
903
904  result = imapsendf(conn, str, "%s LOGOUT", str, NULL);
905  if(result)
906    return result;
907  state(conn, IMAP_LOGOUT);
908
909  result = imap_easy_statemach(conn);
910
911  return result;
912}
913
914/***********************************************************************
915 *
916 * imap_disconnect()
917 *
918 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
919 * resources. BLOCKING.
920 */
921static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
922{
923  struct imap_conn *imapc= &conn->proto.imapc;
924
925  /* The IMAP session may or may not have been allocated/setup at this
926     point! */
927  if(!dead_connection && imapc->pp.conn)
928    (void)imap_logout(conn); /* ignore errors on the LOGOUT */
929
930  Curl_pp_disconnect(&imapc->pp);
931
932  Curl_safefree(imapc->mailbox);
933
934  return CURLE_OK;
935}
936
937/***********************************************************************
938 *
939 * imap_parse_url_path()
940 *
941 * Parse the URL path into separate path components.
942 *
943 */
944static CURLcode imap_parse_url_path(struct connectdata *conn)
945{
946  /* the imap struct is already inited in imap_connect() */
947  struct imap_conn *imapc = &conn->proto.imapc;
948  struct SessionHandle *data = conn->data;
949  const char *path = data->state.path;
950  int len;
951
952  if(!*path)
953    path = "INBOX";
954
955  /* url decode the path and use this mailbox */
956  imapc->mailbox = curl_easy_unescape(data, path, 0, &len);
957  if(!imapc->mailbox)
958    return CURLE_OUT_OF_MEMORY;
959
960  return CURLE_OK;
961}
962
963/* call this when the DO phase has completed */
964static CURLcode imap_dophase_done(struct connectdata *conn,
965                                  bool connected)
966{
967  struct FTP *imap = conn->data->state.proto.imap;
968  (void)connected;
969
970  if(imap->transfer != FTPTRANSFER_BODY)
971    /* no data to transfer */
972    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
973
974  return CURLE_OK;
975}
976
977/* called from multi.c while DOing */
978static CURLcode imap_doing(struct connectdata *conn,
979                               bool *dophase_done)
980{
981  CURLcode result;
982  result = imap_multi_statemach(conn, dophase_done);
983
984  if(*dophase_done) {
985    result = imap_dophase_done(conn, FALSE /* not connected */);
986
987    DEBUGF(infof(conn->data, "DO phase is complete\n"));
988  }
989  return result;
990}
991
992/***********************************************************************
993 *
994 * imap_regular_transfer()
995 *
996 * The input argument is already checked for validity.
997 *
998 * Performs all commands done before a regular transfer between a local and a
999 * remote host.
1000 *
1001 */
1002static
1003CURLcode imap_regular_transfer(struct connectdata *conn,
1004                              bool *dophase_done)
1005{
1006  CURLcode result=CURLE_OK;
1007  bool connected=FALSE;
1008  struct SessionHandle *data = conn->data;
1009  data->req.size = -1; /* make sure this is unknown at this point */
1010
1011  Curl_pgrsSetUploadCounter(data, 0);
1012  Curl_pgrsSetDownloadCounter(data, 0);
1013  Curl_pgrsSetUploadSize(data, 0);
1014  Curl_pgrsSetDownloadSize(data, 0);
1015
1016  result = imap_perform(conn,
1017                        &connected, /* have we connected after PASV/PORT */
1018                        dophase_done); /* all commands in the DO-phase done? */
1019
1020  if(CURLE_OK == result) {
1021
1022    if(!*dophase_done)
1023      /* the DO phase has not completed yet */
1024      return CURLE_OK;
1025
1026    result = imap_dophase_done(conn, connected);
1027    if(result)
1028      return result;
1029  }
1030
1031  return result;
1032}
1033
1034static CURLcode imap_setup_connection(struct connectdata * conn)
1035{
1036  struct SessionHandle *data = conn->data;
1037
1038  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1039    /* Unless we have asked to tunnel imap operations through the proxy, we
1040       switch and use HTTP operations only */
1041#ifndef CURL_DISABLE_HTTP
1042    if(conn->handler == &Curl_handler_imap)
1043      conn->handler = &Curl_handler_imap_proxy;
1044    else {
1045#ifdef USE_SSL
1046      conn->handler = &Curl_handler_imaps_proxy;
1047#else
1048      failf(data, "IMAPS not supported!");
1049      return CURLE_UNSUPPORTED_PROTOCOL;
1050#endif
1051    }
1052    /*
1053     * We explicitly mark this connection as persistent here as we're doing
1054     * IMAP over HTTP and thus we accidentally avoid setting this value
1055     * otherwise.
1056     */
1057    conn->bits.close = FALSE;
1058#else
1059    failf(data, "IMAP over http proxy requires HTTP support built-in!");
1060    return CURLE_UNSUPPORTED_PROTOCOL;
1061#endif
1062  }
1063
1064  data->state.path++;   /* don't include the initial slash */
1065
1066  return CURLE_OK;
1067}
1068
1069#endif /* CURL_DISABLE_IMAP */
1070