Andreas Krennmair:
[apps/madmutt.git] / imap / imap.c
1 /*
2  * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org>
3  * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
4  * Copyright (C) 1999-2003 Brendan Cully <brendan@kublai.com>
5  * 
6  *     This program is free software; you can redistribute it and/or modify
7  *     it under the terms of the GNU General Public License as published by
8  *     the Free Software Foundation; either version 2 of the License, or
9  *     (at your option) any later version.
10  * 
11  *     This program is distributed in the hope that it will be useful,
12  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *     GNU General Public License for more details.
15  * 
16  *     You should have received a copy of the GNU General Public License
17  *     along with this program; if not, write to the Free Software
18  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
19  */ 
20
21 /* Support for IMAP4rev1, with the occasional nod to IMAP 4. */
22
23 #if HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include "mutt.h"
28 #include "mutt_curses.h"
29 #include "mx.h"
30 #include "mailbox.h"
31 #include "globals.h"
32 #include "sort.h"
33 #include "browser.h"
34 #include "message.h"
35 #include "imap_private.h"
36 #if defined(USE_SSL) || defined(USE_GNUTLS)
37 # include "mutt_ssl.h"
38 #endif
39
40 #include <unistd.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46
47 /* imap forward declarations */
48 static int imap_get_delim (IMAP_DATA *idata);
49 static char* imap_get_flags (LIST** hflags, char* s);
50 static int imap_check_acl (IMAP_DATA *idata);
51 static int imap_check_capabilities (IMAP_DATA* idata);
52 static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag,
53                            const char* str, char* flags, size_t flsize);
54
55 /* imap_access: Check permissions on an IMAP mailbox. */
56 int imap_access (const char* path, int flags)
57 {
58   IMAP_DATA* idata;
59   IMAP_MBOX mx;
60   char buf[LONG_STRING];
61   char mailbox[LONG_STRING];
62   char mbox[LONG_STRING];
63
64   if (imap_parse_path (path, &mx))
65     return -1;
66
67   if (!(idata = imap_conn_find (&mx.account,
68     option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0)))
69   {
70     FREE (&mx.mbox);
71     return -1;
72   }
73
74   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
75   FREE (&mx.mbox);
76   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
77
78   /* TODO: ACL checks. Right now we assume if it exists we can mess with it. */
79   if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
80     snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
81   else if (mutt_bit_isset (idata->capabilities, STATUS))
82     snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
83   else
84   {
85     dprint (2, (debugfile, "imap_access: STATUS not supported?\n"));
86     return -1;
87   }
88
89   if (imap_exec (idata, buf, IMAP_CMD_FAIL_OK) < 0)
90   {
91     dprint (1, (debugfile, "imap_access: Can't check STATUS of %s\n", mbox));
92     return -1;
93   }
94
95   return 0;
96 }
97
98 int imap_create_mailbox (IMAP_DATA* idata, char* mailbox)
99 {
100   char buf[LONG_STRING], mbox[LONG_STRING];
101
102   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
103   snprintf (buf, sizeof (buf), "CREATE %s", mbox);
104       
105   if (imap_exec (idata, buf, 0) != 0)
106     return -1;
107
108   return 0;
109 }
110
111 int imap_delete_mailbox (CONTEXT* ctx, IMAP_MBOX mx)
112 {
113   char buf[LONG_STRING], mbox[LONG_STRING];
114   IMAP_DATA *idata;
115
116   if (!ctx || !ctx->data) {
117         if (!(idata = imap_conn_find (&mx.account,
118                 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0)))
119         {
120                 FREE (&mx.mbox);
121                 return -1;
122         }
123   } else {
124           idata = ctx->data;
125   }
126
127   imap_munge_mbox_name (mbox, sizeof (mbox), mx.mbox);
128   snprintf (buf, sizeof (buf), "DELETE %s", mbox);
129
130   if (imap_exec ((IMAP_DATA*) idata, buf, 0) != 0)
131     return -1;
132
133   return 0;
134 }
135
136 /* imap_logout_all: close all open connections. Quick and dirty until we can
137  *   make sure we've got all the context we need. */
138 void imap_logout_all (void) 
139 {
140   CONNECTION* conn;
141   CONNECTION* tmp;
142
143   conn = mutt_socket_head ();
144
145   while (conn)
146   {
147     tmp = conn->next;
148
149     if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0)
150     {
151       mutt_message (_("Closing connection to %s..."), conn->account.host);
152       imap_logout ((IMAP_DATA*) conn->data);
153       mutt_clear_error ();
154       mutt_socket_close (conn);
155       mutt_socket_free (conn);
156     }
157
158     conn = tmp;
159   }
160 }
161
162 /* imap_read_literal: read bytes bytes from server into file. Not explicitly
163  *   buffered, relies on FILE buffering. NOTE: strips \r from \r\n.
164  *   Apparently even literals use \r\n-terminated strings ?! */
165 int imap_read_literal (FILE* fp, IMAP_DATA* idata, long bytes)
166 {
167   long pos;
168   char c;
169
170   int r = 0;
171
172   dprint (2, (debugfile, "imap_read_literal: reading %ld bytes\n", bytes));
173  
174   for (pos = 0; pos < bytes; pos++)
175   {
176     if (mutt_socket_readchar (idata->conn, &c) != 1)
177     {
178       dprint (1, (debugfile, "imap_read_literal: error during read, %ld bytes read\n", pos));
179       idata->status = IMAP_FATAL;
180       
181       return -1;
182     }
183
184 #if 1
185     if (r == 1 && c != '\n')
186       fputc ('\r', fp);
187
188     if (c == '\r')
189     {
190       r = 1;
191       continue;
192     }
193     else
194       r = 0;
195 #endif
196     fputc (c, fp);
197 #ifdef DEBUG
198     if (debuglevel >= IMAP_LOG_LTRL)
199       fputc (c, debugfile);
200 #endif
201   }
202
203   return 0;
204 }
205
206 /* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the
207  *   context. Must not be done while something has a handle on any headers
208  *   (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */
209 void imap_expunge_mailbox (IMAP_DATA* idata)
210 {
211   HEADER* h;
212   int i, cacheno;
213
214   for (i = 0; i < idata->ctx->msgcount; i++)
215   {
216     h = idata->ctx->hdrs[i];
217
218     if (h->index == -1)
219     {
220       dprint (2, (debugfile, "Expunging message UID %d.\n", HEADER_DATA (h)->uid));
221
222       h->active = 0;
223
224       /* free cached body from disk, if neccessary */
225       cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN;
226       if (idata->cache[cacheno].uid == HEADER_DATA(h)->uid &&
227           idata->cache[cacheno].path)
228       {
229         unlink (idata->cache[cacheno].path);
230         FREE (&idata->cache[cacheno].path);
231       }
232
233       imap_free_header_data (&h->data);
234     }
235   }
236
237   /* We may be called on to expunge at any time. We can't rely on the caller
238    * to always know to rethread */
239   mx_update_tables (idata->ctx, 0);
240   mutt_sort_headers (idata->ctx, 1);
241 }
242
243 static int imap_get_delim (IMAP_DATA *idata)
244 {
245   char *s;
246   int rc;
247
248   /* assume that the delim is /.  If this fails, we're in bigger trouble
249    * than getting the delim wrong */
250   idata->delim = '/';
251
252   imap_cmd_start (idata, "LIST \"\" \"\"");
253
254   do 
255   {
256     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
257       break;
258
259     s = imap_next_word (idata->cmd.buf);
260     if (ascii_strncasecmp ("LIST", s, 4) == 0)
261     {
262       s = imap_next_word (s);
263       s = imap_next_word (s);
264       if (s && s[0] == '\"' && s[1] && s[2] == '\"')
265         idata->delim = s[1];
266       else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2] && s[3] == '\"')
267         idata->delim = s[2];
268     }
269   }
270   while (rc == IMAP_CMD_CONTINUE);
271
272   if (rc != IMAP_CMD_OK)
273   {
274     dprint (1, (debugfile, "imap_get_delim: failed.\n"));
275     return -1;
276   }
277
278   dprint (2, (debugfile, "Delimiter: %c\n", idata->delim));
279
280   return -1;
281 }
282
283 /* get rights for folder, let imap_handle_untagged do the rest */
284 static int imap_check_acl (IMAP_DATA *idata)
285 {
286   char buf[LONG_STRING];
287   char mbox[LONG_STRING];
288
289   imap_munge_mbox_name (mbox, sizeof(mbox), idata->mailbox);
290   snprintf (buf, sizeof (buf), "MYRIGHTS %s", mbox);
291   if (imap_exec (idata, buf, 0) != 0)
292   {
293     imap_error ("imap_check_acl", buf);
294     return -1;
295   }
296   return 0;
297 }
298
299 /* imap_check_capabilities: make sure we can log in to this server. */
300 static int imap_check_capabilities (IMAP_DATA* idata)
301 {
302   if (imap_exec (idata, "CAPABILITY", 0) != 0)
303   {
304     imap_error ("imap_check_capabilities", idata->cmd.buf);
305     return -1;
306   }
307
308   if (!(mutt_bit_isset(idata->capabilities,IMAP4)
309       ||mutt_bit_isset(idata->capabilities,IMAP4REV1)))
310   {
311     mutt_error _("This IMAP server is ancient. Mutt does not work with it.");
312     mutt_sleep (2);     /* pause a moment to let the user see the error */
313
314     return -1;
315   }
316
317   return 0;
318 }
319
320 /* imap_conn_find: Find an open IMAP connection matching account, or open
321  *   a new one if none can be found. */
322 IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags)
323 {
324   CONNECTION* conn;
325   IMAP_DATA* idata;
326   ACCOUNT* creds;
327
328   if (!(conn = mutt_conn_find (NULL, account)))
329     return NULL;
330
331   /* if opening a new UNSELECTED connection, preserve existing creds */
332   creds = &(conn->account);
333
334   /* make sure this connection is not in SELECTED state, if neccessary */
335   if (flags & M_IMAP_CONN_NOSELECT)
336     while (conn->data && ((IMAP_DATA*) conn->data)->state == IMAP_SELECTED)
337     {
338       if (!(conn = mutt_conn_find (conn, account)))
339         return NULL;
340       memcpy (&(conn->account), creds, sizeof (ACCOUNT));
341     }
342   
343   idata = (IMAP_DATA*) conn->data;
344
345   /* don't open a new connection if one isn't wanted */
346   if (flags & M_IMAP_CONN_NONEW)
347   {
348     if (!idata)
349     {
350       mutt_socket_free (conn);
351       return NULL;
352     }
353     if (idata->state < IMAP_AUTHENTICATED)
354       return NULL;
355   }
356   
357   if (!idata)
358   {
359     /* The current connection is a new connection */
360     if (! (idata = imap_new_idata ()))
361     {
362       mutt_socket_free (conn);
363       return NULL;
364     }
365
366     conn->data = idata;
367     idata->conn = conn;
368   }
369
370   if (idata->state == IMAP_DISCONNECTED)
371     imap_open_connection (idata);
372   if (idata->state == IMAP_CONNECTED)
373   {
374     if (!imap_authenticate (idata))
375     {
376       idata->state = IMAP_AUTHENTICATED;
377       if (idata->conn->ssf)
378         dprint (2, (debugfile, "Communication encrypted at %d bits\n",
379                     idata->conn->ssf));
380     }
381     else
382       mutt_account_unsetpass (&idata->conn->account);
383     
384     FREE (&idata->capstr);
385   }
386   if (idata->state == IMAP_AUTHENTICATED)
387     imap_get_delim (idata);
388   
389   return idata;
390 }
391
392 int imap_open_connection (IMAP_DATA* idata)
393 {
394   char buf[LONG_STRING];
395
396   if (mutt_socket_open (idata->conn) < 0)
397     return -1;
398
399   idata->state = IMAP_CONNECTED;
400
401   if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) {
402     mutt_error (_("Unexpected response received from server: %s"), idata->cmd.buf);
403     mutt_sleep (1);
404
405     mutt_socket_close (idata->conn);
406     idata->state = IMAP_DISCONNECTED;
407     return -1;
408   }
409
410   if (ascii_strncasecmp ("* OK", idata->cmd.buf, 4) == 0)
411   {
412     /* TODO: Parse new tagged CAPABILITY data (* OK [CAPABILITY...]) */
413     if (imap_check_capabilities (idata))
414       goto bail;
415 #if defined(USE_SSL) || defined(USE_GNUTLS)
416     /* Attempt STARTTLS if available and desired. */
417     if (mutt_bit_isset (idata->capabilities, STARTTLS) && !idata->conn->ssf)
418     {
419       int rc;
420
421       if ((rc = query_quadoption (OPT_SSLSTARTTLS,
422         _("Secure connection with TLS?"))) == -1)
423         goto err_close_conn;
424       if (rc == M_YES) {
425         if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
426           goto bail;
427         if (rc != -2)
428         {
429 #ifdef USE_SSL
430           if (mutt_ssl_starttls (idata->conn))
431 #elif USE_GNUTLS
432           if (mutt_gnutls_starttls (idata->conn))
433 #endif
434           {
435             mutt_error (_("Could not negotiate TLS connection"));
436             mutt_sleep (1);
437             goto err_close_conn;
438           }
439           else
440           {
441             /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
442             if (imap_exec (idata, "CAPABILITY", 0))
443               goto bail;
444           }
445         }
446       }
447     }
448 #endif    
449   }
450   else if (ascii_strncasecmp ("* PREAUTH", idata->cmd.buf, 9) == 0)
451   {
452     idata->state = IMAP_AUTHENTICATED;
453     if (imap_check_capabilities (idata) != 0)
454       goto bail;
455     FREE (&idata->capstr);
456   } 
457   else
458   {
459     imap_error ("imap_open_connection()", buf);
460     goto bail;
461   }
462
463   return 0;
464
465  err_close_conn:
466   mutt_socket_close (idata->conn);
467   idata->state = IMAP_DISCONNECTED;
468  bail:
469   FREE (&idata->capstr);
470   return -1;
471 }
472
473 /* imap_get_flags: Make a simple list out of a FLAGS response.
474  *   return stream following FLAGS response */
475 static char* imap_get_flags (LIST** hflags, char* s)
476 {
477   LIST* flags;
478   char* flag_word;
479   char ctmp;
480
481   /* sanity-check string */
482   if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
483   {
484     dprint (1, (debugfile, "imap_get_flags: not a FLAGS response: %s\n",
485       s));
486     return NULL;
487   }
488   s += 5;
489   SKIPWS(s);
490   if (*s != '(')
491   {
492     dprint (1, (debugfile, "imap_get_flags: bogus FLAGS response: %s\n",
493       s));
494     return NULL;
495   }
496
497   /* create list, update caller's flags handle */
498   flags = mutt_new_list();
499   *hflags = flags;
500
501   while (*s && *s != ')')
502   {
503     s++;
504     SKIPWS(s);
505     flag_word = s;
506     while (*s && (*s != ')') && !ISSPACE (*s))
507       s++;
508     ctmp = *s;
509     *s = '\0';
510     if (*flag_word)
511       mutt_add_list (flags, flag_word);
512     *s = ctmp;
513   }
514
515   /* note bad flags response */
516   if (*s != ')')
517   {
518     dprint (1, (debugfile,
519       "imap_get_flags: Unterminated FLAGS response: %s\n", s));
520     mutt_free_list (hflags);
521
522     return NULL;
523   }
524
525   s++;
526
527   return s;
528 }
529
530 int imap_open_mailbox (CONTEXT* ctx)
531 {
532   CONNECTION *conn;
533   IMAP_DATA *idata;
534   char buf[LONG_STRING];
535   char bufout[LONG_STRING];
536   int count = 0;
537   IMAP_MBOX mx;
538   int rc;
539   
540   if (imap_parse_path (ctx->path, &mx))
541   {
542     mutt_error (_("%s is an invalid IMAP path"), ctx->path);
543     return -1;
544   }
545
546   /* we require a connection which isn't currently in IMAP_SELECTED state */
547   if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NOSELECT)))
548     goto fail_noidata;
549   if (idata->state < IMAP_AUTHENTICATED)
550     goto fail;
551
552   conn = idata->conn;
553
554   /* once again the context is new */
555   ctx->data = idata;
556
557   /* Clean up path and replace the one in the ctx */
558   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
559   FREE(&(idata->mailbox));
560   idata->mailbox = safe_strdup (buf);
561   imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox);
562
563   FREE (&(ctx->path));
564   ctx->path = safe_strdup (buf);
565
566   idata->ctx = ctx;
567
568   /* clear mailbox status */
569   idata->status = 0;
570   memset (idata->rights, 0, (RIGHTSMAX+7)/8);
571   idata->newMailCount = 0;
572
573   mutt_message (_("Selecting %s..."), idata->mailbox);
574   imap_munge_mbox_name (buf, sizeof(buf), idata->mailbox);
575   snprintf (bufout, sizeof (bufout), "%s %s",
576     ctx->readonly ? "EXAMINE" : "SELECT", buf);
577
578   idata->state = IMAP_SELECTED;
579
580   imap_cmd_start (idata, bufout);
581
582   do
583   {
584     char *pc;
585     
586     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
587       break;
588
589     pc = idata->cmd.buf + 2;
590
591     /* Obtain list of available flags here, may be overridden by a
592      * PERMANENTFLAGS tag in the OK response */
593     if (ascii_strncasecmp ("FLAGS", pc, 5) == 0)
594     {
595       /* don't override PERMANENTFLAGS */
596       if (!idata->flags)
597       {
598         dprint (2, (debugfile, "Getting mailbox FLAGS\n"));
599         if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
600           goto fail;
601       }
602     }
603     /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
604     else if (ascii_strncasecmp ("OK [PERMANENTFLAGS", pc, 18) == 0)
605     {
606       dprint (2, (debugfile, "Getting mailbox PERMANENTFLAGS\n"));
607       /* safe to call on NULL */
608       mutt_free_list (&(idata->flags));
609       /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
610       pc += 13;
611       if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
612         goto fail;
613     }
614 #ifdef USE_HCACHE
615     /* save UIDVALIDITY for the header cache */
616     else if (ascii_strncasecmp("OK [UIDVALIDITY", pc, 14) == 0)
617     {
618             dprint(2, (debugfile, "Getting mailbox UIDVALIDITY\n"));
619             pc += 3;
620             pc = imap_next_word(pc);
621
622             sscanf(pc, "%u", &(idata->uid_validity));
623     }
624 #endif
625     else
626     {
627       pc = imap_next_word (pc);
628       if (!ascii_strncasecmp ("EXISTS", pc, 6))
629       {
630         count = idata->newMailCount;
631         idata->newMailCount = 0;
632       }
633     }
634   }
635   while (rc == IMAP_CMD_CONTINUE);
636
637   if (rc == IMAP_CMD_NO)
638   {
639     char *s;
640     s = imap_next_word (idata->cmd.buf); /* skip seq */
641     s = imap_next_word (s); /* Skip response */
642     mutt_error ("%s", s);
643     mutt_sleep (2);
644     goto fail;
645   }
646
647   if (rc != IMAP_CMD_OK)
648     goto fail;
649
650   /* check for READ-ONLY notification */
651   if (!ascii_strncasecmp (imap_get_qualifier (idata->cmd.buf), "[READ-ONLY]", 11)  \
652   && !mutt_bit_isset (idata->capabilities, ACL))
653   {
654     dprint (2, (debugfile, "Mailbox is read-only.\n"));
655     ctx->readonly = 1;
656   }
657
658 #ifdef DEBUG
659   /* dump the mailbox flags we've found */
660   if (debuglevel > 2)
661   {
662     if (!idata->flags)
663       dprint (3, (debugfile, "No folder flags found\n"));
664     else
665     {
666       LIST* t = idata->flags;
667
668       dprint (3, (debugfile, "Mailbox flags: "));
669
670       t = t->next;
671       while (t)
672       {
673         dprint (3, (debugfile, "[%s] ", t->data));
674         t = t->next;
675       }
676       dprint (3, (debugfile, "\n"));
677     }
678   }
679 #endif
680
681   if (mutt_bit_isset (idata->capabilities, ACL))
682   {
683     if (imap_check_acl (idata))
684       goto fail;
685     if (!(mutt_bit_isset(idata->rights, IMAP_ACL_DELETE) ||
686           mutt_bit_isset(idata->rights, IMAP_ACL_SEEN) ||
687           mutt_bit_isset(idata->rights, IMAP_ACL_WRITE) ||
688           mutt_bit_isset(idata->rights, IMAP_ACL_INSERT)))
689        ctx->readonly = 1;
690   }
691   /* assume we have all rights if ACL is unavailable */
692   else
693   {
694     mutt_bit_set (idata->rights, IMAP_ACL_LOOKUP);
695     mutt_bit_set (idata->rights, IMAP_ACL_READ);
696     mutt_bit_set (idata->rights, IMAP_ACL_SEEN);
697     mutt_bit_set (idata->rights, IMAP_ACL_WRITE);
698     mutt_bit_set (idata->rights, IMAP_ACL_INSERT);
699     mutt_bit_set (idata->rights, IMAP_ACL_POST);
700     mutt_bit_set (idata->rights, IMAP_ACL_CREATE);
701     mutt_bit_set (idata->rights, IMAP_ACL_DELETE);
702   }
703
704   ctx->hdrmax = count;
705   ctx->hdrs = safe_calloc (count, sizeof (HEADER *));
706   ctx->v2r = safe_calloc (count, sizeof (int));
707   ctx->msgcount = 0;
708   if (count && (imap_read_headers (idata, 0, count-1) < 0))
709   {
710     mutt_error _("Error opening mailbox");
711     mutt_sleep (1);
712     goto fail;
713   }
714
715   dprint (2, (debugfile, "imap_open_mailbox: msgcount is %d\n", ctx->msgcount));
716   FREE (&mx.mbox);
717   return 0;
718
719  fail:
720   if (idata->state == IMAP_SELECTED)
721     idata->state = IMAP_AUTHENTICATED;
722  fail_noidata:
723   FREE (&mx.mbox);
724   return -1;
725 }
726
727 int imap_open_mailbox_append (CONTEXT *ctx)
728 {
729   CONNECTION *conn;
730   IMAP_DATA *idata;
731   char buf[LONG_STRING], mbox[LONG_STRING];
732   char mailbox[LONG_STRING];
733   int r;
734   IMAP_MBOX mx;
735
736   if (imap_parse_path (ctx->path, &mx))
737     return -1;
738
739   /* in APPEND mode, we appear to hijack an existing IMAP connection -
740    * ctx is brand new and mostly empty */
741
742   if (!(idata = imap_conn_find (&(mx.account), 0)))
743     goto fail;
744   conn = idata->conn;
745
746   ctx->magic = M_IMAP;
747   ctx->data = idata;
748
749   /* check mailbox existance */
750
751   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
752
753   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
754                                 
755   if (mutt_bit_isset(idata->capabilities,IMAP4REV1))
756     snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
757   else if (mutt_bit_isset(idata->capabilities,STATUS))
758     /* We have no idea what the other guy wants. UW imapd 8.3 wants this
759      * (but it does not work if another mailbox is selected) */
760     snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
761   else
762   {
763     /* STATUS not supported */
764     mutt_message _("Unable to append to IMAP mailboxes at this server");
765
766     goto fail;
767   }
768
769   r = imap_exec (idata, buf, IMAP_CMD_FAIL_OK);
770   if (r == -2)
771   {
772     /* command failed cause folder doesn't exist */
773     snprintf (buf, sizeof (buf), _("Create %s?"), mailbox);
774     if (option (OPTCONFIRMCREATE) && mutt_yesorno (buf, 1) < 1)
775       goto fail;
776
777     if (imap_create_mailbox (idata, mailbox) < 0)
778       goto fail;
779   }
780   else if (r == -1)
781     /* Hmm, some other failure */
782     goto fail;
783
784   FREE (&mx.mbox);
785   return 0;
786
787  fail:
788   FREE (&mx.mbox);
789   return -1;
790 }
791
792 /* imap_logout: Gracefully log out of server. */
793 void imap_logout (IMAP_DATA* idata)
794 {
795   /* we set status here to let imap_handle_untagged know we _expect_ to
796    * receive a bye response (so it doesn't freak out and close the conn) */
797   idata->status = IMAP_BYE;
798   imap_cmd_start (idata, "LOGOUT");
799   while (imap_cmd_step (idata) == IMAP_CMD_CONTINUE)
800     ;
801   FREE(& idata->cmd.buf);
802   FREE(& idata);
803 }
804
805 /*
806 int imap_close_connection (CONTEXT *ctx)
807 {
808   dprint (1, (debugfile, "imap_close_connection(): closing connection\n"));
809   if (CTX_DATA->status != IMAP_BYE)
810   {
811     mutt_message _("Closing connection to IMAP server...");
812     imap_logout (CTX_DATA);
813     mutt_clear_error ();
814   }
815   mutt_socket_close (CTX_DATA->conn);
816   CTX_DATA->state = IMAP_DISCONNECTED;
817   CTX_DATA->conn->data = NULL;
818   return 0;
819 }
820 */
821
822 /* imap_set_flag: append str to flags if we currently have permission
823  *   according to aclbit */
824 static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag,
825   const char *str, char *flags, size_t flsize)
826 {
827   if (mutt_bit_isset (idata->rights, aclbit))
828     if (flag)
829       safe_strcat (flags, flsize, str);
830 }
831
832 /* imap_make_msg_set: make an IMAP4rev1 UID message set out of a set of
833  *   headers, given a flag enum to filter on.
834  * Params: idata: IMAP_DATA containing context containing header set
835  *         buf: to write message set into
836  *         buflen: length of buffer
837  *         flag: enum of flag type on which to filter
838  *         changed: include only changed messages in message set
839  * Returns: number of messages in message set (0 if no matches) */
840 int imap_make_msg_set (IMAP_DATA* idata, BUFFER* buf, int flag, int changed)
841 {
842   HEADER** hdrs;        /* sorted local copy */
843   int count = 0;        /* number of messages in message set */
844   int match = 0;        /* whether current message matches flag condition */
845   unsigned int setstart = 0;    /* start of current message range */
846   int n;
847   short oldsort;        /* we clobber reverse, must restore it */
848   /* assuming 32-bit UIDs */
849   char uid[12];
850   int started = 0;
851
852   /* make copy of header pointers to sort in natural order */
853   hdrs = safe_calloc (idata->ctx->msgcount, sizeof (HEADER*));
854   memcpy (hdrs, idata->ctx->hdrs, idata->ctx->msgcount * sizeof (HEADER*));
855
856   if (Sort != SORT_ORDER)
857   {
858     oldsort = Sort;
859     Sort = SORT_ORDER;
860     qsort ((void*) hdrs, idata->ctx->msgcount, sizeof (HEADER*),
861       mutt_get_sort_func (SORT_ORDER));
862     Sort = oldsort;
863   }
864   
865   for (n = 0; n < idata->ctx->msgcount; n++)
866   {
867     match = 0;
868     /* don't include pending expunged messages */
869     if (hdrs[n]->active)
870       switch (flag)
871       {
872         case M_DELETE:
873           if (hdrs[n]->deleted)
874             match = 1;
875           break;
876         case M_TAG:
877           if (hdrs[n]->tagged)
878             match = 1;
879           break;
880       }
881
882     if (match && (!changed || hdrs[n]->changed))
883     {
884       count++;
885       if (setstart == 0)
886       {
887         setstart = HEADER_DATA (hdrs[n])->uid;
888         if (started == 0)
889         {
890           snprintf (uid, sizeof (uid), "%u", HEADER_DATA (hdrs[n])->uid);
891           mutt_buffer_addstr (buf, uid);
892           started = 1;
893         }
894         else
895         {
896           snprintf (uid, sizeof (uid), ",%u", HEADER_DATA (hdrs[n])->uid);
897           mutt_buffer_addstr (buf, uid);
898         }
899       }
900       /* tie up if the last message also matches */
901       else if (n == idata->ctx->msgcount-1)
902       {
903         snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n])->uid);
904         mutt_buffer_addstr (buf, uid);
905       }
906     }
907     /* this message is not expunged and doesn't match. End current set. */
908     else if (setstart && hdrs[n]->active)
909     {
910       if (HEADER_DATA (hdrs[n-1])->uid > setstart)
911       {
912         snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n-1])->uid);
913         mutt_buffer_addstr (buf, uid);
914       }
915       setstart = 0;
916     }
917   }
918
919   FREE (&hdrs);
920
921   return count;
922 }
923
924 /* update the IMAP server to reflect message changes done within mutt.
925  * Arguments
926  *   ctx: the current context
927  *   expunge: 0 or 1 - do expunge? 
928  */
929
930 int imap_sync_mailbox (CONTEXT* ctx, int expunge, int* index_hint)
931 {
932   IMAP_DATA* idata;
933   CONTEXT* appendctx = NULL;
934   BUFFER cmd;
935   char flags[LONG_STRING];
936   char uid[11];
937   int deleted;
938   int n;
939   int err_continue = M_NO;      /* continue on error? */
940   int rc;
941
942   idata = (IMAP_DATA*) ctx->data;
943
944   if (idata->state != IMAP_SELECTED)
945   {
946     dprint (2, (debugfile, "imap_sync_mailbox: no mailbox selected\n"));
947     return -1;
948   }
949
950   /* CLOSE purges deleted messages. If we don't want to purge them, we must
951    * tell imap_close_mailbox not to issue the CLOSE command */
952   if (expunge)
953     idata->noclose = 0;
954   else
955     idata->noclose = 1;
956
957   /* This function is only called when the calling code expects the context
958    * to be changed. */
959   imap_allow_reopen (ctx);
960
961   if ((rc = imap_check_mailbox (ctx, index_hint, 0)) != 0)
962     return rc;
963
964   memset (&cmd, 0, sizeof (cmd));
965
966   /* if we are expunging anyway, we can do deleted messages very quickly... */
967   if (expunge && mutt_bit_isset (idata->rights, IMAP_ACL_DELETE))
968   {
969     mutt_buffer_addstr (&cmd, "UID STORE ");
970     deleted = imap_make_msg_set (idata, &cmd, M_DELETE, 1);
971
972     /* if we have a message set, then let's delete */
973     if (deleted)
974     {
975       mutt_message (_("Marking %d messages deleted..."), deleted);
976       mutt_buffer_addstr (&cmd, " +FLAGS.SILENT (\\Deleted)");
977       /* mark these messages as unchanged so second pass ignores them. Done
978        * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
979       for (n = 0; n < ctx->msgcount; n++)
980         if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
981           ctx->hdrs[n]->active = 0;
982       if (imap_exec (idata, cmd.data, 0) != 0)
983       {
984         mutt_error (_("Expunge failed"));
985         mutt_sleep (1);
986         rc = -1;
987         goto out;
988       }
989     }
990   }
991
992   /* save status changes */
993   for (n = 0; n < ctx->msgcount; n++)
994   {
995     if (ctx->hdrs[n]->active && ctx->hdrs[n]->changed)
996     {
997       ctx->hdrs[n]->changed = 0;
998
999       mutt_message (_("Saving message status flags... [%d/%d]"), n+1,
1000         ctx->msgcount);
1001
1002       snprintf (uid, sizeof (uid), "%u", HEADER_DATA(ctx->hdrs[n])->uid);
1003       cmd.dptr = cmd.data;
1004       mutt_buffer_addstr (&cmd, "UID STORE ");
1005       mutt_buffer_addstr (&cmd, uid);
1006
1007       /* if the message has been rethreaded or attachments have been deleted
1008        * we delete the message and reupload it.
1009        * This works better if we're expunging, of course. */
1010       if (ctx->hdrs[n]->refs_changed || ctx->hdrs[n]->irt_changed ||
1011           ctx->hdrs[n]->attach_del)
1012       {
1013         dprint (3, (debugfile, "imap_sync_mailbox: Attachments to be deleted, falling back to _mutt_save_message\n"));
1014         if (!appendctx)
1015           appendctx = mx_open_mailbox (ctx->path, M_APPEND | M_QUIET, NULL);
1016         if (!appendctx)
1017         {
1018           dprint (1, (debugfile, "imap_sync_mailbox: Error opening mailbox in append mode\n"));
1019         }
1020         else
1021           _mutt_save_message (ctx->hdrs[n], appendctx, 1, 0, 0);
1022       }
1023       flags[0] = '\0';
1024       
1025       imap_set_flag (idata, IMAP_ACL_SEEN, ctx->hdrs[n]->read, "\\Seen ",
1026         flags, sizeof (flags));
1027       imap_set_flag (idata, IMAP_ACL_WRITE, ctx->hdrs[n]->flagged,
1028         "\\Flagged ", flags, sizeof (flags));
1029       imap_set_flag (idata, IMAP_ACL_WRITE, ctx->hdrs[n]->replied,
1030         "\\Answered ", flags, sizeof (flags));
1031       imap_set_flag (idata, IMAP_ACL_DELETE, ctx->hdrs[n]->deleted,
1032         "\\Deleted ", flags, sizeof (flags));
1033
1034       /* now make sure we don't lose custom tags */
1035       if (mutt_bit_isset (idata->rights, IMAP_ACL_WRITE))
1036         imap_add_keywords (flags, ctx->hdrs[n], idata->flags, sizeof (flags));
1037       
1038       mutt_remove_trailing_ws (flags);
1039       
1040       /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1041        * explicitly revoke all system flags (if we have permission) */
1042       if (!*flags)
1043       {
1044         imap_set_flag (idata, IMAP_ACL_SEEN, 1, "\\Seen ", flags, sizeof (flags));
1045         imap_set_flag (idata, IMAP_ACL_WRITE, 1, "\\Flagged ", flags, sizeof (flags));
1046         imap_set_flag (idata, IMAP_ACL_WRITE, 1, "\\Answered ", flags, sizeof (flags));
1047         imap_set_flag (idata, IMAP_ACL_DELETE, 1, "\\Deleted ", flags, sizeof (flags));
1048
1049         mutt_remove_trailing_ws (flags);
1050
1051         mutt_buffer_addstr (&cmd, " -FLAGS.SILENT (");
1052       }
1053       else
1054         mutt_buffer_addstr (&cmd, " FLAGS.SILENT (");
1055       
1056       mutt_buffer_addstr (&cmd, flags);
1057       mutt_buffer_addstr (&cmd, ")");
1058
1059       /* dumb hack for bad UW-IMAP 4.7 servers spurious FLAGS updates */
1060       ctx->hdrs[n]->active = 0;
1061
1062       /* after all this it's still possible to have no flags, if you
1063        * have no ACL rights */
1064       if (*flags && (imap_exec (idata, cmd.data, 0) != 0) &&
1065         (err_continue != M_YES))
1066       {
1067         err_continue = imap_continue ("imap_sync_mailbox: STORE failed",
1068           idata->cmd.buf);
1069         if (err_continue != M_YES)
1070         {
1071           rc = -1;
1072           goto out;
1073         }
1074       }
1075
1076       ctx->hdrs[n]->active = 1;
1077     }
1078   }
1079   ctx->changed = 0;
1080
1081   /* We must send an EXPUNGE command if we're not closing. */
1082   if (expunge && !(ctx->closing) &&
1083       mutt_bit_isset(idata->rights, IMAP_ACL_DELETE))
1084   {
1085     mutt_message _("Expunging messages from server...");
1086     /* Set expunge bit so we don't get spurious reopened messages */
1087     idata->reopen |= IMAP_EXPUNGE_EXPECTED;
1088     if (imap_exec (idata, "EXPUNGE", 0) != 0)
1089     {
1090       imap_error (_("imap_sync_mailbox: EXPUNGE failed"), idata->cmd.buf);
1091       rc = -1;
1092       goto out;
1093     }
1094   }
1095
1096   rc = 0;
1097  out:
1098   if (cmd.data)
1099     FREE (&cmd.data);
1100   if (appendctx)
1101   {
1102     mx_fastclose_mailbox (appendctx);
1103     FREE (&appendctx);
1104   }
1105   return rc;
1106 }
1107
1108 /* imap_close_mailbox: issue close command if neccessary, reset IMAP_DATA */
1109 void imap_close_mailbox (CONTEXT* ctx)
1110 {
1111   IMAP_DATA* idata;
1112   int i;
1113
1114   idata = (IMAP_DATA*) ctx->data;
1115   /* Check to see if the mailbox is actually open */
1116   if (!idata)
1117     return;
1118
1119   if ((idata->status != IMAP_FATAL) &&
1120       (idata->state == IMAP_SELECTED) &&
1121       (ctx == idata->ctx))
1122   {
1123     if (!(idata->noclose) && imap_exec (idata, "CLOSE", 0))
1124       mutt_error (_("CLOSE failed"));
1125
1126     idata->reopen &= IMAP_REOPEN_ALLOW;
1127     idata->state = IMAP_AUTHENTICATED;
1128     FREE (&(idata->mailbox));
1129     mutt_free_list (&idata->flags);
1130     idata->ctx = NULL;
1131   }
1132
1133   /* free IMAP part of headers */
1134   for (i = 0; i < ctx->msgcount; i++)
1135     imap_free_header_data (&(ctx->hdrs[i]->data));
1136
1137   for (i = 0; i < IMAP_CACHE_LEN; i++)
1138   {
1139     if (idata->cache[i].path)
1140     {
1141       unlink (idata->cache[i].path);
1142       FREE (&idata->cache[i].path);
1143     }
1144   }
1145 }
1146
1147 /* use the NOOP command to poll for new mail
1148  *
1149  * return values:
1150  *      M_REOPENED      mailbox has been externally modified
1151  *      M_NEW_MAIL      new mail has arrived!
1152  *      0               no change
1153  *      -1              error
1154  */
1155 int imap_check_mailbox (CONTEXT *ctx, int *index_hint, int force)
1156 {
1157   /* overload keyboard timeout to avoid many mailbox checks in a row.
1158    * Most users don't like having to wait exactly when they press a key. */
1159   IMAP_DATA* idata;
1160   int result = 0;
1161
1162   idata = (IMAP_DATA*) ctx->data;
1163
1164   if ((force || time(NULL) > idata->lastread + Timeout)
1165       && imap_exec (idata, "NOOP", 0) != 0)
1166     return -1;
1167
1168   /* We call this even when we haven't run NOOP in case we have pending
1169    * changes to process, since we can reopen here. */
1170   imap_cmd_finish (idata);
1171
1172   if (idata->check_status & IMAP_EXPUNGE_PENDING)
1173     result = M_REOPENED;
1174   else if (idata->check_status & IMAP_NEWMAIL_PENDING)
1175     result = M_NEW_MAIL;
1176   else if (idata->check_status & IMAP_FLAGS_PENDING)
1177     result = M_FLAGS;
1178
1179   idata->check_status = 0;
1180
1181   return result;
1182 }
1183
1184 /* returns count of recent messages if new = 1, else count of total messages.
1185  * (useful for at least postponed function)
1186  * Question of taste: use RECENT or UNSEEN for new?
1187  *   0+   number of messages in mailbox
1188  *  -1    error while polling mailboxes
1189  */
1190 int imap_mailbox_check (char* path, int new)
1191 {
1192   CONNECTION *conn;
1193   IMAP_DATA *idata;
1194   char buf[LONG_STRING];
1195   char mbox[LONG_STRING];
1196   char mbox_unquoted[LONG_STRING];
1197   char *s;
1198   int msgcount = 0;
1199   int connflags = 0;
1200   IMAP_MBOX mx;
1201   int rc;
1202   
1203   if (imap_parse_path (path, &mx))
1204     return -1;
1205
1206   /* If imap_passive is set, don't open a connection to check for new mail */
1207   if (option (OPTIMAPPASSIVE))
1208     connflags = M_IMAP_CONN_NONEW;
1209
1210   if (!(idata = imap_conn_find (&(mx.account), connflags)))
1211   {
1212     FREE (&mx.mbox);
1213     return -1;
1214   }
1215   conn = idata->conn;
1216
1217   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1218   FREE (&mx.mbox);
1219
1220   imap_munge_mbox_name (mbox, sizeof(mbox), buf);
1221   strfcpy (mbox_unquoted, buf, sizeof (mbox_unquoted));
1222
1223   /* The draft IMAP implementor's guide warns againts using the STATUS
1224    * command on a mailbox that you have selected 
1225    */
1226
1227   if (mutt_strcmp (mbox_unquoted, idata->mailbox) == 0
1228       || (ascii_strcasecmp (mbox_unquoted, "INBOX") == 0
1229           && mutt_strcasecmp (mbox_unquoted, idata->mailbox) == 0))
1230   {
1231     strfcpy (buf, "NOOP", sizeof (buf));
1232   }
1233   else if (mutt_bit_isset(idata->capabilities,IMAP4REV1) ||
1234            mutt_bit_isset(idata->capabilities,STATUS))
1235   {                             
1236     snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox,
1237       new ? "RECENT" : "MESSAGES");
1238   }
1239   else
1240     /* Server does not support STATUS, and this is not the current mailbox.
1241      * There is no lightweight way to check recent arrivals */
1242     return -1;
1243
1244   imap_cmd_start (idata, buf);
1245
1246   do 
1247   {
1248     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
1249       break;
1250
1251     s = imap_next_word (idata->cmd.buf);
1252     if (ascii_strncasecmp ("STATUS", s, 6) == 0)
1253     {
1254       s = imap_next_word (s);
1255       /* The mailbox name may or may not be quoted here. We could try to 
1256        * munge the server response and compare with quoted (or vise versa)
1257        * but it is probably more efficient to just strncmp against both. */
1258       if (mutt_strncmp (mbox_unquoted, s, mutt_strlen (mbox_unquoted)) == 0
1259           || mutt_strncmp (mbox, s, mutt_strlen (mbox)) == 0)
1260       {
1261         s = imap_next_word (s);
1262         s = imap_next_word (s);
1263         if (isdigit ((unsigned char) *s))
1264         {
1265           if (*s != '0')
1266           {
1267             msgcount = atoi(s);
1268             dprint (2, (debugfile, "%d new messages in %s\n", msgcount, path));
1269           }
1270         }
1271       }
1272       else
1273         dprint (1, (debugfile, "imap_mailbox_check: STATUS response doesn't match requested mailbox.\n"));
1274     }
1275   }
1276   while (rc == IMAP_CMD_CONTINUE);
1277
1278   return msgcount;
1279 }
1280
1281 /* all this listing/browsing is a mess. I don't like that name is a pointer
1282  *   into idata->buf (used to be a pointer into the passed in buffer, just
1283  *   as bad), nor do I like the fact that the fetch is done here. This
1284  *   code can't possibly handle non-LIST untagged responses properly.
1285  *   FIXME. ?! */
1286 int imap_parse_list_response(IMAP_DATA* idata, char **name, int *noselect,
1287   int *noinferiors, char *delim)
1288 {
1289   char *s;
1290   long bytes;
1291   int rc;
1292
1293   *name = NULL;
1294
1295   rc = imap_cmd_step (idata);
1296   if (rc == IMAP_CMD_OK)
1297     return 0;
1298   if (rc != IMAP_CMD_CONTINUE)
1299     return -1;
1300
1301   s = imap_next_word (idata->cmd.buf);
1302   if ((ascii_strncasecmp ("LIST", s, 4) == 0) ||
1303       (ascii_strncasecmp ("LSUB", s, 4) == 0))
1304   {
1305     *noselect = 0;
1306     *noinferiors = 0;
1307       
1308     s = imap_next_word (s); /* flags */
1309     if (*s == '(')
1310     {
1311       char *ep;
1312
1313       s++;
1314       ep = s;
1315       while (*ep && *ep != ')') ep++;
1316       do
1317       {
1318         if (!ascii_strncasecmp (s, "\\NoSelect", 9))
1319           *noselect = 1;
1320         if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
1321           *noinferiors = 1;
1322         /* See draft-gahrns-imap-child-mailbox-?? */
1323         if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
1324           *noinferiors = 1;
1325         if (*s != ')')
1326           s++;
1327         while (*s && *s != '\\' && *s != ')') s++;
1328       } while (s != ep);
1329     }
1330     else
1331       return 0;
1332     s = imap_next_word (s); /* delim */
1333     /* Reset the delimiter, this can change */
1334     if (ascii_strncasecmp (s, "NIL", 3))
1335     {
1336       if (s && s[0] == '\"' && s[1] && s[2] == '\"')
1337         *delim = s[1];
1338       else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2] && s[3] == '\"')
1339         *delim = s[2];
1340     }
1341     s = imap_next_word (s); /* name */
1342     if (s && *s == '{') /* Literal */
1343     { 
1344       if (imap_get_literal_count(idata->cmd.buf, &bytes) < 0)
1345         return -1;
1346       if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1347         return -1;
1348       *name = idata->cmd.buf;
1349     }
1350     else
1351       *name = s;
1352   }
1353
1354   return 0;
1355 }
1356
1357 int imap_subscribe (char *path, int subscribe)
1358 {
1359   CONNECTION *conn;
1360   IMAP_DATA *idata;
1361   char buf[LONG_STRING];
1362   char mbox[LONG_STRING];
1363   IMAP_MBOX mx;
1364
1365   if (!mx_is_imap (path) || imap_parse_path (path, &mx))
1366   {
1367     mutt_error (_("Bad mailbox name"));
1368     return -1;
1369   }
1370   
1371
1372   if (!(idata = imap_conn_find (&(mx.account), 0)))
1373     goto fail;
1374   
1375   conn = idata->conn;
1376
1377   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1378   if (subscribe)
1379     mutt_message (_("Subscribing to %s..."), buf);
1380   else
1381     mutt_message (_("Unsubscribing to %s..."), buf);
1382   imap_munge_mbox_name (mbox, sizeof(mbox), buf);
1383
1384   snprintf (buf, sizeof (buf), "%s %s", subscribe ? "SUBSCRIBE" :
1385     "UNSUBSCRIBE", mbox);
1386
1387   if (imap_exec (idata, buf, 0) < 0)
1388     goto fail;
1389
1390   FREE (&mx.mbox);
1391   return 0;
1392
1393  fail:
1394   FREE (&mx.mbox);
1395   return -1;
1396 }
1397
1398 /* imap_complete: given a partial IMAP folder path, return a string which
1399  *   adds as much to the path as is unique */
1400 int imap_complete(char* dest, size_t dlen, char* path) {
1401   CONNECTION* conn;
1402   IMAP_DATA* idata;
1403   char list[LONG_STRING];
1404   char buf[LONG_STRING];
1405   char* list_word = NULL;
1406   int noselect, noinferiors;
1407   char delim;
1408   char completion[LONG_STRING];
1409   int clen, matchlen = 0;
1410   int completions = 0;
1411   int pos = 0;
1412   IMAP_MBOX mx;
1413
1414   /* verify passed in path is an IMAP path */
1415   if (imap_parse_path (path, &mx))
1416   {
1417     dprint(2, (debugfile, "imap_complete: bad path %s\n", path));
1418     return -1;
1419   }
1420
1421   /* don't open a new socket just for completion */
1422   if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NONEW)))
1423     goto fail;
1424   conn = idata->conn;
1425
1426   /* reformat path for IMAP list, and append wildcard */
1427   /* don't use INBOX in place of "" */
1428   if (mx.mbox && mx.mbox[0])
1429     imap_fix_path (idata, mx.mbox, list, sizeof(list));
1430   else
1431     list[0] = '\0';
1432
1433   /* fire off command */
1434   snprintf (buf, sizeof(buf), "%s \"\" \"%s%%\"",
1435     option (OPTIMAPLSUB) ? "LSUB" : "LIST", list);
1436
1437   imap_cmd_start (idata, buf);
1438
1439   /* and see what the results are */
1440   strfcpy (completion, NONULL(mx.mbox), sizeof(completion));
1441   do
1442   {
1443     if (imap_parse_list_response(idata, &list_word, &noselect, &noinferiors,
1444         &delim))
1445       break;
1446
1447     if (list_word)
1448     {
1449       /* store unquoted */
1450       imap_unmunge_mbox_name (list_word);
1451
1452       /* if the folder isn't selectable, append delimiter to force browse
1453        * to enter it on second tab. */
1454       if (noselect)
1455       {
1456         clen = strlen(list_word);
1457         list_word[clen++] = delim;
1458         list_word[clen] = '\0';
1459       }
1460       /* copy in first word */
1461       if (!completions)
1462       {
1463         strfcpy (completion, list_word, sizeof(completion));
1464         matchlen = strlen (completion);
1465         completions++;
1466         continue;
1467       }
1468
1469       pos = 0;
1470       while (pos < matchlen && list_word[pos] &&
1471           completion[pos] == list_word[pos])
1472         pos++;
1473       completion[pos] = '\0';
1474       matchlen = pos;
1475
1476       completions++;
1477     }
1478   }
1479   while (ascii_strncmp(idata->cmd.seq, idata->cmd.buf, SEQLEN));
1480
1481   if (completions)
1482   {
1483     /* reformat output */
1484     imap_qualify_path (dest, dlen, &mx, completion);
1485     mutt_pretty_mailbox (dest);
1486
1487     FREE (&mx.mbox);
1488     return 0;
1489   }
1490
1491  fail:
1492   FREE (&mx.mbox);
1493   return -1;
1494 }
1495
1496 /* reconnect if connection was lost */
1497 int imap_reconnect(CONTEXT* ctx) {
1498   IMAP_DATA* imap_data = (IMAP_DATA *)ctx->data;
1499
1500   if (imap_data) {
1501     if (imap_data->status == IMAP_CONNECTED)
1502       return -1;
1503   }
1504
1505   if (query_quadoption(OPT_IMAPRECONNECT,_("Connection lost. Reconnect to IMAP server?")) != M_YES)
1506     return -1;
1507
1508   mx_open_mailbox(ctx->path,0,ctx);
1509   return 0;
1510 }
1511