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