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