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