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