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