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