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