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