build: fix circular header inclusion with other packages
[platform/upstream/curl.git] / lib / imap.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, 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  * RFC2195 CRAM-MD5 authentication
22  * RFC2222 Simple Authentication and Security Layer (SASL)
23  * RFC2595 Using TLS with IMAP, POP3 and ACAP
24  * RFC2831 DIGEST-MD5 authentication
25  * RFC3501 IMAPv4 protocol
26  * RFC4616 PLAIN authentication
27  * RFC5092 IMAP URL Scheme
28  *
29  ***************************************************************************/
30
31 #include "curl_setup.h"
32
33 #ifndef CURL_DISABLE_IMAP
34
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
37 #endif
38 #ifdef HAVE_ARPA_INET_H
39 #include <arpa/inet.h>
40 #endif
41 #ifdef HAVE_UTSNAME_H
42 #include <sys/utsname.h>
43 #endif
44 #ifdef HAVE_NETDB_H
45 #include <netdb.h>
46 #endif
47 #ifdef __VMS
48 #include <in.h>
49 #include <inet.h>
50 #endif
51
52 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
53 #undef in_addr_t
54 #define in_addr_t unsigned long
55 #endif
56
57 #include <curl/curl.h>
58 #include "urldata.h"
59 #include "sendf.h"
60 #include "if2ip.h"
61 #include "hostip.h"
62 #include "progress.h"
63 #include "transfer.h"
64 #include "escape.h"
65 #include "http.h" /* for HTTP proxy tunnel stuff */
66 #include "socks.h"
67 #include "imap.h"
68
69 #include "strtoofft.h"
70 #include "strequal.h"
71 #include "sslgen.h"
72 #include "connect.h"
73 #include "strerror.h"
74 #include "select.h"
75 #include "multiif.h"
76 #include "url.h"
77 #include "rawstr.h"
78 #include "curl_sasl.h"
79
80 #define _MPRINTF_REPLACE /* use our functions only */
81 #include <curl/mprintf.h>
82
83 #include "curl_memory.h"
84 /* The last #include file should be: */
85 #include "memdebug.h"
86
87 /* Local API functions */
88 static CURLcode imap_parse_url_path(struct connectdata *conn);
89 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
90 static CURLcode imap_do(struct connectdata *conn, bool *done);
91 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
92                           bool premature);
93 static CURLcode imap_connect(struct connectdata *conn, bool *done);
94 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
95 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
96 static int imap_getsock(struct connectdata *conn,
97                         curl_socket_t *socks,
98                         int numsocks);
99 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode imap_setup_connection(struct connectdata *conn);
101 static CURLcode imap_state_upgrade_tls(struct connectdata *conn);
102
103 /*
104  * IMAP protocol handler.
105  */
106
107 const struct Curl_handler Curl_handler_imap = {
108   "IMAP",                           /* scheme */
109   imap_setup_connection,            /* setup_connection */
110   imap_do,                          /* do_it */
111   imap_done,                        /* done */
112   ZERO_NULL,                        /* do_more */
113   imap_connect,                     /* connect_it */
114   imap_multi_statemach,             /* connecting */
115   imap_doing,                       /* doing */
116   imap_getsock,                     /* proto_getsock */
117   imap_getsock,                     /* doing_getsock */
118   ZERO_NULL,                        /* domore_getsock */
119   ZERO_NULL,                        /* perform_getsock */
120   imap_disconnect,                  /* disconnect */
121   ZERO_NULL,                        /* readwrite */
122   PORT_IMAP,                        /* defport */
123   CURLPROTO_IMAP,                   /* protocol */
124   PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
125   | PROTOPT_NOURLQUERY              /* flags */
126 };
127
128
129 #ifdef USE_SSL
130 /*
131  * IMAPS protocol handler.
132  */
133
134 const struct Curl_handler Curl_handler_imaps = {
135   "IMAPS",                          /* scheme */
136   imap_setup_connection,            /* setup_connection */
137   imap_do,                          /* do_it */
138   imap_done,                        /* done */
139   ZERO_NULL,                        /* do_more */
140   imap_connect,                     /* connect_it */
141   imap_multi_statemach,             /* connecting */
142   imap_doing,                       /* doing */
143   imap_getsock,                     /* proto_getsock */
144   imap_getsock,                     /* doing_getsock */
145   ZERO_NULL,                        /* domore_getsock */
146   ZERO_NULL,                        /* perform_getsock */
147   imap_disconnect,                  /* disconnect */
148   ZERO_NULL,                        /* readwrite */
149   PORT_IMAPS,                       /* defport */
150   CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
151   PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
152   | PROTOPT_NOURLQUERY              /* flags */
153 };
154 #endif
155
156 #ifndef CURL_DISABLE_HTTP
157 /*
158  * HTTP-proxyed IMAP protocol handler.
159  */
160
161 static const struct Curl_handler Curl_handler_imap_proxy = {
162   "IMAP",                               /* scheme */
163   ZERO_NULL,                            /* setup_connection */
164   Curl_http,                            /* do_it */
165   Curl_http_done,                       /* done */
166   ZERO_NULL,                            /* do_more */
167   ZERO_NULL,                            /* connect_it */
168   ZERO_NULL,                            /* connecting */
169   ZERO_NULL,                            /* doing */
170   ZERO_NULL,                            /* proto_getsock */
171   ZERO_NULL,                            /* doing_getsock */
172   ZERO_NULL,                            /* domore_getsock */
173   ZERO_NULL,                            /* perform_getsock */
174   ZERO_NULL,                            /* disconnect */
175   ZERO_NULL,                            /* readwrite */
176   PORT_IMAP,                            /* defport */
177   CURLPROTO_HTTP,                       /* protocol */
178   PROTOPT_NONE                          /* flags */
179 };
180
181
182 #ifdef USE_SSL
183 /*
184  * HTTP-proxyed IMAPS protocol handler.
185  */
186
187 static const struct Curl_handler Curl_handler_imaps_proxy = {
188   "IMAPS",                              /* scheme */
189   ZERO_NULL,                            /* setup_connection */
190   Curl_http,                            /* do_it */
191   Curl_http_done,                       /* done */
192   ZERO_NULL,                            /* do_more */
193   ZERO_NULL,                            /* connect_it */
194   ZERO_NULL,                            /* connecting */
195   ZERO_NULL,                            /* doing */
196   ZERO_NULL,                            /* proto_getsock */
197   ZERO_NULL,                            /* doing_getsock */
198   ZERO_NULL,                            /* domore_getsock */
199   ZERO_NULL,                            /* perform_getsock */
200   ZERO_NULL,                            /* disconnect */
201   ZERO_NULL,                            /* readwrite */
202   PORT_IMAPS,                           /* defport */
203   CURLPROTO_HTTP,                       /* protocol */
204   PROTOPT_NONE                          /* flags */
205 };
206 #endif
207 #endif
208
209 /***********************************************************************
210  *
211  * imap_sendf()
212  *
213  * Sends the formated string as an IMAP command to the server.
214  *
215  * Designed to never block.
216  */
217 static CURLcode imap_sendf(struct connectdata *conn,
218                            const char *idstr, /* command id to wait for */
219                            const char *fmt, ...)
220 {
221   CURLcode res;
222   struct imap_conn *imapc = &conn->proto.imapc;
223   va_list ap;
224   va_start(ap, fmt);
225
226   imapc->idstr = idstr;
227
228   res = Curl_pp_vsendf(&imapc->pp, fmt, ap);
229
230   va_end(ap);
231
232   return res;
233 }
234
235 static const char *getcmdid(struct connectdata *conn)
236 {
237   static const char * const ids[]= {
238     "A",
239     "B",
240     "C",
241     "D"
242   };
243
244   struct imap_conn *imapc = &conn->proto.imapc;
245
246   /* Get the next id, but wrap at end of table */
247   imapc->cmdid = (int)((imapc->cmdid + 1) % (sizeof(ids) / sizeof(ids[0])));
248
249   return ids[imapc->cmdid];
250 }
251
252 /***********************************************************************
253  *
254  * imap_atom()
255  *
256  * Checks the input string for characters that need escaping and returns an
257  * atom ready for sending to the server.
258  *
259  * The returned string needs to be freed.
260  *
261  */
262 static char* imap_atom(const char* str)
263 {
264   const char *p1;
265   char *p2;
266   size_t backsp_count = 0;
267   size_t quote_count = 0;
268   bool space_exists = FALSE;
269   size_t newlen = 0;
270   char *newstr = NULL;
271
272   if(!str)
273     return NULL;
274
275   /* Count any unescapped characters */
276   p1 = str;
277   while(*p1) {
278     if(*p1 == '\\')
279       backsp_count++;
280     else if(*p1 == '"')
281       quote_count++;
282     else if(*p1 == ' ')
283       space_exists = TRUE;
284
285     p1++;
286   }
287
288   /* Does the input contain any unescapped characters? */
289   if(!backsp_count && !quote_count && !space_exists)
290     return strdup(str);
291
292   /* Calculate the new string length */
293   newlen = strlen(str) + backsp_count + quote_count + (space_exists ? 2 : 0);
294
295   /* Allocate the new string */
296   newstr = (char *) malloc((newlen + 1) * sizeof(char));
297   if(!newstr)
298     return NULL;
299
300   /* Surround the string in quotes if necessary */
301   p2 = newstr;
302   if(space_exists) {
303     newstr[0] = '"';
304     newstr[newlen - 1] = '"';
305     p2++;
306   }
307
308   /* Copy the string, escaping backslash and quote characters along the way */
309   p1 = str;
310   while(*p1) {
311     if(*p1 == '\\' || *p1 == '"') {
312       *p2 = '\\';
313       p2++;
314     }
315
316    *p2 = *p1;
317
318     p1++;
319     p2++;
320   }
321
322   /* Terminate the string */
323   newstr[newlen] = '\0';
324
325   return newstr;
326 }
327
328 /* Function that checks for an ending imap status code at the start of the
329    given string but also detects the supported mechanisms from the CAPABILITY
330    response. */
331 static int imap_endofresp(struct pingpong *pp, int *resp)
332 {
333   char *line = pp->linestart_resp;
334   size_t len = pp->nread_resp;
335   struct imap_conn *imapc = &pp->conn->proto.imapc;
336   const char *id = imapc->idstr;
337   size_t id_len = strlen(id);
338   size_t wordlen;
339
340   /* Do we have a generic command response? */
341   if(len >= id_len + 3) {
342     if(!memcmp(id, line, id_len) && line[id_len] == ' ') {
343       *resp = line[id_len + 1]; /* O, N or B */
344       return TRUE;
345     }
346   }
347
348   /* Do we have a generic continuation response? */
349   if((len == 3 && !memcmp("+", line, 1)) ||
350      (len >= 2 && !memcmp("+ ", line, 2))) {
351     *resp = '+';
352     return TRUE;
353   }
354
355   /* Are we processing CAPABILITY command responses? */
356   if(imapc->state == IMAP_CAPABILITY) {
357     /* Do we have a valid response? */
358     if(len >= 2 && !memcmp("* ", line, 2)) {
359       line += 2;
360       len -= 2;
361
362       /* Loop through the data line */
363       for(;;) {
364         while(len &&
365               (*line == ' ' || *line == '\t' ||
366                *line == '\r' || *line == '\n')) {
367
368           if(*line == '\n')
369             return FALSE;
370
371           line++;
372           len--;
373         }
374
375         if(!len)
376           break;
377
378         /* Extract the word */
379         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
380               line[wordlen] != '\t' && line[wordlen] != '\r' &&
381               line[wordlen] != '\n';)
382           wordlen++;
383
384         /* Do we have an AUTH capability? */
385         if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
386           line += 5;
387           len -= 5;
388           wordlen -= 5;
389
390           /* Test the word for a matching authentication mechanism */
391           if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
392             imapc->authmechs |= SASL_MECH_LOGIN;
393           if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
394             imapc->authmechs |= SASL_MECH_PLAIN;
395           else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
396             imapc->authmechs |= SASL_MECH_CRAM_MD5;
397           else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
398             imapc->authmechs |= SASL_MECH_DIGEST_MD5;
399           else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
400             imapc->authmechs |= SASL_MECH_GSSAPI;
401           else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
402             imapc->authmechs |= SASL_MECH_EXTERNAL;
403           else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
404             imapc->authmechs |= SASL_MECH_NTLM;
405         }
406
407         line += wordlen;
408         len -= wordlen;
409       }
410     }
411   }
412
413   /* Are we processing FETCH command responses? */
414   if(imapc->state == IMAP_FETCH) {
415     /* Do we have a valid response? */
416     if(len >= 2 && !memcmp("* ", line, 2)) {
417       *resp = '*';
418       return TRUE;
419     }
420   }
421
422   return FALSE; /* Nothing for us */
423 }
424
425 /* This is the ONLY way to change IMAP state! */
426 static void state(struct connectdata *conn,
427                   imapstate newstate)
428 {
429   struct imap_conn *imapc = &conn->proto.imapc;
430 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
431   /* for debug purposes */
432   static const char * const names[]={
433     "STOP",
434     "SERVERGREET",
435     "STARTTLS",
436     "UPGRADETLS",
437     "CAPABILITY",
438     "AUTHENTICATE_PLAIN",
439     "AUTHENTICATE_LOGIN",
440     "AUTHENTICATE_LOGIN_PASSWD",
441     "AUTHENTICATE_CRAMMD5",
442     "AUTHENTICATE_DIGESTMD5",
443     "AUTHENTICATE_DIGESTMD5_RESP",
444     "AUTHENTICATE_NTLM",
445     "AUTHENTICATE_NTLM_TYPE2MSG",
446     "AUTHENTICATE",
447     "LOGIN",
448     "SELECT",
449     "FETCH",
450     "LOGOUT",
451     /* LAST */
452   };
453
454   if(imapc->state != newstate)
455     infof(conn->data, "IMAP %p state change from %s to %s\n",
456           imapc, names[imapc->state], names[newstate]);
457 #endif
458
459   imapc->state = newstate;
460 }
461
462 static CURLcode imap_state_capability(struct connectdata *conn)
463 {
464   CURLcode result = CURLE_OK;
465   struct imap_conn *imapc = &conn->proto.imapc;
466   const char *str;
467
468   imapc->authmechs = 0;         /* No known authentication mechanisms yet */
469   imapc->authused = 0;          /* Clear the authentication mechanism used */
470
471   /* Check we have a username and password to authenticate with and end the
472      connect phase if we don't */
473   if(!conn->bits.user_passwd) {
474     state(conn, IMAP_STOP);
475
476     return result;
477   }
478
479   str = getcmdid(conn);
480
481   /* Send the CAPABILITY command */
482   result = imap_sendf(conn, str, "%s CAPABILITY", str);
483
484   if(result)
485     return result;
486
487   state(conn, IMAP_CAPABILITY);
488
489   return CURLE_OK;
490 }
491
492 static CURLcode imap_state_login(struct connectdata *conn)
493 {
494   CURLcode result;
495   struct FTP *imap = conn->data->state.proto.imap;
496   const char *str = getcmdid(conn);
497   char *user = imap_atom(imap->user);
498   char *passwd = imap_atom(imap->passwd);
499
500   /* send USER and password */
501   result = imap_sendf(conn, str, "%s LOGIN %s %s", str,
502                       user ? user : "", passwd ? passwd : "");
503
504   Curl_safefree(user);
505   Curl_safefree(passwd);
506
507   if(result)
508     return result;
509
510   state(conn, IMAP_LOGIN);
511
512   return CURLE_OK;
513 }
514
515 static CURLcode imap_authenticate(struct connectdata *conn)
516 {
517   CURLcode result = CURLE_OK;
518   struct imap_conn *imapc = &conn->proto.imapc;
519   const char *mech = NULL;
520   imapstate authstate = IMAP_STOP;
521
522   /* Check supported authentication mechanisms by decreasing order of
523      security */
524 #ifndef CURL_DISABLE_CRYPTO_AUTH
525   if(imapc->authmechs & SASL_MECH_DIGEST_MD5) {
526     mech = "DIGEST-MD5";
527     authstate = IMAP_AUTHENTICATE_DIGESTMD5;
528     imapc->authused = SASL_MECH_DIGEST_MD5;
529   }
530   else if(imapc->authmechs & SASL_MECH_CRAM_MD5) {
531     mech = "CRAM-MD5";
532     authstate = IMAP_AUTHENTICATE_CRAMMD5;
533     imapc->authused = SASL_MECH_CRAM_MD5;
534   }
535   else
536 #endif
537 #ifdef USE_NTLM
538   if(imapc->authmechs & SASL_MECH_NTLM) {
539     mech = "NTLM";
540     authstate = IMAP_AUTHENTICATE_NTLM;
541     imapc->authused = SASL_MECH_NTLM;
542   }
543   else
544 #endif
545   if(imapc->authmechs & SASL_MECH_LOGIN) {
546     mech = "LOGIN";
547     authstate = IMAP_AUTHENTICATE_LOGIN;
548     imapc->authused = SASL_MECH_LOGIN;
549   }
550   else if(imapc->authmechs & SASL_MECH_PLAIN) {
551     mech = "PLAIN";
552     authstate = IMAP_AUTHENTICATE_PLAIN;
553     imapc->authused = SASL_MECH_PLAIN;
554   }
555   else {
556     infof(conn->data, "No known authentication mechanisms supported!\n");
557     result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
558   }
559
560   if(!result) {
561     const char *str = getcmdid(conn);
562
563     result = imap_sendf(conn, str, "%s AUTHENTICATE %s", str, mech);
564
565     if(!result)
566       state(conn, authstate);
567   }
568
569   return result;
570 }
571
572 /* For the IMAP "protocol connect" and "doing" phases only */
573 static int imap_getsock(struct connectdata *conn,
574                         curl_socket_t *socks,
575                         int numsocks)
576 {
577   return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
578 }
579
580 #ifdef USE_SSL
581 static void imap_to_imaps(struct connectdata *conn)
582 {
583   conn->handler = &Curl_handler_imaps;
584 }
585 #else
586 #define imap_to_imaps(x) Curl_nop_stmt
587 #endif
588
589 /* For the initial server greeting */
590 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
591                                             int imapcode,
592                                             imapstate instate)
593 {
594   CURLcode result = CURLE_OK;
595   struct SessionHandle *data = conn->data;
596
597   (void)instate; /* no use for this yet */
598
599   if(imapcode != 'O') {
600     failf(data, "Got unexpected imap-server response");
601     return CURLE_FTP_WEIRD_SERVER_REPLY;
602   }
603
604   if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
605     /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
606        to TLS connection now */
607     const char *str = getcmdid(conn);
608     result = imap_sendf(conn, str, "%s STARTTLS", str);
609     state(conn, IMAP_STARTTLS);
610   }
611   else
612     result = imap_state_capability(conn);
613
614   return result;
615 }
616
617 /* For STARTTLS responses */
618 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
619                                          int imapcode,
620                                          imapstate instate)
621 {
622   CURLcode result = CURLE_OK;
623   struct SessionHandle *data = conn->data;
624
625   (void)instate; /* no use for this yet */
626
627   if(imapcode != 'O') {
628     if(data->set.use_ssl != CURLUSESSL_TRY) {
629       failf(data, "STARTTLS denied. %c", imapcode);
630       result = CURLE_USE_SSL_FAILED;
631     }
632     else
633       result = imap_state_capability(conn);
634   }
635   else {
636     if(data->state.used_interface == Curl_if_multi) {
637       state(conn, IMAP_UPGRADETLS);
638       result = imap_state_upgrade_tls(conn);
639     }
640     else {
641       result = Curl_ssl_connect(conn, FIRSTSOCKET);
642       if(CURLE_OK == result) {
643         imap_to_imaps(conn);
644         result = imap_state_capability(conn);
645       }
646     }
647   }
648
649   return result;
650 }
651
652 static CURLcode imap_state_upgrade_tls(struct connectdata *conn)
653 {
654   struct imap_conn *imapc = &conn->proto.imapc;
655   CURLcode result;
656
657   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
658
659   if(imapc->ssldone) {
660     imap_to_imaps(conn);
661     result = imap_state_capability(conn);
662   }
663
664   return result;
665 }
666
667 /* For CAPABILITY responses */
668 static CURLcode imap_state_capability_resp(struct connectdata *conn,
669                                            int imapcode,
670                                            imapstate instate)
671 {
672   CURLcode result = CURLE_OK;
673   struct imap_conn *imapc = &conn->proto.imapc;
674
675   (void)instate; /* no use for this yet */
676
677   if(imapcode == 'O' && imapc->authmechs)
678     result = imap_authenticate(conn);
679   else
680     result = imap_state_login(conn);
681
682   return result;
683 }
684
685 /* For AUTHENTICATE PLAIN responses */
686 static CURLcode imap_state_auth_plain_resp(struct connectdata *conn,
687                                            int imapcode,
688                                            imapstate instate)
689 {
690   CURLcode result = CURLE_OK;
691   struct SessionHandle *data = conn->data;
692   size_t len = 0;
693   char *plainauth = NULL;
694
695   (void)instate; /* no use for this yet */
696
697   if(imapcode != '+') {
698     failf(data, "Access denied. %c", imapcode);
699     result = CURLE_LOGIN_DENIED;
700   }
701   else {
702     /* Create the authorisation message */
703     result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
704                                             &plainauth, &len);
705
706     /* Send the message */
707     if(!result) {
708       if(plainauth) {
709         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", plainauth);
710
711         if(!result)
712           state(conn, IMAP_AUTHENTICATE);
713       }
714
715       Curl_safefree(plainauth);
716     }
717   }
718
719   return result;
720 }
721
722 /* For AUTHENTICATE LOGIN responses */
723 static CURLcode imap_state_auth_login_resp(struct connectdata *conn,
724                                            int imapcode,
725                                            imapstate instate)
726 {
727   CURLcode result = CURLE_OK;
728   struct SessionHandle *data = conn->data;
729   size_t len = 0;
730   char *authuser = NULL;
731
732   (void)instate; /* no use for this yet */
733
734   if(imapcode != '+') {
735     failf(data, "Access denied: %d", imapcode);
736     result = CURLE_LOGIN_DENIED;
737   }
738   else {
739     /* Create the user message */
740     result = Curl_sasl_create_login_message(data, conn->user,
741                                             &authuser, &len);
742
743     /* Send the user */
744     if(!result) {
745       if(authuser) {
746         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authuser);
747
748         if(!result)
749           state(conn, IMAP_AUTHENTICATE_LOGIN_PASSWD);
750       }
751
752       Curl_safefree(authuser);
753     }
754   }
755
756   return result;
757 }
758
759 /* For AUTHENTICATE LOGIN user entry responses */
760 static CURLcode imap_state_auth_login_password_resp(struct connectdata *conn,
761                                                     int imapcode,
762                                                     imapstate instate)
763 {
764   CURLcode result = CURLE_OK;
765   struct SessionHandle *data = conn->data;
766   size_t len = 0;
767   char *authpasswd = NULL;
768
769   (void)instate; /* no use for this yet */
770
771   if(imapcode != '+') {
772     failf(data, "Access denied: %d", imapcode);
773     result = CURLE_LOGIN_DENIED;
774   }
775   else {
776     /* Create the password message */
777     result = Curl_sasl_create_login_message(data, conn->passwd,
778                                             &authpasswd, &len);
779
780     /* Send the password */
781     if(!result) {
782       if(authpasswd) {
783         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", authpasswd);
784
785         if(!result)
786           state(conn, IMAP_AUTHENTICATE);
787       }
788
789       Curl_safefree(authpasswd);
790     }
791   }
792
793   return result;
794 }
795
796 #ifndef CURL_DISABLE_CRYPTO_AUTH
797 /* For AUTHENTICATE CRAM-MD5 responses */
798 static CURLcode imap_state_auth_cram_resp(struct connectdata *conn,
799                                           int imapcode,
800                                           imapstate instate)
801 {
802   CURLcode result = CURLE_OK;
803   struct SessionHandle *data = conn->data;
804   char *chlg64 = data->state.buffer;
805   size_t len = 0;
806   char *rplyb64 = NULL;
807
808   (void)instate; /* no use for this yet */
809
810   if(imapcode != '+') {
811     failf(data, "Access denied: %d", imapcode);
812     return CURLE_LOGIN_DENIED;
813   }
814
815   /* Get the challenge */
816   for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
817     ;
818
819   /* Terminate the challenge */
820   if(*chlg64 != '=') {
821     for(len = strlen(chlg64); len--;)
822       if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
823          chlg64[len] != '\t')
824         break;
825
826     if(++len) {
827       chlg64[len] = '\0';
828     }
829   }
830
831   /* Create the response message */
832   result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
833                                              conn->passwd, &rplyb64, &len);
834
835   /* Send the response */
836   if(!result) {
837     if(rplyb64) {
838       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
839
840       if(!result)
841         state(conn, IMAP_AUTHENTICATE);
842     }
843
844     Curl_safefree(rplyb64);
845   }
846
847   return result;
848 }
849
850 /* For AUTHENTICATE DIGEST-MD5 challenge responses */
851 static CURLcode imap_state_auth_digest_resp(struct connectdata *conn,
852                                             int imapcode,
853                                             imapstate instate)
854 {
855   CURLcode result = CURLE_OK;
856   struct SessionHandle *data = conn->data;
857   char *chlg64 = data->state.buffer;
858   size_t len = 0;
859   char *rplyb64 = NULL;
860
861   (void)instate; /* no use for this yet */
862
863   if(imapcode != '+') {
864     failf(data, "Access denied: %d", imapcode);
865     return CURLE_LOGIN_DENIED;
866   }
867
868   /* Get the challenge */
869   for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
870     ;
871
872   /* Create the response message */
873   result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
874                                                conn->passwd, "imap",
875                                                &rplyb64, &len);
876
877   /* Send the response */
878   if(!result) {
879     if(rplyb64) {
880       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", rplyb64);
881
882       if(!result)
883         state(conn, IMAP_AUTHENTICATE_DIGESTMD5_RESP);
884     }
885
886     Curl_safefree(rplyb64);
887   }
888
889   return result;
890 }
891
892 /* For AUTHENTICATE DIGEST-MD5 challenge-response responses */
893 static CURLcode imap_state_auth_digest_resp_resp(struct connectdata *conn,
894                                                  int imapcode,
895                                                  imapstate instate)
896 {
897   CURLcode result = CURLE_OK;
898   struct SessionHandle *data = conn->data;
899
900   (void)instate; /* no use for this yet */
901
902   if(imapcode != '+') {
903     failf(data, "Authentication failed: %d", imapcode);
904     result = CURLE_LOGIN_DENIED;
905   }
906   else {
907     /* Send an empty response */
908     result = Curl_pp_sendf(&conn->proto.imapc.pp, "");
909
910     if(!result)
911       state(conn, IMAP_AUTHENTICATE);
912   }
913
914   return result;
915 }
916 #endif
917
918 #ifdef USE_NTLM
919 /* For AUTHENTICATE NTLM responses */
920 static CURLcode imap_state_auth_ntlm_resp(struct connectdata *conn,
921                                           int imapcode,
922                                           imapstate instate)
923 {
924   CURLcode result = CURLE_OK;
925   struct SessionHandle *data = conn->data;
926   size_t len = 0;
927   char *type1msg = NULL;
928
929   (void)instate; /* no use for this yet */
930
931   if(imapcode != '+') {
932     failf(data, "Access denied: %d", imapcode);
933     result = CURLE_LOGIN_DENIED;
934   }
935   else {
936     /* Create the type-1 message */
937     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
938                                                  &conn->ntlm,
939                                                  &type1msg, &len);
940
941     /* Send the message */
942     if(!result) {
943       if(type1msg) {
944         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type1msg);
945
946         if(!result)
947           state(conn, IMAP_AUTHENTICATE_NTLM_TYPE2MSG);
948       }
949
950       Curl_safefree(type1msg);
951     }
952   }
953
954   return result;
955 }
956
957 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
958 static CURLcode imap_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
959                                                    int imapcode,
960                                                    imapstate instate)
961 {
962   CURLcode result = CURLE_OK;
963   struct SessionHandle *data = conn->data;
964   size_t len = 0;
965   char *type3msg = NULL;
966
967   (void)instate; /* no use for this yet */
968
969   if(imapcode != '+') {
970     failf(data, "Access denied: %d", imapcode);
971     result = CURLE_LOGIN_DENIED;
972   }
973   else {
974     /* Create the type-3 message */
975     result = Curl_sasl_create_ntlm_type3_message(data,
976                                                  data->state.buffer + 2,
977                                                  conn->user, conn->passwd,
978                                                  &conn->ntlm,
979                                                  &type3msg, &len);
980
981     /* Send the message */
982     if(!result) {
983       if(type3msg) {
984         result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", type3msg);
985
986         if(!result)
987           state(conn, IMAP_AUTHENTICATE);
988       }
989
990       Curl_safefree(type3msg);
991     }
992   }
993
994   return result;
995 }
996 #endif
997
998 /* For final responses to the AUTHENTICATE sequence */
999 static CURLcode imap_state_auth_final_resp(struct connectdata *conn,
1000                                            int imapcode,
1001                                            imapstate instate)
1002 {
1003   CURLcode result = CURLE_OK;
1004   struct SessionHandle *data = conn->data;
1005
1006   (void)instate; /* no use for this yet */
1007
1008   if(imapcode != 'O') {
1009     failf(data, "Authentication failed: %d", imapcode);
1010     result = CURLE_LOGIN_DENIED;
1011   }
1012
1013   /* End of connect phase */
1014   state(conn, IMAP_STOP);
1015
1016   return result;
1017 }
1018
1019 /* For LOGIN responses */
1020 static CURLcode imap_state_login_resp(struct connectdata *conn,
1021                                       int imapcode,
1022                                       imapstate instate)
1023 {
1024   CURLcode result = CURLE_OK;
1025   struct SessionHandle *data = conn->data;
1026
1027   (void)instate; /* no use for this yet */
1028
1029   if(imapcode != 'O') {
1030     failf(data, "Access denied. %c", imapcode);
1031     result = CURLE_LOGIN_DENIED;
1032   }
1033   else
1034     /* End of connect phase */
1035     state(conn, IMAP_STOP);
1036
1037   return result;
1038 }
1039
1040 /* Start the DO phase */
1041 static CURLcode imap_select(struct connectdata *conn)
1042 {
1043   CURLcode result = CURLE_OK;
1044   struct imap_conn *imapc = &conn->proto.imapc;
1045   const char *str = getcmdid(conn);
1046
1047   result = imap_sendf(conn, str, "%s SELECT %s", str,
1048                       imapc->mailbox?imapc->mailbox:"");
1049   if(result)
1050     return result;
1051
1052   state(conn, IMAP_SELECT);
1053
1054   return result;
1055 }
1056
1057 static CURLcode imap_fetch(struct connectdata *conn)
1058 {
1059   CURLcode result = CURLE_OK;
1060   const char *str = getcmdid(conn);
1061
1062   /* TODO: make this select the correct mail
1063    * Use "1 body[text]" to get the full mail body of mail 1
1064    */
1065   result = imap_sendf(conn, str, "%s FETCH 1 BODY[TEXT]", str);
1066   if(result)
1067     return result;
1068
1069   /*
1070    * When issued, the server will respond with a single line similar to
1071    * '* 1 FETCH (BODY[TEXT] {2021}'
1072    *
1073    * Identifying the fetch and how many bytes of contents we can expect. We
1074    * must extract that number before continuing to "download as usual".
1075    */
1076
1077   state(conn, IMAP_FETCH);
1078
1079   return result;
1080 }
1081
1082 /* For SELECT responses */
1083 static CURLcode imap_state_select_resp(struct connectdata *conn,
1084                                        int imapcode,
1085                                        imapstate instate)
1086 {
1087   CURLcode result = CURLE_OK;
1088   struct SessionHandle *data = conn->data;
1089
1090   (void)instate; /* no use for this yet */
1091
1092   if(imapcode != 'O') {
1093     failf(data, "Select failed");
1094     result = CURLE_LOGIN_DENIED;
1095   }
1096   else
1097     result = imap_fetch(conn);
1098
1099   return result;
1100 }
1101
1102 /* For the (first line of) FETCH BODY[TEXT] response */
1103 static CURLcode imap_state_fetch_resp(struct connectdata *conn,
1104                                       int imapcode,
1105                                       imapstate instate)
1106 {
1107   CURLcode result = CURLE_OK;
1108   struct SessionHandle *data = conn->data;
1109   struct imap_conn *imapc = &conn->proto.imapc;
1110   struct FTP *imap = data->state.proto.imap;
1111   struct pingpong *pp = &imapc->pp;
1112   const char *ptr = data->state.buffer;
1113
1114   (void)instate; /* no use for this yet */
1115
1116   if('*' != imapcode) {
1117     Curl_pgrsSetDownloadSize(data, 0);
1118     state(conn, IMAP_STOP);
1119     return CURLE_OK;
1120   }
1121
1122   /* Something like this comes "* 1 FETCH (BODY[TEXT] {2021}\r" */
1123   while(*ptr && (*ptr != '{'))
1124     ptr++;
1125
1126   if(*ptr == '{') {
1127     curl_off_t filesize = curlx_strtoofft(ptr + 1, NULL, 10);
1128     if(filesize)
1129       Curl_pgrsSetDownloadSize(data, filesize);
1130
1131     infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", filesize);
1132
1133     if(pp->cache) {
1134       /* At this point there is a bunch of data in the header "cache" that is
1135          actually body content, send it as body and then skip it. Do note
1136          that there may even be additional "headers" after the body. */
1137       size_t chunk = pp->cache_size;
1138
1139       if(chunk > (size_t)filesize)
1140         /* the conversion from curl_off_t to size_t is always fine here */
1141         chunk = (size_t)filesize;
1142
1143       result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1144       if(result)
1145         return result;
1146
1147       filesize -= chunk;
1148
1149       /* we've now used parts of or the entire cache */
1150       if(pp->cache_size > chunk) {
1151         /* part of, move the trailing data to the start and reduce the size */
1152         memmove(pp->cache, pp->cache+chunk,
1153                 pp->cache_size - chunk);
1154         pp->cache_size -= chunk;
1155       }
1156       else {
1157         /* cache is drained */
1158         Curl_safefree(pp->cache);
1159         pp->cache = NULL;
1160         pp->cache_size = 0;
1161       }
1162     }
1163
1164     infof(data, "Filesize left: %" FORMAT_OFF_T "\n", filesize);
1165
1166     if(!filesize)
1167       /* the entire data is already transferred! */
1168       Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1169     else
1170       /* IMAP download */
1171       Curl_setup_transfer(conn, FIRSTSOCKET, filesize, FALSE,
1172                           imap->bytecountp, -1, NULL); /* no upload here */
1173
1174     data->req.maxdownload = filesize;
1175   }
1176   else
1177     /* We don't know how to parse this line */
1178     result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
1179
1180   /* End of do phase */
1181   state(conn, IMAP_STOP);
1182
1183   return result;
1184 }
1185
1186 static CURLcode imap_statemach_act(struct connectdata *conn)
1187 {
1188   CURLcode result;
1189   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1190   int imapcode;
1191   struct imap_conn *imapc = &conn->proto.imapc;
1192   struct pingpong *pp = &imapc->pp;
1193   size_t nread = 0;
1194
1195   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1196   if(imapc->state == IMAP_UPGRADETLS)
1197     return imap_state_upgrade_tls(conn);
1198
1199   /* Flush any data that needs to be sent */
1200   if(pp->sendleft)
1201     return Curl_pp_flushsend(pp);
1202
1203   /* Read the response from the server */
1204   result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1205   if(result)
1206     return result;
1207
1208   if(imapcode) {
1209     /* We have now received a full IMAP server response */
1210     switch(imapc->state) {
1211     case IMAP_SERVERGREET:
1212       result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1213       break;
1214
1215     case IMAP_STARTTLS:
1216       result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1217       break;
1218
1219     case IMAP_CAPABILITY:
1220       result = imap_state_capability_resp(conn, imapcode, imapc->state);
1221       break;
1222
1223     case IMAP_AUTHENTICATE_PLAIN:
1224       result = imap_state_auth_plain_resp(conn, imapcode, imapc->state);
1225       break;
1226
1227     case IMAP_AUTHENTICATE_LOGIN:
1228       result = imap_state_auth_login_resp(conn, imapcode, imapc->state);
1229       break;
1230
1231     case IMAP_AUTHENTICATE_LOGIN_PASSWD:
1232       result = imap_state_auth_login_password_resp(conn, imapcode,
1233                                                    imapc->state);
1234       break;
1235
1236 #ifndef CURL_DISABLE_CRYPTO_AUTH
1237     case IMAP_AUTHENTICATE_CRAMMD5:
1238       result = imap_state_auth_cram_resp(conn, imapcode, imapc->state);
1239       break;
1240
1241     case IMAP_AUTHENTICATE_DIGESTMD5:
1242       result = imap_state_auth_digest_resp(conn, imapcode, imapc->state);
1243       break;
1244
1245     case IMAP_AUTHENTICATE_DIGESTMD5_RESP:
1246       result = imap_state_auth_digest_resp_resp(conn, imapcode, imapc->state);
1247       break;
1248 #endif
1249
1250 #ifdef USE_NTLM
1251     case IMAP_AUTHENTICATE_NTLM:
1252       result = imap_state_auth_ntlm_resp(conn, imapcode, imapc->state);
1253       break;
1254
1255     case IMAP_AUTHENTICATE_NTLM_TYPE2MSG:
1256       result = imap_state_auth_ntlm_type2msg_resp(conn, imapcode,
1257                                                   imapc->state);
1258       break;
1259 #endif
1260
1261     case IMAP_AUTHENTICATE:
1262       result = imap_state_auth_final_resp(conn, imapcode, imapc->state);
1263       break;
1264
1265     case IMAP_LOGIN:
1266       result = imap_state_login_resp(conn, imapcode, imapc->state);
1267       break;
1268
1269     case IMAP_FETCH:
1270       result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1271       break;
1272
1273     case IMAP_SELECT:
1274       result = imap_state_select_resp(conn, imapcode, imapc->state);
1275       break;
1276
1277     case IMAP_LOGOUT:
1278       /* fallthrough, just stop! */
1279     default:
1280       /* internal error */
1281       state(conn, IMAP_STOP);
1282       break;
1283     }
1284   }
1285
1286   return result;
1287 }
1288
1289 /* Called repeatedly until done from multi.c */
1290 static CURLcode imap_multi_statemach(struct connectdata *conn,
1291                                          bool *done)
1292 {
1293   struct imap_conn *imapc = &conn->proto.imapc;
1294   CURLcode result;
1295
1296   if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone)
1297     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1298   else
1299     result = Curl_pp_multi_statemach(&imapc->pp);
1300
1301   *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1302
1303   return result;
1304 }
1305
1306 static CURLcode imap_easy_statemach(struct connectdata *conn)
1307 {
1308   struct imap_conn *imapc = &conn->proto.imapc;
1309   struct pingpong *pp = &imapc->pp;
1310   CURLcode result = CURLE_OK;
1311
1312   while(imapc->state != IMAP_STOP) {
1313     result = Curl_pp_easy_statemach(pp);
1314     if(result)
1315       break;
1316   }
1317
1318   return result;
1319 }
1320
1321 /* Allocate and initialize the struct IMAP for the current SessionHandle if
1322    required */
1323 static CURLcode imap_init(struct connectdata *conn)
1324 {
1325   struct SessionHandle *data = conn->data;
1326   struct FTP *imap = data->state.proto.imap;
1327
1328   if(!imap) {
1329     imap = data->state.proto.imap = calloc(sizeof(struct FTP), 1);
1330     if(!imap)
1331       return CURLE_OUT_OF_MEMORY;
1332   }
1333
1334   /* Get some initial data into the imap struct */
1335   imap->bytecountp = &data->req.bytecount;
1336
1337   /* No need to duplicate user+password, the connectdata struct won't change
1338      during a session, but we re-init them here since on subsequent inits
1339      since the conn struct may have changed or been replaced.
1340   */
1341   imap->user = conn->user;
1342   imap->passwd = conn->passwd;
1343
1344   return CURLE_OK;
1345 }
1346
1347 /***********************************************************************
1348  *
1349  * imap_connect() should do everything that is to be considered a part of
1350  * the connection phase.
1351  *
1352  * The variable 'done' points to will be TRUE if the protocol-layer connect
1353  * phase is done when this function returns, or FALSE is not. When called as
1354  * a part of the easy interface, it will always be TRUE.
1355  */
1356 static CURLcode imap_connect(struct connectdata *conn,
1357                                  bool *done) /* see description above */
1358 {
1359   CURLcode result;
1360   struct imap_conn *imapc = &conn->proto.imapc;
1361   struct SessionHandle *data=conn->data;
1362   struct pingpong *pp = &imapc->pp;
1363
1364   *done = FALSE; /* default to not done yet */
1365
1366   /* If there already is a protocol-specific struct allocated for this
1367      sessionhandle, deal with it */
1368   Curl_reset_reqproto(conn);
1369
1370   result = imap_init(conn);
1371   if(CURLE_OK != result)
1372     return result;
1373
1374   /* We always support persistent connections on imap */
1375   conn->bits.close = FALSE;
1376
1377   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1378   pp->statemach_act = imap_statemach_act;
1379   pp->endofresp = imap_endofresp;
1380   pp->conn = conn;
1381
1382   if((conn->handler->flags & PROTOPT_SSL) &&
1383      data->state.used_interface != Curl_if_multi) {
1384     /* IMAPS is simply imap with SSL for the control channel */
1385     /* so perform the SSL initialization for this socket */
1386     result = Curl_ssl_connect(conn, FIRSTSOCKET);
1387     if(result)
1388       return result;
1389   }
1390
1391   /* Initialise the response reader stuff */
1392   Curl_pp_init(pp);
1393
1394   /* Start off waiting for the server greeting response */
1395   state(conn, IMAP_SERVERGREET);
1396
1397   /* Start off with an id of '*' */
1398   imapc->idstr = "*";
1399
1400   if(data->state.used_interface == Curl_if_multi)
1401     result = imap_multi_statemach(conn, done);
1402   else {
1403     result = imap_easy_statemach(conn);
1404     if(!result)
1405       *done = TRUE;
1406   }
1407
1408   return result;
1409 }
1410
1411 /***********************************************************************
1412  *
1413  * imap_done()
1414  *
1415  * The DONE function. This does what needs to be done after a single DO has
1416  * performed.
1417  *
1418  * Input argument is already checked for validity.
1419  */
1420 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1421                           bool premature)
1422 {
1423   struct SessionHandle *data = conn->data;
1424   struct FTP *imap = data->state.proto.imap;
1425   CURLcode result=CURLE_OK;
1426
1427   (void)premature;
1428
1429   if(!imap)
1430     /* When the easy handle is removed from the multi while libcurl is still
1431      * trying to resolve the host name, it seems that the imap struct is not
1432      * yet initialized, but the removal action calls Curl_done() which calls
1433      * this function. So we simply return success if no imap pointer is set.
1434      */
1435     return CURLE_OK;
1436
1437   if(status) {
1438     conn->bits.close = TRUE; /* marked for closure */
1439     result = status;         /* use the already set error code */
1440   }
1441
1442   /* Clear the transfer mode for the next connection */
1443   imap->transfer = FTPTRANSFER_BODY;
1444
1445   return result;
1446 }
1447
1448 /***********************************************************************
1449  *
1450  * imap_perform()
1451  *
1452  * This is the actual DO function for IMAP. Get a file/directory according to
1453  * the options previously setup.
1454  */
1455 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1456                              bool *dophase_done)
1457 {
1458   /* This is IMAP and no proxy */
1459   CURLcode result = CURLE_OK;
1460
1461   DEBUGF(infof(conn->data, "DO phase starts\n"));
1462
1463   if(conn->data->set.opt_no_body) {
1464     /* Requested no body means no transfer */
1465     struct FTP *imap = conn->data->state.proto.imap;
1466     imap->transfer = FTPTRANSFER_INFO;
1467   }
1468
1469   *dophase_done = FALSE; /* not done yet */
1470
1471   /* Start the first command in the DO phase */
1472   result = imap_select(conn);
1473   if(result)
1474     return result;
1475
1476   /* Run the state-machine */
1477   if(conn->data->state.used_interface == Curl_if_multi)
1478     result = imap_multi_statemach(conn, dophase_done);
1479   else {
1480     result = imap_easy_statemach(conn);
1481     *dophase_done = TRUE; /* with the easy interface we are done here */
1482   }
1483   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1484
1485   if(*dophase_done)
1486     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1487
1488   return result;
1489 }
1490
1491 /***********************************************************************
1492  *
1493  * imap_do()
1494  *
1495  * This function is registered as 'curl_do' function. It decodes the path
1496  * parts etc as a wrapper to the actual DO function (imap_perform).
1497  *
1498  * The input argument is already checked for validity.
1499  */
1500 static CURLcode imap_do(struct connectdata *conn, bool *done)
1501 {
1502   CURLcode retcode = CURLE_OK;
1503
1504   *done = FALSE; /* default to false */
1505
1506   /*
1507     Since connections can be re-used between SessionHandles, this might be a
1508     connection already existing but on a fresh SessionHandle struct so we must
1509     make sure we have a good 'struct IMAP' to play with. For new connections,
1510     the struct IMAP is allocated and setup in the imap_connect() function.
1511   */
1512   Curl_reset_reqproto(conn);
1513   retcode = imap_init(conn);
1514   if(retcode)
1515     return retcode;
1516
1517   /* Parse the URL path */
1518   retcode = imap_parse_url_path(conn);
1519   if(retcode)
1520     return retcode;
1521
1522   retcode = imap_regular_transfer(conn, done);
1523
1524   return retcode;
1525 }
1526
1527 /***********************************************************************
1528  *
1529  * imap_logout()
1530  *
1531  * This should be called before calling sclose().  We should then wait for the
1532  * response from the server before returning. The calling code should then try
1533  * to close the connection.
1534  *
1535  */
1536 static CURLcode imap_logout(struct connectdata *conn)
1537 {
1538   CURLcode result = CURLE_OK;
1539   const char *str = getcmdid(conn);
1540
1541   result = imap_sendf(conn, str, "%s LOGOUT", str, NULL);
1542   if(result)
1543     return result;
1544
1545   state(conn, IMAP_LOGOUT);
1546
1547   result = imap_easy_statemach(conn);
1548
1549   return result;
1550 }
1551
1552 /***********************************************************************
1553  *
1554  * imap_disconnect()
1555  *
1556  * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1557  * resources. BLOCKING.
1558  */
1559 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1560 {
1561   struct imap_conn *imapc= &conn->proto.imapc;
1562
1563   /* We cannot send quit unconditionally. If this connection is stale or
1564      bad in any way, sending quit and waiting around here will make the
1565      disconnect wait in vain and cause more problems than we need to */
1566
1567   /* The IMAP session may or may not have been allocated/setup at this
1568      point! */
1569   if(!dead_connection && imapc->pp.conn)
1570     (void)imap_logout(conn); /* ignore errors on the LOGOUT */
1571
1572   /* Disconnect from the server */
1573   Curl_pp_disconnect(&imapc->pp);
1574
1575   /* Cleanup the SASL module */
1576   Curl_sasl_cleanup(conn, imapc->authused);
1577
1578   /* Cleanup our connection based variables */
1579   Curl_safefree(imapc->mailbox);
1580
1581   return CURLE_OK;
1582 }
1583
1584 /***********************************************************************
1585  *
1586  * imap_parse_url_path()
1587  *
1588  * Parse the URL path into separate path components.
1589  *
1590  */
1591 static CURLcode imap_parse_url_path(struct connectdata *conn)
1592 {
1593   /* The imap struct is already inited in imap_connect() */
1594   struct imap_conn *imapc = &conn->proto.imapc;
1595   struct SessionHandle *data = conn->data;
1596   const char *path = data->state.path;
1597
1598   if(!*path)
1599     path = "INBOX";
1600
1601   /* URL decode the path and use this mailbox */
1602   return Curl_urldecode(data, path, 0, &imapc->mailbox, NULL, TRUE);
1603 }
1604
1605 /* Call this when the DO phase has completed */
1606 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1607 {
1608   struct FTP *imap = conn->data->state.proto.imap;
1609
1610   (void)connected;
1611
1612   if(imap->transfer != FTPTRANSFER_BODY)
1613     /* no data to transfer */
1614     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1615
1616   return CURLE_OK;
1617 }
1618
1619 /* Called from multi.c while DOing */
1620 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1621 {
1622   CURLcode result = imap_multi_statemach(conn, dophase_done);
1623
1624   if(result)
1625     DEBUGF(infof(conn->data, "DO phase failed\n"));
1626   else {
1627     if(*dophase_done) {
1628       result = imap_dophase_done(conn, FALSE /* not connected */);
1629
1630       DEBUGF(infof(conn->data, "DO phase is complete\n"));
1631     }
1632   }
1633
1634   return result;
1635 }
1636
1637 /***********************************************************************
1638  *
1639  * imap_regular_transfer()
1640  *
1641  * The input argument is already checked for validity.
1642  *
1643  * Performs all commands done before a regular transfer between a local and a
1644  * remote host.
1645  */
1646 static CURLcode imap_regular_transfer(struct connectdata *conn,
1647                                       bool *dophase_done)
1648 {
1649   CURLcode result = CURLE_OK;
1650   bool connected = FALSE;
1651   struct SessionHandle *data = conn->data;
1652
1653   /* Make sure size is unknown at this point */
1654   data->req.size = -1;
1655
1656   Curl_pgrsSetUploadCounter(data, 0);
1657   Curl_pgrsSetDownloadCounter(data, 0);
1658   Curl_pgrsSetUploadSize(data, 0);
1659   Curl_pgrsSetDownloadSize(data, 0);
1660
1661   result = imap_perform(conn, &connected, dophase_done);
1662
1663   if(CURLE_OK == result) {
1664     if(!*dophase_done)
1665       /* The DO phase has not completed yet */
1666       return CURLE_OK;
1667
1668     result = imap_dophase_done(conn, connected);
1669     if(result)
1670       return result;
1671   }
1672
1673   return result;
1674 }
1675
1676 static CURLcode imap_setup_connection(struct connectdata * conn)
1677 {
1678   struct SessionHandle *data = conn->data;
1679
1680   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1681     /* Unless we have asked to tunnel imap operations through the proxy, we
1682        switch and use HTTP operations only */
1683 #ifndef CURL_DISABLE_HTTP
1684     if(conn->handler == &Curl_handler_imap)
1685       conn->handler = &Curl_handler_imap_proxy;
1686     else {
1687 #ifdef USE_SSL
1688       conn->handler = &Curl_handler_imaps_proxy;
1689 #else
1690       failf(data, "IMAPS not supported!");
1691       return CURLE_UNSUPPORTED_PROTOCOL;
1692 #endif
1693     }
1694
1695     /* We explicitly mark this connection as persistent here as we're doing
1696        IMAP over HTTP and thus we accidentally avoid setting this value
1697        otherwise */
1698     conn->bits.close = FALSE;
1699 #else
1700     failf(data, "IMAP over http proxy requires HTTP support built-in!");
1701     return CURLE_UNSUPPORTED_PROTOCOL;
1702 #endif
1703   }
1704
1705   data->state.path++;   /* don't include the initial slash */
1706
1707   return CURLE_OK;
1708 }
1709
1710 #endif /* CURL_DISABLE_IMAP */