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