bbaf57d7be0800af7447fe91d9758ea5173a8a59
[apps/madmutt.git] / mx.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5  *
6  * This file is part of mutt-ng, see http://www.muttng.org/.
7  * It's licensed under the GNU General Public License,
8  * please see the file GPL in the top level source directory.
9  */
10
11 #if HAVE_CONFIG_H
12 # include "config.h"
13 #endif
14
15 #include <lib-lib/mem.h>
16 #include <lib-lib/macros.h>
17
18 #include "mutt.h"
19 #include "buffy.h"
20 #include "ascii.h"
21 #include "mx.h"
22 #include "mbox.h"
23 #include "mh.h"
24 #include "rfc2047.h"
25 #include "sort.h"
26 #include "thread.h"
27 #include "copy.h"
28 #include "keymap.h"
29 #include "url.h"
30 #include "sidebar.h"
31
32 #ifdef USE_COMPRESSED
33 #include "compress.h"
34 #endif
35
36 #ifdef USE_IMAP
37 #include "imap/imap.h"
38 #include "imap/mx_imap.h"
39 #endif
40
41 #ifdef USE_POP
42 #include "pop/pop.h"
43 #include "pop/mx_pop.h"
44 #endif
45
46 #ifdef USE_NNTP
47 #include "nntp/nntp.h"
48 #include "nntp/mx_nntp.h"
49 #endif
50
51 #ifdef USE_DOTLOCK
52 #include "dotlock.h"
53 #endif
54
55 #include "mutt_crypt.h"
56
57 #include "lib/str.h"
58 #include "lib/list.h"
59 #include "lib/debug.h"
60
61 #include <dirent.h>
62 #include <fcntl.h>
63 #include <sys/file.h>
64 #include <sys/stat.h>
65 #include <errno.h>
66 #include <unistd.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <ctype.h>
70 #include <utime.h>
71
72 static list2_t* MailboxFormats = NULL;
73 #define MX_COMMAND(idx,cmd) ((mx_t*) MailboxFormats->data[idx])->cmd
74 #define MX_IDX(idx) (idx >= 0 && idx < MailboxFormats->length)
75
76 #define mutt_is_spool(s)  (str_cmp (Spoolfile, s) == 0)
77
78 #ifdef USE_DOTLOCK
79 /* parameters: 
80  * path - file to lock
81  * retry - should retry if unable to lock?
82  */
83
84 #ifdef DL_STANDALONE
85
86 static int invoke_dotlock (const char *path, int dummy, int flags, int retry)
87 {
88   char cmd[LONG_STRING + _POSIX_PATH_MAX];
89   char f[SHORT_STRING + _POSIX_PATH_MAX];
90   char r[SHORT_STRING];
91
92   if (flags & DL_FL_RETRY)
93     snprintf (r, sizeof (r), "-r %d ", retry ? MAXLOCKATTEMPT : 0);
94
95   mutt_quote_filename (f, sizeof (f), path);
96
97   snprintf (cmd, sizeof (cmd),
98             "%s %s%s%s%s%s%s%s",
99             NONULL (MuttDotlock),
100             flags & DL_FL_TRY ? "-t " : "",
101             flags & DL_FL_UNLOCK ? "-u " : "",
102             flags & DL_FL_USEPRIV ? "-p " : "",
103             flags & DL_FL_FORCE ? "-f " : "",
104             flags & DL_FL_UNLINK ? "-d " : "",
105             flags & DL_FL_RETRY ? r : "", f);
106
107   return mutt_system (cmd);
108 }
109
110 #else
111
112 #define invoke_dotlock dotlock_invoke
113
114 #endif
115
116 static int dotlock_file (const char *path, int fd, int retry)
117 {
118   int r;
119   int flags = DL_FL_USEPRIV | DL_FL_RETRY;
120
121   if (retry)
122     retry = 1;
123
124 retry_lock:
125   if ((r = invoke_dotlock (path, fd, flags, retry)) == DL_EX_EXIST) {
126     if (!option (OPTNOCURSES)) {
127       char msg[LONG_STRING];
128
129       snprintf (msg, sizeof (msg),
130                 _("Lock count exceeded, remove lock for %s?"), path);
131       if (retry && mutt_yesorno (msg, M_YES) == M_YES) {
132         flags |= DL_FL_FORCE;
133         retry--;
134         mutt_clear_error ();
135         goto retry_lock;
136       }
137     }
138     else {
139       mutt_error (_("Can't dotlock %s.\n"), path);
140     }
141   }
142   return (r == DL_EX_OK ? 0 : -1);
143 }
144
145 static int undotlock_file (const char *path, int fd)
146 {
147   return (invoke_dotlock (path, fd, DL_FL_USEPRIV | DL_FL_UNLOCK, 0) ==
148           DL_EX_OK ? 0 : -1);
149 }
150
151 #endif /* USE_DOTLOCK */
152
153 /* looks up index of type for path in MailboxFormats */
154 static int mx_get_idx (const char* path) {
155   int i = 0, t = 0;
156   struct stat st;
157
158   /* first, test all non-local folders to avoid stat() call */
159   for (i = 0; i < MailboxFormats->length; i++) {
160     if (!MX_COMMAND(i,local))
161       t = MX_COMMAND(i,mx_is_magic)(path, NULL);
162     if (t >= 1)
163       return (t-1);
164   }
165   if (stat (path, &st) == 0) {
166     /* if stat() succeeded, keep testing until success and
167      * pass stat() info so that we only need to do it once */
168     for (i = 0; i < MailboxFormats->length; i++) {
169       if (MX_COMMAND(i,local))
170         t = MX_COMMAND(i,mx_is_magic)(path, &st);
171       if (t >= 1)
172         return (t-1);
173     }
174   }
175   return (-1);
176 }
177
178 /* Args:
179  *      excl            if excl != 0, request an exclusive lock
180  *      dot             if dot != 0, try to dotlock the file
181  *      timeout         should retry locking?
182  */
183 int mx_lock_file (const char *path, int fd, int excl, int dot, int timeout)
184 {
185 #if defined (USE_FCNTL) || defined (USE_FLOCK)
186   int count;
187   int attempt;
188   struct stat prev_sb;
189 #endif
190   int r = 0;
191
192 #ifdef USE_FCNTL
193   struct flock lck;
194
195
196   memset (&lck, 0, sizeof (struct flock));
197   lck.l_type = excl ? F_WRLCK : F_RDLCK;
198   lck.l_whence = SEEK_SET;
199
200   count = 0;
201   attempt = 0;
202   prev_sb.st_size = 0;
203   while (fcntl (fd, F_SETLK, &lck) == -1) {
204     struct stat sb;
205
206     debug_print (1, ("fcntl errno %d.\n", errno));
207     if (errno != EAGAIN && errno != EACCES) {
208       mutt_perror ("fcntl");
209       return (-1);
210     }
211
212     if (fstat (fd, &sb) != 0)
213       sb.st_size = 0;
214
215     if (count == 0)
216       prev_sb = sb;
217
218     /* only unlock file if it is unchanged */
219     if (prev_sb.st_size == sb.st_size
220         && ++count >= (timeout ? MAXLOCKATTEMPT : 0)) {
221       if (timeout)
222         mutt_error _("Timeout exceeded while attempting fcntl lock!");
223
224       return (-1);
225     }
226
227     prev_sb = sb;
228
229     mutt_message (_("Waiting for fcntl lock... %d"), ++attempt);
230     sleep (1);
231   }
232 #endif /* USE_FCNTL */
233
234 #ifdef USE_FLOCK
235   count = 0;
236   attempt = 0;
237   while (flock (fd, (excl ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1) {
238     struct stat sb;
239
240     if (errno != EWOULDBLOCK) {
241       mutt_perror ("flock");
242       r = -1;
243       break;
244     }
245
246     if (fstat (fd, &sb) != 0)
247       sb.st_size = 0;
248
249     if (count == 0)
250       prev_sb = sb;
251
252     /* only unlock file if it is unchanged */
253     if (prev_sb.st_size == sb.st_size
254         && ++count >= (timeout ? MAXLOCKATTEMPT : 0)) {
255       if (timeout)
256         mutt_error _("Timeout exceeded while attempting flock lock!");
257
258       r = -1;
259       break;
260     }
261
262     prev_sb = sb;
263
264     mutt_message (_("Waiting for flock attempt... %d"), ++attempt);
265     sleep (1);
266   }
267 #endif /* USE_FLOCK */
268
269 #ifdef USE_DOTLOCK
270   if (r == 0 && dot)
271     r = dotlock_file (path, fd, timeout);
272 #endif /* USE_DOTLOCK */
273
274   if (r == -1) {
275     /* release any other locks obtained in this routine */
276
277 #ifdef USE_FCNTL
278     lck.l_type = F_UNLCK;
279     fcntl (fd, F_SETLK, &lck);
280 #endif /* USE_FCNTL */
281
282 #ifdef USE_FLOCK
283     flock (fd, LOCK_UN);
284 #endif /* USE_FLOCK */
285
286     return (-1);
287   }
288
289   return 0;
290 }
291
292 int mx_unlock_file (const char *path, int fd, int dot)
293 {
294 #ifdef USE_FCNTL
295   struct flock unlockit;
296
297   memset (&unlockit, 0, sizeof (struct flock));
298   unlockit.l_type = F_UNLCK;
299   unlockit.l_whence = SEEK_SET;
300   fcntl (fd, F_SETLK, &unlockit);
301 #endif
302
303 #ifdef USE_FLOCK
304   flock (fd, LOCK_UN);
305 #endif
306
307 #ifdef USE_DOTLOCK
308   if (dot)
309     undotlock_file (path, fd);
310 #endif
311
312   return 0;
313 }
314
315 void mx_unlink_empty (const char *path)
316 {
317   int fd;
318
319 #ifndef USE_DOTLOCK
320   struct stat sb;
321 #endif
322
323   if ((fd = open (path, O_RDWR)) == -1)
324     return;
325
326   if (mx_lock_file (path, fd, 1, 0, 1) == -1) {
327     close (fd);
328     return;
329   }
330
331 #ifdef USE_DOTLOCK
332   invoke_dotlock (path, fd, DL_FL_UNLINK, 1);
333 #else
334   if (fstat (fd, &sb) == 0 && sb.st_size == 0)
335     unlink (path);
336 #endif
337
338   mx_unlock_file (path, fd, 0);
339   close (fd);
340 }
341
342 /* try to figure out what type of mailbox ``path'' is */
343 int mx_get_magic (const char *path) {
344   int i = 0;
345
346   if (str_len (path) == 0)
347     return (-1);
348   if ((i = mx_get_idx (path)) >= 0)
349     return (MX_COMMAND(i,type));
350   return (-1);
351 }
352
353 int mx_is_local (int m) {
354   if (!MX_IDX(m))
355     return (0);
356   return (MX_COMMAND(m,local));
357 }
358
359 /*
360  * set DefaultMagic to the given value
361  */
362 int mx_set_magic (const char *s)
363 {
364   if (ascii_strcasecmp (s, "mbox") == 0)
365     DefaultMagic = M_MBOX;
366   else if (ascii_strcasecmp (s, "mmdf") == 0)
367     DefaultMagic = M_MMDF;
368   else if (ascii_strcasecmp (s, "mh") == 0)
369     DefaultMagic = M_MH;
370   else if (ascii_strcasecmp (s, "maildir") == 0)
371     DefaultMagic = M_MAILDIR;
372   else
373     return (-1);
374
375   return 0;
376 }
377
378 /* mx_access: Wrapper for access, checks permissions on a given mailbox.
379  *   We may be interested in using ACL-style flags at some point, currently
380  *   we use the normal access() flags. */
381 int mx_access (const char *path, int flags)
382 {
383   int i = 0;
384
385   if ((i = mx_get_idx (path)) >= 0 && MX_COMMAND(i,mx_access))
386     return (MX_COMMAND(i,mx_access)(path,flags));
387   return (0);
388 }
389
390 static int mx_open_mailbox_append (CONTEXT * ctx, int flags)
391 {
392   struct stat sb;
393
394 #ifdef USE_COMPRESSED
395   /* special case for appending to compressed folders -
396    * even if we can not open them for reading */
397   if (mutt_can_append_compressed (ctx->path))
398     mutt_open_append_compressed (ctx);
399 #endif
400
401   ctx->append = 1;
402
403 #ifdef USE_IMAP
404
405   if (mx_get_magic (ctx->path) == M_IMAP)
406     return imap_open_mailbox_append (ctx);
407
408 #endif
409
410   if (stat (ctx->path, &sb) == 0) {
411     ctx->magic = mx_get_magic (ctx->path);
412
413     switch (ctx->magic) {
414     case 0:
415       mutt_error (_("%s is not a mailbox."), ctx->path);
416       /* fall through */
417     case -1:
418       return (-1);
419     }
420   }
421   else if (errno == ENOENT) {
422     ctx->magic = DefaultMagic;
423
424     if (ctx->magic == M_MH || ctx->magic == M_MAILDIR) {
425       char tmp[_POSIX_PATH_MAX];
426
427       if (mkdir (ctx->path, S_IRWXU)) {
428         mutt_perror (ctx->path);
429         return (-1);
430       }
431
432       if (ctx->magic == M_MAILDIR) {
433         snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
434         if (mkdir (tmp, S_IRWXU)) {
435           mutt_perror (tmp);
436           rmdir (ctx->path);
437           return (-1);
438         }
439
440         snprintf (tmp, sizeof (tmp), "%s/new", ctx->path);
441         if (mkdir (tmp, S_IRWXU)) {
442           mutt_perror (tmp);
443           snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
444           rmdir (tmp);
445           rmdir (ctx->path);
446           return (-1);
447         }
448         snprintf (tmp, sizeof (tmp), "%s/tmp", ctx->path);
449         if (mkdir (tmp, S_IRWXU)) {
450           mutt_perror (tmp);
451           snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
452           rmdir (tmp);
453           snprintf (tmp, sizeof (tmp), "%s/new", ctx->path);
454           rmdir (tmp);
455           rmdir (ctx->path);
456           return (-1);
457         }
458       }
459       else {
460         int i;
461
462         snprintf (tmp, sizeof (tmp), "%s/.mh_sequences", ctx->path);
463         if ((i = creat (tmp, S_IRWXU)) == -1) {
464           mutt_perror (tmp);
465           rmdir (ctx->path);
466           return (-1);
467         }
468         close (i);
469       }
470     }
471   }
472   else {
473     mutt_perror (ctx->path);
474     return (-1);
475   }
476
477   switch (ctx->magic) {
478   case M_MBOX:
479   case M_MMDF:
480     if ((ctx->fp =
481          safe_fopen (ctx->path, flags & M_NEWFOLDER ? "w" : "a")) == NULL
482         || mbox_lock_mailbox (ctx, 1, 1) != 0) {
483       if (!ctx->fp)
484         mutt_perror (ctx->path);
485       else {
486         mutt_error (_("Couldn't lock %s\n"), ctx->path);
487         safe_fclose (&ctx->fp);
488       }
489       return (-1);
490     }
491     fseeko (ctx->fp, 0, 2);
492     break;
493
494   case M_MH:
495   case M_MAILDIR:
496     /* nothing to do */
497     break;
498
499   default:
500     return (-1);
501   }
502
503   return 0;
504 }
505
506 /*
507  * open a mailbox and parse it
508  *
509  * Args:
510  *      flags   M_NOSORT        do not sort mailbox
511  *              M_APPEND        open mailbox for appending
512  *              M_READONLY      open mailbox in read-only mode
513  *              M_QUIET         only print error messages
514  *      ctx     if non-null, context struct to use
515  */
516 CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT * pctx)
517 {
518   CONTEXT *ctx = pctx;
519   int rc;
520
521   if (!ctx)
522     ctx = p_new(CONTEXT, 1);
523   p_clear(ctx, 1);
524   ctx->path = str_dup (path);
525
526   ctx->msgnotreadyet = -1;
527   ctx->collapsed = 0;
528
529   if (flags & M_QUIET)
530     ctx->quiet = 1;
531   if (flags & M_READONLY)
532     ctx->readonly = 1;
533   if (flags & M_COUNT)
534     ctx->counting = 1;
535
536   if (flags & (M_APPEND | M_NEWFOLDER)) {
537     if (mx_open_mailbox_append (ctx, flags) != 0) {
538       mx_fastclose_mailbox (ctx);
539       if (!pctx)
540         p_delete(&ctx);
541       return NULL;
542     }
543     return ctx;
544   }
545
546   if (!MX_IDX(ctx->magic-1))
547     ctx->magic = mx_get_magic (path);
548
549 #ifdef USE_COMPRESSED
550   if (ctx->magic == M_COMPRESSED)
551     mutt_open_read_compressed (ctx);
552 #endif
553
554   if (ctx->magic == 0)
555     mutt_error (_("%s is not a mailbox."), path);
556
557   if (ctx->magic == -1)
558     mutt_perror (path);
559
560   if (ctx->magic <= 0) {
561     mx_fastclose_mailbox (ctx);
562     if (!pctx)
563       p_delete(&ctx);
564     return (NULL);
565   }
566
567   /* if the user has a `push' command in their .muttrc, or in a folder-hook,
568    * it will cause the progress messages not to be displayed because
569    * mutt_refresh() will think we are in the middle of a macro.  so set a
570    * flag to indicate that we should really refresh the screen.
571    */
572   set_option (OPTFORCEREFRESH);
573
574   if (!ctx->quiet)
575     mutt_message (_("Reading %s..."), ctx->path);
576
577   rc = MX_COMMAND(ctx->magic-1,mx_open_mailbox)(ctx);
578
579   if (rc == 0) {
580     if ((flags & M_NOSORT) == 0) {
581       /* avoid unnecessary work since the mailbox is completely unthreaded
582          to begin with */
583       unset_option (OPTSORTSUBTHREADS);
584       unset_option (OPTNEEDRESCORE);
585       mutt_sort_headers (ctx, 1);
586     }
587     if (!ctx->quiet)
588       mutt_clear_error ();
589   }
590   else {
591     mx_fastclose_mailbox (ctx);
592     if (!pctx)
593       p_delete(&ctx);
594   }
595
596   unset_option (OPTFORCEREFRESH);
597   return (ctx);
598 }
599
600 /* free up memory associated with the mailbox context */
601 void mx_fastclose_mailbox (CONTEXT * ctx)
602 {
603   int i;
604
605   if (!ctx)
606     return;
607
608   if (MX_IDX(ctx->magic-1) && MX_COMMAND(ctx->magic-1,mx_fastclose_mailbox))
609     MX_COMMAND(ctx->magic-1,mx_fastclose_mailbox(ctx));
610   if (ctx->subj_hash)
611     hash_destroy (&ctx->subj_hash, NULL);
612   if (ctx->id_hash)
613     hash_destroy (&ctx->id_hash, NULL);
614   mutt_clear_threads (ctx);
615   for (i = 0; i < ctx->msgcount; i++)
616     mutt_free_header (&ctx->hdrs[i]);
617   p_delete(&ctx->hdrs);
618   p_delete(&ctx->v2r);
619 #ifdef USE_COMPRESSED
620   if (ctx->compressinfo)
621     mutt_fast_close_compressed (ctx);
622 #endif
623   p_delete(&ctx->path);
624   p_delete(&ctx->pattern);
625   if (ctx->limit_pattern)
626     mutt_pattern_free (&ctx->limit_pattern);
627   safe_fclose (&ctx->fp);
628   memset (ctx, 0, sizeof (CONTEXT));
629 }
630
631 /* save changes to disk */
632 static int sync_mailbox (CONTEXT * ctx, int *index_hint)
633 {
634   int rc = -1;
635
636   if (!ctx->quiet)
637     mutt_message (_("Writing %s..."), ctx->path);
638
639   if (MX_IDX(ctx->magic-1))
640     /* the 1 is only of interest for IMAP and means EXPUNGE */
641     rc = MX_COMMAND(ctx->magic-1,mx_sync_mailbox(ctx,1,index_hint));
642
643 #ifdef USE_COMPRESSED
644   if (rc == 0 && ctx->compressinfo)
645     return mutt_sync_compressed (ctx);
646 #endif
647
648   return rc;
649 }
650
651 /* move deleted mails to the trash folder */
652 static int trash_append (CONTEXT * ctx)
653 {
654   CONTEXT *ctx_trash;
655   int i = 0;
656   struct stat st, stc;
657
658   if (!TrashPath || !ctx->deleted ||
659       (ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH)))
660     return 0;
661
662   for (; i < ctx->msgcount && (!ctx->hdrs[i]->deleted ||
663                                ctx->hdrs[i]->appended); i++);
664   if (i == ctx->msgcount)
665     return 0;                   /* nothing to be done */
666
667   if (mutt_save_confirm (TrashPath, &st) != 0) {
668     mutt_error _("message(s) not deleted");
669
670     return -1;
671   }
672
673   if (lstat (ctx->path, &stc) == 0 && stc.st_ino == st.st_ino
674       && stc.st_dev == st.st_dev && stc.st_rdev == st.st_rdev)
675     return 0;                   /* we are in the trash folder: simple sync */
676
677   if ((ctx_trash = mx_open_mailbox (TrashPath, M_APPEND, NULL)) != NULL) {
678     for (i = 0; i < ctx->msgcount; i++)
679       if (ctx->hdrs[i]->deleted && !ctx->hdrs[i]->appended
680           && !ctx->hdrs[i]->purged
681           && mutt_append_message (ctx_trash, ctx, ctx->hdrs[i], 0, 0) == -1) {
682         mx_close_mailbox (ctx_trash, NULL);
683         return -1;
684       }
685
686     mx_close_mailbox (ctx_trash, NULL);
687   }
688   else {
689     mutt_error _("Can't open trash folder");
690
691     return -1;
692   }
693
694   return 0;
695 }
696
697 /* save changes and close mailbox */
698 static int _mx_close_mailbox (CONTEXT * ctx, int *index_hint)
699 {
700   int i, move_messages = 0, purge = 1, read_msgs = 0;
701   int check;
702   int isSpool = 0;
703   CONTEXT f;
704   char mbox[_POSIX_PATH_MAX];
705   char buf[SHORT_STRING];
706
707   if (!ctx)
708     return 0;
709
710   ctx->closing = 1;
711
712 #ifdef USE_NNTP
713   if (ctx->magic == M_NNTP) {
714     int ret;
715
716     ret = nntp_close_mailbox (ctx);
717     mx_fastclose_mailbox (ctx);
718     return ret;
719   }
720 #endif
721   if (ctx->readonly || ctx->dontwrite) {
722     /* mailbox is readonly or we don't want to write */
723     mx_fastclose_mailbox (ctx);
724     return 0;
725   }
726
727   if (ctx->append) {
728     /* mailbox was opened in write-mode */
729     if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
730       mbox_close_mailbox (ctx);
731     else
732       mx_fastclose_mailbox (ctx);
733     return 0;
734   }
735
736   for (i = 0; i < ctx->msgcount; i++) {
737     if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read
738         && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED)))
739       read_msgs++;
740   }
741
742   if (read_msgs && quadoption (OPT_MOVE) != M_NO) {
743     char *p;
744
745     if ((p = mutt_find_hook (M_MBOXHOOK, ctx->path))) {
746       isSpool = 1;
747       strfcpy (mbox, p, sizeof (mbox));
748     }
749     else {
750       strfcpy (mbox, NONULL (Inbox), sizeof (mbox));
751       isSpool = mutt_is_spool (ctx->path) && !mutt_is_spool (mbox);
752     }
753     mutt_expand_path (mbox, sizeof (mbox));
754
755     if (isSpool) {
756       snprintf (buf, sizeof (buf), _("Move read messages to %s?"), mbox);
757       if ((move_messages = query_quadoption (OPT_MOVE, buf)) == -1) {
758         ctx->closing = 0;
759         return (-1);
760       }
761     }
762   }
763
764   /* 
765    * There is no point in asking whether or not to purge if we are
766    * just marking messages as "trash".
767    */
768   if (ctx->deleted && !(ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH))) {
769     snprintf (buf, sizeof (buf), ctx->deleted == 1
770               ? _("Purge %d deleted message?") :
771               _("Purge %d deleted messages?"), ctx->deleted);
772     if ((purge = query_quadoption (OPT_DELETE, buf)) < 0) {
773       ctx->closing = 0;
774       return (-1);
775     }
776   }
777
778 #ifdef USE_IMAP
779   /* IMAP servers manage the OLD flag themselves */
780   if (ctx->magic != M_IMAP)
781 #endif
782     if (option (OPTMARKOLD)) {
783       for (i = 0; i < ctx->msgcount; i++) {
784         if (!ctx->hdrs[i]->deleted && !ctx->hdrs[i]->old)
785           mutt_set_flag (ctx, ctx->hdrs[i], M_OLD, 1);
786       }
787     }
788
789   if (move_messages) {
790     mutt_message (_("Moving read messages to %s..."), mbox);
791
792 #ifdef USE_IMAP
793     /* try to use server-side copy first */
794     i = 1;
795
796     if (ctx->magic == M_IMAP && imap_is_magic (mbox, NULL) == M_IMAP) {
797       /* tag messages for moving, and clear old tags, if any */
798       for (i = 0; i < ctx->msgcount; i++)
799         if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted
800             && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED)))
801           ctx->hdrs[i]->tagged = 1;
802         else
803           ctx->hdrs[i]->tagged = 0;
804
805       i = imap_copy_messages (ctx, NULL, mbox, 1);
806     }
807
808     if (i == 0)                 /* success */
809       mutt_clear_error ();
810     else if (i == -1) {         /* horrible error, bail */
811       ctx->closing = 0;
812       return -1;
813     }
814     else                        /* use regular append-copy mode */
815 #endif
816     {
817       if (mx_open_mailbox (mbox, M_APPEND, &f) == NULL) {
818         ctx->closing = 0;
819         return -1;
820       }
821
822       for (i = 0; i < ctx->msgcount; i++) {
823         if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted
824             && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED))) {
825           if (mutt_append_message (&f, ctx, ctx->hdrs[i], 0, CH_UPDATE_LEN) ==
826               0) {
827             mutt_set_flag (ctx, ctx->hdrs[i], M_DELETE, 1);
828             mutt_set_flag (ctx, ctx->hdrs[i], M_APPENDED, 1);
829           }
830           else {
831             mx_close_mailbox (&f, NULL);
832             ctx->closing = 0;
833             return -1;
834           }
835         }
836       }
837
838       mx_close_mailbox (&f, NULL);
839     }
840
841   }
842   else if (!ctx->changed && ctx->deleted == 0) {
843     mutt_message _("Mailbox is unchanged.");
844
845     mx_fastclose_mailbox (ctx);
846     return 0;
847   }
848
849   /* copy mails to the trash before expunging */
850   if (purge && ctx->deleted)
851     if (trash_append (ctx) != 0) {
852       ctx->closing = 0;
853       return -1;
854     }
855
856 #ifdef USE_IMAP
857   /* allow IMAP to preserve the deleted flag across sessions */
858   if (ctx->magic == M_IMAP) {
859     if ((check = imap_sync_mailbox (ctx, purge, index_hint)) != 0) {
860       ctx->closing = 0;
861       return check;
862     }
863   }
864   else
865 #endif
866   {
867     if (!purge) {
868       for (i = 0; i < ctx->msgcount; i++)
869         ctx->hdrs[i]->deleted = 0;
870       ctx->deleted = 0;
871     }
872
873     if (ctx->changed || ctx->deleted) {
874       if ((check = sync_mailbox (ctx, index_hint)) != 0) {
875         ctx->closing = 0;
876         return check;
877       }
878     }
879   }
880
881   if (move_messages)
882     mutt_message (_("%d kept, %d moved, %d deleted."),
883                   ctx->msgcount - ctx->deleted, read_msgs, ctx->deleted);
884   else
885     mutt_message (_("%d kept, %d deleted."),
886                   ctx->msgcount - ctx->deleted, ctx->deleted);
887
888   if (ctx->msgcount == ctx->deleted &&
889       (ctx->magic == M_MMDF || ctx->magic == M_MBOX) &&
890       !mutt_is_spool (ctx->path) && !option (OPTSAVEEMPTY))
891     mx_unlink_empty (ctx->path);
892
893 #ifdef USE_COMPRESSED
894   if (ctx->compressinfo && mutt_slow_close_compressed (ctx))
895     return (-1);
896 #endif
897
898   mx_fastclose_mailbox (ctx);
899
900   return 0;
901 }
902
903 int mx_close_mailbox (CONTEXT * ctx, int *index_hint) {
904   int ret = 0;
905   if (!ctx)
906     return (0);
907   ret = _mx_close_mailbox (ctx, index_hint);
908   sidebar_set_buffystats (ctx);
909   return (ret);
910 }
911
912 /* update a Context structure's internal tables. */
913
914 void mx_update_tables (CONTEXT * ctx, int committing)
915 {
916   int i, j;
917
918   /* update memory to reflect the new state of the mailbox */
919   ctx->vcount = 0;
920   ctx->vsize = 0;
921   ctx->tagged = 0;
922   ctx->deleted = 0;
923   ctx->new = 0;
924   ctx->unread = 0;
925   ctx->changed = 0;
926   ctx->flagged = 0;
927 #define this_body ctx->hdrs[j]->content
928   for (i = 0, j = 0; i < ctx->msgcount; i++) {
929     if ((committing && (!ctx->hdrs[i]->deleted ||
930                         (ctx->magic == M_MAILDIR
931                          && option (OPTMAILDIRTRASH)))) || (!committing
932                                                             && ctx->hdrs[i]->
933                                                             active)) {
934       if (i != j) {
935         ctx->hdrs[j] = ctx->hdrs[i];
936         ctx->hdrs[i] = NULL;
937       }
938       ctx->hdrs[j]->msgno = j;
939       if (ctx->hdrs[j]->virtual != -1) {
940         ctx->v2r[ctx->vcount] = j;
941         ctx->hdrs[j]->virtual = ctx->vcount++;
942         ctx->vsize += this_body->length + this_body->offset -
943           this_body->hdr_offset;
944       }
945
946       if (committing)
947         ctx->hdrs[j]->changed = 0;
948       else if (ctx->hdrs[j]->changed)
949         ctx->changed++;
950
951       if (!committing
952           || (ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH))) {
953         if (ctx->hdrs[j]->deleted)
954           ctx->deleted++;
955       }
956
957       if (ctx->hdrs[j]->tagged)
958         ctx->tagged++;
959       if (ctx->hdrs[j]->flagged)
960         ctx->flagged++;
961       if (!ctx->hdrs[j]->read) {
962         ctx->unread++;
963         if (!ctx->hdrs[j]->old)
964           ctx->new++;
965       }
966
967       j++;
968     }
969     else {
970       if (ctx->magic == M_MH || ctx->magic == M_MAILDIR)
971         ctx->size -= (ctx->hdrs[i]->content->length +
972                       ctx->hdrs[i]->content->offset -
973                       ctx->hdrs[i]->content->hdr_offset);
974       /* remove message from the hash tables */
975       if (ctx->subj_hash && ctx->hdrs[i]->env->real_subj)
976         hash_delete (ctx->subj_hash, ctx->hdrs[i]->env->real_subj,
977                      ctx->hdrs[i], NULL);
978       if (ctx->id_hash && ctx->hdrs[i]->env->message_id)
979         hash_delete (ctx->id_hash, ctx->hdrs[i]->env->message_id,
980                      ctx->hdrs[i], NULL);
981       mutt_free_header (&ctx->hdrs[i]);
982     }
983   }
984 #undef this_body
985   ctx->msgcount = j;
986
987   /* update sidebar count */
988   sidebar_set_buffystats (ctx);
989 }
990
991
992 /* save changes to mailbox
993  *
994  * return values:
995  *      0               success
996  *      -1              error
997  */
998 static int _mx_sync_mailbox (CONTEXT * ctx, int *index_hint)
999 {
1000   int rc, i;
1001   int purge = 1;
1002   int msgcount, deleted;
1003
1004   if (ctx->dontwrite) {
1005     char buf[STRING], tmp[STRING];
1006
1007     if (km_expand_key (buf, sizeof (buf),
1008                        km_find_func (MENU_MAIN, OP_TOGGLE_WRITE)))
1009       snprintf (tmp, sizeof (tmp), _(" Press '%s' to toggle write"), buf);
1010     else
1011       strfcpy (tmp, _("Use 'toggle-write' to re-enable write!"),
1012                sizeof (tmp));
1013
1014     mutt_error (_("Mailbox is marked unwritable. %s"), tmp);
1015     return -1;
1016   }
1017   else if (ctx->readonly) {
1018     mutt_error _("Mailbox is read-only.");
1019
1020     return -1;
1021   }
1022
1023   if (!ctx->changed && !ctx->deleted) {
1024     mutt_message _("Mailbox is unchanged.");
1025
1026     return (0);
1027   }
1028
1029   if (ctx->deleted) {
1030     char buf[SHORT_STRING];
1031
1032     snprintf (buf, sizeof (buf), ctx->deleted == 1
1033               ? _("Purge %d deleted message?") :
1034               _("Purge %d deleted messages?"), ctx->deleted);
1035     if ((purge = query_quadoption (OPT_DELETE, buf)) < 0)
1036       return (-1);
1037     else if (purge == M_NO) {
1038       if (!ctx->changed)
1039         return 0;               /* nothing to do! */
1040 #ifdef USE_IMAP
1041       /* let IMAP servers hold on to D flags */
1042       if (ctx->magic != M_IMAP)
1043 #endif
1044       {
1045         for (i = 0; i < ctx->msgcount; i++)
1046           ctx->hdrs[i]->deleted = 0;
1047         ctx->deleted = 0;
1048       }
1049     }
1050     else if (ctx->last_tag && ctx->last_tag->deleted)
1051       ctx->last_tag = NULL;     /* reset last tagged msg now useless */
1052   }
1053
1054   /* really only for IMAP - imap_sync_mailbox results in a call to
1055    * mx_update_tables, so ctx->deleted is 0 when it comes back */
1056   msgcount = ctx->msgcount;
1057   deleted = ctx->deleted;
1058
1059   if (purge && ctx->deleted) {
1060     if (trash_append (ctx) == -1)
1061       return -1;
1062   }
1063
1064 #ifdef USE_IMAP
1065   if (ctx->magic == M_IMAP)
1066     rc = imap_sync_mailbox (ctx, purge, index_hint);
1067   else
1068 #endif
1069     rc = sync_mailbox (ctx, index_hint);
1070   if (rc == 0) {
1071 #ifdef USE_IMAP
1072     if (ctx->magic == M_IMAP && !purge)
1073       mutt_message (_("Mailbox checkpointed."));
1074
1075     else
1076 #endif
1077       mutt_message (_("%d kept, %d deleted."), msgcount - deleted, deleted);
1078
1079     mutt_sleep (0);
1080
1081     if (ctx->msgcount == ctx->deleted &&
1082         (ctx->magic == M_MBOX || ctx->magic == M_MMDF) &&
1083         !mutt_is_spool (ctx->path) && !option (OPTSAVEEMPTY)) {
1084       unlink (ctx->path);
1085       mx_fastclose_mailbox (ctx);
1086       return 0;
1087     }
1088
1089     /* if we haven't deleted any messages, we don't need to resort */
1090     /* ... except for certain folder formats which need "unsorted" 
1091      * sort order in order to synchronize folders.
1092      * 
1093      * MH and maildir are safe.  mbox-style seems to need re-sorting,
1094      * at least with the new threading code.
1095      */
1096     if (purge || (ctx->magic != M_MAILDIR && ctx->magic != M_MH)) {
1097 #ifdef USE_IMAP
1098       /* IMAP does this automatically after handling EXPUNGE */
1099       if (ctx->magic != M_IMAP)
1100 #endif
1101       {
1102         mx_update_tables (ctx, 1);
1103         mutt_sort_headers (ctx, 1);     /* rethread from scratch */
1104       }
1105     }
1106   }
1107
1108   return (rc);
1109 }
1110
1111 int mx_sync_mailbox (CONTEXT* ctx, int* index_hint) {
1112   int ret = _mx_sync_mailbox (ctx, index_hint);
1113   sidebar_set_buffystats (ctx);
1114   return (ret);
1115 }
1116
1117 /* args:
1118  *      dest    destintation mailbox
1119  *      hdr     message being copied (required for maildir support, because
1120  *              the filename depends on the message flags)
1121  */
1122 MESSAGE *mx_open_new_message (CONTEXT * dest, HEADER * hdr, int flags)
1123 {
1124   MESSAGE *msg;
1125   ADDRESS *p = NULL;
1126
1127   if (!MX_IDX(dest->magic-1)) {
1128     debug_print (1, ("function unimplemented for mailbox type %d.\n", dest->magic));
1129     return (NULL);
1130   }
1131
1132   msg = p_new(MESSAGE, 1);
1133   msg->magic = dest->magic;
1134   msg->write = 1;
1135
1136   if (hdr) {
1137     msg->flags.flagged = hdr->flagged;
1138     msg->flags.replied = hdr->replied;
1139     msg->flags.read = hdr->read;
1140     msg->received = hdr->received;
1141   }
1142
1143   if (msg->received == 0)
1144     time (&msg->received);
1145
1146   if (MX_COMMAND(dest->magic-1,mx_open_new_message)(msg, dest, hdr) == 0) {
1147     if (dest->magic == M_MMDF)
1148       fputs (MMDF_SEP, msg->fp);
1149
1150     if ((msg->magic == M_MBOX || msg->magic == M_MMDF) && flags & M_ADD_FROM) {
1151       if (hdr) {
1152         if (hdr->env->return_path)
1153           p = hdr->env->return_path;
1154         else if (hdr->env->sender)
1155           p = hdr->env->sender;
1156         else
1157           p = hdr->env->from;
1158       }
1159
1160       fprintf (msg->fp, "From %s %s", p ? p->mailbox : NONULL (Username),
1161                ctime (&msg->received));
1162     }
1163   }
1164   else
1165     p_delete(&msg);
1166
1167   return msg;
1168 }
1169
1170 /* check for new mail */
1171 int mx_check_mailbox (CONTEXT * ctx, int *index_hint, int lock) {
1172 #ifdef USE_COMPRESSED
1173   if (ctx->compressinfo)
1174     return mutt_check_mailbox_compressed (ctx);
1175 #endif
1176
1177   if (ctx) {
1178     if (ctx->locked)
1179       lock = 0;
1180     if (MX_IDX(ctx->magic-1) && MX_COMMAND(ctx->magic-1,mx_check_mailbox))
1181       return (MX_COMMAND(ctx->magic-1,mx_check_mailbox)(ctx, index_hint, lock));
1182   }
1183
1184   debug_print (1, ("null or invalid context.\n"));
1185   return (-1);
1186
1187 }
1188
1189 /* return a stream pointer for a message */
1190 MESSAGE *mx_open_message (CONTEXT * ctx, int msgno)
1191 {
1192   MESSAGE *msg;
1193
1194   msg = p_new(MESSAGE, 1);
1195   switch (msg->magic = ctx->magic) {
1196   case M_MBOX:
1197   case M_MMDF:
1198     msg->fp = ctx->fp;
1199     break;
1200
1201   case M_MH:
1202   case M_MAILDIR:
1203     {
1204       HEADER *cur = ctx->hdrs[msgno];
1205       char path[_POSIX_PATH_MAX];
1206
1207       snprintf (path, sizeof (path), "%s/%s", ctx->path, cur->path);
1208
1209       if ((msg->fp = fopen (path, "r")) == NULL && errno == ENOENT &&
1210           ctx->magic == M_MAILDIR)
1211         msg->fp = maildir_open_find_message (ctx->path, cur->path);
1212
1213       if (msg->fp == NULL) {
1214         mutt_perror (path);
1215         debug_print (1, ("fopen: %s: %s (errno %d).\n", path, strerror (errno), errno));
1216         p_delete(&msg);
1217       }
1218     }
1219     break;
1220
1221 #ifdef USE_IMAP
1222   case M_IMAP:
1223     {
1224       if (imap_fetch_message (msg, ctx, msgno) != 0)
1225         p_delete(&msg);
1226       break;
1227     }
1228 #endif /* USE_IMAP */
1229
1230 #ifdef USE_POP
1231   case M_POP:
1232     {
1233       if (pop_fetch_message (msg, ctx, msgno) != 0)
1234         p_delete(&msg);
1235       break;
1236     }
1237 #endif /* USE_POP */
1238
1239 #ifdef USE_NNTP
1240   case M_NNTP:
1241     {
1242       if (nntp_fetch_message (msg, ctx, msgno) != 0)
1243         p_delete(&msg);
1244       break;
1245     }
1246 #endif /* USE_NNTP */
1247
1248   default:
1249     debug_print (1, ("function not implemented for mailbox type %d.\n", ctx->magic));
1250     p_delete(&msg);
1251     break;
1252   }
1253   return (msg);
1254 }
1255
1256 /* commit a message to a folder */
1257
1258 int mx_commit_message (MESSAGE * msg, CONTEXT * ctx) {
1259   if (!(msg->write && ctx->append)) {
1260     debug_print (1, ("msg->write = %d, ctx->append = %d\n", msg->write, ctx->append));
1261     return -1;
1262   }
1263   if (!ctx || !MX_IDX(ctx->magic-1) || !MX_COMMAND(ctx->magic-1,mx_commit_message))
1264     return (-1);
1265   return (MX_COMMAND(ctx->magic-1,mx_commit_message) (msg, ctx));
1266 }
1267
1268 /* close a pointer to a message */
1269 int mx_close_message (MESSAGE ** msg)
1270 {
1271   int r = 0;
1272
1273   if ((*msg)->magic == M_MH || (*msg)->magic == M_MAILDIR
1274 #ifdef USE_IMAP
1275       || (*msg)->magic == M_IMAP
1276 #endif
1277 #ifdef USE_POP
1278       || (*msg)->magic == M_POP
1279 #endif
1280 #ifdef USE_NNTP
1281       || (*msg)->magic == M_NNTP
1282 #endif
1283     ) {
1284     r = safe_fclose (&(*msg)->fp);
1285   }
1286   else
1287     (*msg)->fp = NULL;
1288
1289   if ((*msg)->path) {
1290     debug_print (1, ("unlinking %s\n", (*msg)->path));
1291     unlink ((*msg)->path);
1292     p_delete(&(*msg)->path);
1293   }
1294
1295   p_delete(msg);
1296   return (r);
1297 }
1298
1299 void mx_alloc_memory (CONTEXT * ctx)
1300 {
1301   int i;
1302   size_t s = MAX (sizeof (HEADER *), sizeof (int));
1303
1304   if ((ctx->hdrmax + 25) * s < ctx->hdrmax * s) {
1305     mutt_error _("Integer overflow -- can't allocate memory.");
1306
1307     sleep (1);
1308     mutt_exit (1);
1309   }
1310
1311   if (ctx->hdrs) {
1312     p_realloc(&ctx->hdrs, ctx->hdrmax += 25);
1313     p_realloc(&ctx->v2r, ctx->hdrmax);
1314   }
1315   else {
1316     ctx->hdrs = p_new(HEADER *, (ctx->hdrmax += 25));
1317     ctx->v2r = p_new(int, ctx->hdrmax);
1318   }
1319   for (i = ctx->msgcount; i < ctx->hdrmax; i++) {
1320     ctx->hdrs[i] = NULL;
1321     ctx->v2r[i] = -1;
1322   }
1323 }
1324
1325 /* this routine is called to update the counts in the context structure for
1326  * the last message header parsed.
1327  */
1328 void mx_update_context (CONTEXT * ctx, int new_messages)
1329 {
1330   HEADER *h;
1331   int msgno;
1332
1333   for (msgno = ctx->msgcount - new_messages; msgno < ctx->msgcount; msgno++) {
1334     h = ctx->hdrs[msgno];
1335
1336     if (WithCrypto) {
1337       /* NOTE: this _must_ be done before the check for mailcap! */
1338       h->security = crypt_query (h->content);
1339     }
1340
1341     if (!ctx->pattern) {
1342       ctx->v2r[ctx->vcount] = msgno;
1343       h->virtual = ctx->vcount++;
1344     }
1345     else
1346       h->virtual = -1;
1347     h->msgno = msgno;
1348
1349     if (h->env->supersedes) {
1350       HEADER *h2;
1351
1352       if (!ctx->id_hash)
1353         ctx->id_hash = mutt_make_id_hash (ctx);
1354
1355       h2 = hash_find (ctx->id_hash, h->env->supersedes);
1356
1357       /* p_delete(&h->env->supersedes); should I ? */
1358       if (h2) {
1359         h2->superseded = 1;
1360         if (!ctx->counting && option (OPTSCORE))
1361           mutt_score_message (ctx, h2, 1);
1362       }
1363     }
1364
1365     /* add this message to the hash tables */
1366     if (ctx->id_hash && h->env->message_id)
1367       hash_insert (ctx->id_hash, h->env->message_id, h, 0);
1368     if (!ctx->counting) {
1369       if (ctx->subj_hash && h->env->real_subj)
1370         hash_insert (ctx->subj_hash, h->env->real_subj, h, 1);
1371
1372       if (option (OPTSCORE))
1373         mutt_score_message (ctx, h, 0);
1374     }
1375
1376     if (h->changed)
1377       ctx->changed = 1;
1378     if (h->flagged)
1379       ctx->flagged++;
1380     if (h->deleted)
1381       ctx->deleted++;
1382     if (!h->read) {
1383       ctx->unread++;
1384       if (!h->old)
1385         ctx->new++;
1386     }
1387   }
1388   /* update sidebar count */
1389   sidebar_set_buffystats (ctx);
1390 }
1391
1392 /*
1393  * Return:
1394  * 1 if the specified mailbox contains 0 messages.
1395  * 0 if the mailbox contains messages
1396  * -1 on error
1397  */
1398 int mx_check_empty (const char *path)
1399 {
1400   int i = 0;
1401   if ((i = mx_get_idx (path)) >= 0 && MX_COMMAND(i,mx_check_empty))
1402     return (MX_COMMAND(i,mx_check_empty)(path));
1403   errno = EINVAL;
1404   return (-1);
1405 }
1406
1407 int mx_acl_check (CONTEXT* ctx, int flag) {
1408   if (!ctx || !MX_IDX(ctx->magic-1))
1409     return (0);
1410   /* if no acl_check defined for module, assume permission is granted */
1411   if (!MX_COMMAND(ctx->magic-1,mx_acl_check))
1412     return (1);
1413   return (MX_COMMAND(ctx->magic-1,mx_acl_check)(ctx,flag));
1414 }
1415
1416 void mx_init (void) {
1417 #ifdef DEBUG
1418   int i = 0;
1419 #endif
1420   list_push_back (&MailboxFormats, (void*) mbox_reg_mx ());
1421   list_push_back (&MailboxFormats, (void*) mmdf_reg_mx ());
1422   list_push_back (&MailboxFormats, (void*) mh_reg_mx ());
1423   list_push_back (&MailboxFormats, (void*) maildir_reg_mx ());
1424 #ifdef USE_IMAP
1425   list_push_back (&MailboxFormats, (void*) imap_reg_mx ());
1426 #endif
1427 #ifdef USE_POP
1428   list_push_back (&MailboxFormats, (void*) pop_reg_mx ());
1429 #endif
1430 #ifdef USE_NNTP
1431   list_push_back (&MailboxFormats, (void*) nntp_reg_mx ());
1432 #endif
1433 #ifdef USE_COMPRESSED
1434   list_push_back (&MailboxFormats, (void*) compress_reg_mx ());
1435 #endif
1436 #ifdef DEBUG
1437   /* check module registration for completeness with debug versions */
1438 #define EXITWITHERR(m) do { fprintf(stderr, "error: incomplete mx module: %s is missing for type %i\n",m,i);exit(1); } while (0)
1439   for (i = 0; i < MailboxFormats->length; i++) {
1440     if (MX_COMMAND(i,type) < 1)         EXITWITHERR("type");
1441     if (!MX_COMMAND(i,mx_is_magic))     EXITWITHERR("mx_is_magic");
1442     if (!MX_COMMAND(i,mx_open_mailbox)) EXITWITHERR("mx_open_mailbox");
1443 /*    if (!MX_COMMAND(i,mx_sync_mailbox)) EXITWITHERR("mx_sync_mailbox");*/
1444   }
1445 #undef EXITWITHERR
1446 #endif /* DEBUG */
1447 }
1448
1449 int mx_rebuild_cache (void) {
1450 #ifndef USE_HCACHE
1451   mutt_error (_("Support for header caching was not build in."));
1452   return (1);
1453 #else
1454   int i = 0, magic = 0;
1455   CONTEXT* ctx = NULL;
1456   BUFFY* b = NULL;
1457
1458   if (list_empty(Incoming)) {
1459     mutt_error (_("No mailboxes defined."));
1460     return (1);
1461   }
1462
1463   for (i = 0; i < Incoming->length; i++) {
1464     b = (BUFFY*) Incoming->data[i];
1465     magic = mx_get_magic (b->path);
1466     if (magic != M_MAILDIR && magic != M_MH
1467 #ifdef USE_IMAP
1468         && magic != M_IMAP
1469 #endif
1470     )
1471       continue;
1472     sidebar_set_current (b->path);
1473     sidebar_draw (CurrentMenu);
1474     if ((ctx = mx_open_mailbox (b->path,
1475                                 M_READONLY | M_NOSORT | M_COUNT,
1476                                 NULL)) != NULL)
1477       mx_close_mailbox (ctx, 0);
1478   }
1479   mutt_clear_error ();
1480
1481   if (Context && Context->path)
1482     sidebar_set_current (Context->path);
1483   sidebar_draw (CurrentMenu);
1484
1485   return (0);
1486 #endif
1487 }