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