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   if (ctx->magic == M_COMPRESSED)
529     mutt_open_read_compressed (ctx);
530
531   if (ctx->magic == 0)
532     mutt_error (_("%s is not a mailbox."), path);
533
534   if (ctx->magic == -1)
535     mutt_perror (path);
536
537   if (ctx->magic <= 0) {
538     mx_fastclose_mailbox (ctx);
539     if (!pctx)
540       FREE (&ctx);
541     return (NULL);
542   }
543
544   /* if the user has a `push' command in their .muttrc, or in a folder-hook,
545    * it will cause the progress messages not to be displayed because
546    * mutt_refresh() will think we are in the middle of a macro.  so set a
547    * flag to indicate that we should really refresh the screen.
548    */
549   set_option (OPTFORCEREFRESH);
550
551   if (!ctx->quiet)
552     mutt_message (_("Reading %s..."), ctx->path);
553
554   if ((rc = mx_get_idx (ctx->path)) >= 0)
555     rc = MX_COMMAND(rc,mx_open_mailbox)(ctx);
556
557   if (rc == 0) {
558     if ((flags & M_NOSORT) == 0) {
559       /* avoid unnecessary work since the mailbox is completely unthreaded
560          to begin with */
561       unset_option (OPTSORTSUBTHREADS);
562       unset_option (OPTNEEDRESCORE);
563       mutt_sort_headers (ctx, 1);
564     }
565     if (!ctx->quiet)
566       mutt_clear_error ();
567   }
568   else {
569     mx_fastclose_mailbox (ctx);
570     if (!pctx)
571       FREE (&ctx);
572   }
573
574   unset_option (OPTFORCEREFRESH);
575   return (ctx);
576 }
577
578 /* free up memory associated with the mailbox context */
579 void mx_fastclose_mailbox (CONTEXT * ctx)
580 {
581   int i;
582
583   if (!ctx)
584     return;
585
586 #ifdef USE_IMAP
587   if (ctx->magic == M_IMAP)
588     imap_close_mailbox (ctx);
589 #endif /* USE_IMAP */
590 #ifdef USE_POP
591   if (ctx->magic == M_POP)
592     pop_close_mailbox (ctx);
593 #endif /* USE_POP */
594 #ifdef USE_NNTP
595   if (ctx->magic == M_NNTP)
596     nntp_fastclose_mailbox (ctx);
597 #endif /* USE_NNTP */
598   if (ctx->subj_hash)
599     hash_destroy (&ctx->subj_hash, NULL);
600   if (ctx->id_hash)
601     hash_destroy (&ctx->id_hash, NULL);
602   mutt_clear_threads (ctx);
603   for (i = 0; i < ctx->msgcount; i++)
604     mutt_free_header (&ctx->hdrs[i]);
605   FREE (&ctx->hdrs);
606   FREE (&ctx->v2r);
607 #ifdef USE_COMPRESSED
608   if (ctx->compressinfo)
609     mutt_fast_close_compressed (ctx);
610 #endif
611   FREE (&ctx->path);
612   FREE (&ctx->pattern);
613   if (ctx->limit_pattern)
614     mutt_pattern_free (&ctx->limit_pattern);
615   safe_fclose (&ctx->fp);
616   memset (ctx, 0, sizeof (CONTEXT));
617 }
618
619 /* save changes to disk */
620 static int sync_mailbox (CONTEXT * ctx, int *index_hint)
621 {
622 #ifdef BUFFY_SIZE
623   BUFFY *tmp = NULL;
624 #endif
625   int rc = -1;
626
627   if (!ctx->quiet)
628     mutt_message (_("Writing %s..."), ctx->path);
629
630   switch (ctx->magic) {
631   case M_MBOX:
632   case M_MMDF:
633     rc = mbox_sync_mailbox (ctx, index_hint);
634 #ifdef BUFFY_SIZE
635     tmp = mutt_find_mailbox (ctx->path);
636 #endif
637     break;
638
639   case M_MH:
640   case M_MAILDIR:
641     rc = mh_sync_mailbox (ctx, index_hint);
642     break;
643
644 #ifdef USE_IMAP
645   case M_IMAP:
646     /* extra argument means EXPUNGE */
647     rc = imap_sync_mailbox (ctx, 1, index_hint);
648     break;
649 #endif /* USE_IMAP */
650
651 #ifdef USE_POP
652   case M_POP:
653     rc = pop_sync_mailbox (ctx, index_hint);
654     break;
655 #endif /* USE_POP */
656
657 #ifdef USE_NNTP
658   case M_NNTP:
659     rc = nntp_sync_mailbox (ctx);
660     break;
661 #endif /* USE_NNTP */
662   }
663
664 #if 0
665   if (!ctx->quiet && !ctx->shutup && rc == -1)
666     mutt_error (_("Could not synchronize mailbox %s!"), ctx->path);
667 #endif
668
669 #ifdef BUFFY_SIZE
670   if (tmp && tmp->new == 0)
671     mutt_update_mailbox (tmp);
672 #endif
673
674 #ifdef USE_COMPRESSED
675   if (rc == 0 && ctx->compressinfo)
676     return mutt_sync_compressed (ctx);
677 #endif
678
679   return rc;
680 }
681
682 /* move deleted mails to the trash folder */
683 static int trash_append (CONTEXT * ctx)
684 {
685   CONTEXT *ctx_trash;
686   int i = 0;
687   struct stat st, stc;
688
689   if (!TrashPath || !ctx->deleted ||
690       (ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH)))
691     return 0;
692
693   for (; i < ctx->msgcount && (!ctx->hdrs[i]->deleted ||
694                                ctx->hdrs[i]->appended); i++);
695   if (i == ctx->msgcount)
696     return 0;                   /* nothing to be done */
697
698   if (mutt_save_confirm (TrashPath, &st) != 0) {
699     mutt_error _("message(s) not deleted");
700
701     return -1;
702   }
703
704   if (lstat (ctx->path, &stc) == 0 && stc.st_ino == st.st_ino
705       && stc.st_dev == st.st_dev && stc.st_rdev == st.st_rdev)
706     return 0;                   /* we are in the trash folder: simple sync */
707
708   if ((ctx_trash = mx_open_mailbox (TrashPath, M_APPEND, NULL)) != NULL) {
709     for (i = 0; i < ctx->msgcount; i++)
710       if (ctx->hdrs[i]->deleted && !ctx->hdrs[i]->appended
711           && !ctx->hdrs[i]->purged
712           && mutt_append_message (ctx_trash, ctx, ctx->hdrs[i], 0, 0) == -1) {
713         mx_close_mailbox (ctx_trash, NULL);
714         return -1;
715       }
716
717     mx_close_mailbox (ctx_trash, NULL);
718   }
719   else {
720     mutt_error _("Can't open trash folder");
721
722     return -1;
723   }
724
725   return 0;
726 }
727
728 /* save changes and close mailbox */
729 int mx_close_mailbox (CONTEXT * ctx, int *index_hint)
730 {
731   int i, move_messages = 0, purge = 1, read_msgs = 0;
732   int check;
733   int isSpool = 0;
734   CONTEXT f;
735   char mbox[_POSIX_PATH_MAX];
736   char buf[SHORT_STRING];
737
738   if (!ctx)
739     return 0;
740
741   ctx->closing = 1;
742
743 #ifdef USE_NNTP
744   if (ctx->magic == M_NNTP) {
745     int ret;
746
747     ret = nntp_close_mailbox (ctx);
748     mx_fastclose_mailbox (ctx);
749     return ret;
750   }
751 #endif
752   if (ctx->readonly || ctx->dontwrite) {
753     /* mailbox is readonly or we don't want to write */
754     mx_fastclose_mailbox (ctx);
755     return 0;
756   }
757
758   if (ctx->append) {
759     /* mailbox was opened in write-mode */
760     if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
761       mbox_close_mailbox (ctx);
762     else
763       mx_fastclose_mailbox (ctx);
764     return 0;
765   }
766
767   for (i = 0; i < ctx->msgcount; i++) {
768     if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read
769         && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED)))
770       read_msgs++;
771   }
772
773   if (read_msgs && quadoption (OPT_MOVE) != M_NO) {
774     char *p;
775
776     if ((p = mutt_find_hook (M_MBOXHOOK, ctx->path))) {
777       isSpool = 1;
778       strfcpy (mbox, p, sizeof (mbox));
779     }
780     else {
781       strfcpy (mbox, NONULL (Inbox), sizeof (mbox));
782       isSpool = mutt_is_spool (ctx->path) && !mutt_is_spool (mbox);
783     }
784     mutt_expand_path (mbox, sizeof (mbox));
785
786     if (isSpool) {
787       snprintf (buf, sizeof (buf), _("Move read messages to %s?"), mbox);
788       if ((move_messages = query_quadoption (OPT_MOVE, buf)) == -1) {
789         ctx->closing = 0;
790         return (-1);
791       }
792     }
793   }
794
795   /* 
796    * There is no point in asking whether or not to purge if we are
797    * just marking messages as "trash".
798    */
799   if (ctx->deleted && !(ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH))) {
800     snprintf (buf, sizeof (buf), ctx->deleted == 1
801               ? _("Purge %d deleted message?") :
802               _("Purge %d deleted messages?"), ctx->deleted);
803     if ((purge = query_quadoption (OPT_DELETE, buf)) < 0) {
804       ctx->closing = 0;
805       return (-1);
806     }
807   }
808
809 #ifdef USE_IMAP
810   /* IMAP servers manage the OLD flag themselves */
811   if (ctx->magic != M_IMAP)
812 #endif
813     if (option (OPTMARKOLD)) {
814       for (i = 0; i < ctx->msgcount; i++) {
815         if (!ctx->hdrs[i]->deleted && !ctx->hdrs[i]->old)
816           mutt_set_flag (ctx, ctx->hdrs[i], M_OLD, 1);
817       }
818     }
819
820   if (move_messages) {
821     mutt_message (_("Moving read messages to %s..."), mbox);
822
823 #ifdef USE_IMAP
824     /* try to use server-side copy first */
825     i = 1;
826
827     if (ctx->magic == M_IMAP && mx_get_magic (mbox) == M_IMAP) {
828       /* tag messages for moving, and clear old tags, if any */
829       for (i = 0; i < ctx->msgcount; i++)
830         if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted
831             && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED)))
832           ctx->hdrs[i]->tagged = 1;
833         else
834           ctx->hdrs[i]->tagged = 0;
835
836       i = imap_copy_messages (ctx, NULL, mbox, 1);
837     }
838
839     if (i == 0)                 /* success */
840       mutt_clear_error ();
841     else if (i == -1) {         /* horrible error, bail */
842       ctx->closing = 0;
843       return -1;
844     }
845     else                        /* use regular append-copy mode */
846 #endif
847     {
848       if (mx_open_mailbox (mbox, M_APPEND, &f) == NULL) {
849         ctx->closing = 0;
850         return -1;
851       }
852
853       for (i = 0; i < ctx->msgcount; i++) {
854         if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted
855             && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED))) {
856           if (mutt_append_message (&f, ctx, ctx->hdrs[i], 0, CH_UPDATE_LEN) ==
857               0) {
858             mutt_set_flag (ctx, ctx->hdrs[i], M_DELETE, 1);
859             mutt_set_flag (ctx, ctx->hdrs[i], M_APPENDED, 1);
860           }
861           else {
862             mx_close_mailbox (&f, NULL);
863             ctx->closing = 0;
864             return -1;
865           }
866         }
867       }
868
869       mx_close_mailbox (&f, NULL);
870     }
871
872   }
873   else if (!ctx->changed && ctx->deleted == 0) {
874     mutt_message _("Mailbox is unchanged.");
875
876     mx_fastclose_mailbox (ctx);
877     return 0;
878   }
879
880   /* copy mails to the trash before expunging */
881   if (purge && ctx->deleted)
882     if (trash_append (ctx) != 0) {
883       ctx->closing = 0;
884       return -1;
885     }
886
887 #ifdef USE_IMAP
888   /* allow IMAP to preserve the deleted flag across sessions */
889   if (ctx->magic == M_IMAP) {
890     if ((check = imap_sync_mailbox (ctx, purge, index_hint)) != 0) {
891       ctx->closing = 0;
892       return check;
893     }
894   }
895   else
896 #endif
897   {
898     if (!purge) {
899       for (i = 0; i < ctx->msgcount; i++)
900         ctx->hdrs[i]->deleted = 0;
901       ctx->deleted = 0;
902     }
903
904     if (ctx->changed || ctx->deleted) {
905       if ((check = sync_mailbox (ctx, index_hint)) != 0) {
906         ctx->closing = 0;
907         return check;
908       }
909     }
910   }
911
912   if (move_messages)
913     mutt_message (_("%d kept, %d moved, %d deleted."),
914                   ctx->msgcount - ctx->deleted, read_msgs, ctx->deleted);
915   else
916     mutt_message (_("%d kept, %d deleted."),
917                   ctx->msgcount - ctx->deleted, ctx->deleted);
918
919   if (ctx->msgcount == ctx->deleted &&
920       (ctx->magic == M_MMDF || ctx->magic == M_MBOX) &&
921       !mutt_is_spool (ctx->path) && !option (OPTSAVEEMPTY))
922     mx_unlink_empty (ctx->path);
923
924 #ifdef USE_COMPRESSED
925   if (ctx->compressinfo && mutt_slow_close_compressed (ctx))
926     return (-1);
927 #endif
928
929   mx_fastclose_mailbox (ctx);
930
931   return 0;
932 }
933
934
935 /* update a Context structure's internal tables. */
936
937 void mx_update_tables (CONTEXT * ctx, int committing)
938 {
939   int i, j;
940
941   /* update memory to reflect the new state of the mailbox */
942   ctx->vcount = 0;
943   ctx->vsize = 0;
944   ctx->tagged = 0;
945   ctx->deleted = 0;
946   ctx->new = 0;
947   ctx->unread = 0;
948   ctx->changed = 0;
949   ctx->flagged = 0;
950 #define this_body ctx->hdrs[j]->content
951   for (i = 0, j = 0; i < ctx->msgcount; i++) {
952     if ((committing && (!ctx->hdrs[i]->deleted ||
953                         (ctx->magic == M_MAILDIR
954                          && option (OPTMAILDIRTRASH)))) || (!committing
955                                                             && ctx->hdrs[i]->
956                                                             active)) {
957       if (i != j) {
958         ctx->hdrs[j] = ctx->hdrs[i];
959         ctx->hdrs[i] = NULL;
960       }
961       ctx->hdrs[j]->msgno = j;
962       if (ctx->hdrs[j]->virtual != -1) {
963         ctx->v2r[ctx->vcount] = j;
964         ctx->hdrs[j]->virtual = ctx->vcount++;
965         ctx->vsize += this_body->length + this_body->offset -
966           this_body->hdr_offset;
967       }
968
969       if (committing)
970         ctx->hdrs[j]->changed = 0;
971       else if (ctx->hdrs[j]->changed)
972         ctx->changed++;
973
974       if (!committing
975           || (ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH))) {
976         if (ctx->hdrs[j]->deleted)
977           ctx->deleted++;
978       }
979
980       if (ctx->hdrs[j]->tagged)
981         ctx->tagged++;
982       if (ctx->hdrs[j]->flagged)
983         ctx->flagged++;
984       if (!ctx->hdrs[j]->read) {
985         ctx->unread++;
986         if (!ctx->hdrs[j]->old)
987           ctx->new++;
988       }
989
990       j++;
991     }
992     else {
993       if (ctx->magic == M_MH || ctx->magic == M_MAILDIR)
994         ctx->size -= (ctx->hdrs[i]->content->length +
995                       ctx->hdrs[i]->content->offset -
996                       ctx->hdrs[i]->content->hdr_offset);
997       /* remove message from the hash tables */
998       if (ctx->subj_hash && ctx->hdrs[i]->env->real_subj)
999         hash_delete (ctx->subj_hash, ctx->hdrs[i]->env->real_subj,
1000                      ctx->hdrs[i], NULL);
1001       if (ctx->id_hash && ctx->hdrs[i]->env->message_id)
1002         hash_delete (ctx->id_hash, ctx->hdrs[i]->env->message_id,
1003                      ctx->hdrs[i], NULL);
1004       mutt_free_header (&ctx->hdrs[i]);
1005     }
1006   }
1007 #undef this_body
1008   ctx->msgcount = j;
1009 }
1010
1011
1012 /* save changes to mailbox
1013  *
1014  * return values:
1015  *      0               success
1016  *      -1              error
1017  */
1018 int mx_sync_mailbox (CONTEXT * ctx, int *index_hint)
1019 {
1020   int rc, i;
1021   int purge = 1;
1022   int msgcount, deleted;
1023
1024   if (ctx->dontwrite) {
1025     char buf[STRING], tmp[STRING];
1026
1027     if (km_expand_key (buf, sizeof (buf),
1028                        km_find_func (MENU_MAIN, OP_TOGGLE_WRITE)))
1029       snprintf (tmp, sizeof (tmp), _(" Press '%s' to toggle write"), buf);
1030     else
1031       strfcpy (tmp, _("Use 'toggle-write' to re-enable write!"),
1032                sizeof (tmp));
1033
1034     mutt_error (_("Mailbox is marked unwritable. %s"), tmp);
1035     return -1;
1036   }
1037   else if (ctx->readonly) {
1038     mutt_error _("Mailbox is read-only.");
1039
1040     return -1;
1041   }
1042
1043   if (!ctx->changed && !ctx->deleted) {
1044     mutt_message _("Mailbox is unchanged.");
1045
1046     return (0);
1047   }
1048
1049   if (ctx->deleted) {
1050     char buf[SHORT_STRING];
1051
1052     snprintf (buf, sizeof (buf), ctx->deleted == 1
1053               ? _("Purge %d deleted message?") :
1054               _("Purge %d deleted messages?"), ctx->deleted);
1055     if ((purge = query_quadoption (OPT_DELETE, buf)) < 0)
1056       return (-1);
1057     else if (purge == M_NO) {
1058       if (!ctx->changed)
1059         return 0;               /* nothing to do! */
1060 #ifdef USE_IMAP
1061       /* let IMAP servers hold on to D flags */
1062       if (ctx->magic != M_IMAP)
1063 #endif
1064       {
1065         for (i = 0; i < ctx->msgcount; i++)
1066           ctx->hdrs[i]->deleted = 0;
1067         ctx->deleted = 0;
1068       }
1069     }
1070     else if (ctx->last_tag && ctx->last_tag->deleted)
1071       ctx->last_tag = NULL;     /* reset last tagged msg now useless */
1072   }
1073
1074   /* really only for IMAP - imap_sync_mailbox results in a call to
1075    * mx_update_tables, so ctx->deleted is 0 when it comes back */
1076   msgcount = ctx->msgcount;
1077   deleted = ctx->deleted;
1078
1079   if (purge && ctx->deleted) {
1080     if (trash_append (ctx) == -1)
1081       return -1;
1082   }
1083
1084 #ifdef USE_IMAP
1085   if (ctx->magic == M_IMAP)
1086     rc = imap_sync_mailbox (ctx, purge, index_hint);
1087   else
1088 #endif
1089     rc = sync_mailbox (ctx, index_hint);
1090   if (rc == 0) {
1091 #ifdef USE_IMAP
1092     if (ctx->magic == M_IMAP && !purge)
1093       mutt_message (_("Mailbox checkpointed."));
1094
1095     else
1096 #endif
1097       mutt_message (_("%d kept, %d deleted."), msgcount - deleted, deleted);
1098
1099     mutt_sleep (0);
1100
1101     if (ctx->msgcount == ctx->deleted &&
1102         (ctx->magic == M_MBOX || ctx->magic == M_MMDF) &&
1103         !mutt_is_spool (ctx->path) && !option (OPTSAVEEMPTY)) {
1104       unlink (ctx->path);
1105       mx_fastclose_mailbox (ctx);
1106       return 0;
1107     }
1108
1109     /* if we haven't deleted any messages, we don't need to resort */
1110     /* ... except for certain folder formats which need "unsorted" 
1111      * sort order in order to synchronize folders.
1112      * 
1113      * MH and maildir are safe.  mbox-style seems to need re-sorting,
1114      * at least with the new threading code.
1115      */
1116     if (purge || (ctx->magic != M_MAILDIR && ctx->magic != M_MH)) {
1117 #ifdef USE_IMAP
1118       /* IMAP does this automatically after handling EXPUNGE */
1119       if (ctx->magic != M_IMAP)
1120 #endif
1121       {
1122         mx_update_tables (ctx, 1);
1123         mutt_sort_headers (ctx, 1);     /* rethread from scratch */
1124       }
1125     }
1126   }
1127
1128   return (rc);
1129 }
1130
1131 #ifdef USE_IMAP
1132 int imap_open_new_message (MESSAGE * msg, CONTEXT * dest, HEADER * hdr)
1133 {
1134   char tmp[_POSIX_PATH_MAX];
1135
1136   mutt_mktemp (tmp);
1137   if ((msg->fp = safe_fopen (tmp, "w")) == NULL)
1138     return (-1);
1139   msg->path = safe_strdup (tmp);
1140   return 0;
1141 }
1142 #endif
1143
1144 /* args:
1145  *      dest    destintation mailbox
1146  *      hdr     message being copied (required for maildir support, because
1147  *              the filename depends on the message flags)
1148  */
1149 MESSAGE *mx_open_new_message (CONTEXT * dest, HEADER * hdr, int flags)
1150 {
1151   MESSAGE *msg;
1152   int (*func) (MESSAGE *, CONTEXT *, HEADER *);
1153   ADDRESS *p = NULL;
1154
1155   switch (dest->magic) {
1156   case M_MMDF:
1157   case M_MBOX:
1158     func = mbox_open_new_message;
1159     break;
1160   case M_MAILDIR:
1161     func = maildir_open_new_message;
1162     break;
1163   case M_MH:
1164     func = mh_open_new_message;
1165     break;
1166 #ifdef USE_IMAP
1167   case M_IMAP:
1168     func = imap_open_new_message;
1169     break;
1170 #endif
1171   default:
1172     dprint (1,
1173             (debugfile,
1174              "mx_open_new_message(): function unimplemented for mailbox type %d.\n",
1175              dest->magic));
1176     return (NULL);
1177   }
1178
1179   msg = safe_calloc (1, sizeof (MESSAGE));
1180   msg->magic = dest->magic;
1181   msg->write = 1;
1182
1183   if (hdr) {
1184     msg->flags.flagged = hdr->flagged;
1185     msg->flags.replied = hdr->replied;
1186     msg->flags.read = hdr->read;
1187     msg->received = hdr->received;
1188   }
1189
1190   if (msg->received == 0)
1191     time (&msg->received);
1192
1193   if (func (msg, dest, hdr) == 0) {
1194     if (dest->magic == M_MMDF)
1195       fputs (MMDF_SEP, msg->fp);
1196
1197     if ((msg->magic == M_MBOX || msg->magic == M_MMDF) && flags & M_ADD_FROM) {
1198       if (hdr) {
1199         if (hdr->env->return_path)
1200           p = hdr->env->return_path;
1201         else if (hdr->env->sender)
1202           p = hdr->env->sender;
1203         else
1204           p = hdr->env->from;
1205       }
1206
1207       fprintf (msg->fp, "From %s %s", p ? p->mailbox : NONULL (Username),
1208                ctime (&msg->received));
1209     }
1210   }
1211   else
1212     FREE (&msg);
1213
1214   return msg;
1215 }
1216
1217 /* check for new mail */
1218 int mx_check_mailbox (CONTEXT * ctx, int *index_hint, int lock)
1219 {
1220   int rc;
1221
1222 #ifdef USE_COMPRESSED
1223   if (ctx->compressinfo)
1224     return mutt_check_mailbox_compressed (ctx);
1225 #endif
1226
1227   if (ctx) {
1228     if (ctx->locked)
1229       lock = 0;
1230
1231     switch (ctx->magic) {
1232     case M_MBOX:
1233     case M_MMDF:
1234
1235       if (lock) {
1236         mutt_block_signals ();
1237         if (mbox_lock_mailbox (ctx, 0, 0) == -1) {
1238           mutt_unblock_signals ();
1239           return M_LOCKED;
1240         }
1241       }
1242
1243       rc = mbox_check_mailbox (ctx, index_hint);
1244
1245       if (lock) {
1246         mutt_unblock_signals ();
1247         mbox_unlock_mailbox (ctx);
1248       }
1249
1250       return rc;
1251
1252
1253     case M_MH:
1254       return (mh_check_mailbox (ctx, index_hint));
1255     case M_MAILDIR:
1256       return (maildir_check_mailbox (ctx, index_hint));
1257
1258 #ifdef USE_IMAP
1259     case M_IMAP:
1260       return (imap_check_mailbox (ctx, index_hint, 0));
1261 #endif /* USE_IMAP */
1262
1263 #ifdef USE_POP
1264     case M_POP:
1265       return (pop_check_mailbox (ctx, index_hint));
1266 #endif /* USE_POP */
1267
1268 #ifdef USE_NNTP
1269     case M_NNTP:
1270       return (nntp_check_mailbox (ctx));
1271 #endif /* USE_NNTP */
1272     }
1273   }
1274
1275   dprint (1, (debugfile, "mx_check_mailbox: null or invalid context.\n"));
1276   return (-1);
1277 }
1278
1279 /* return a stream pointer for a message */
1280 MESSAGE *mx_open_message (CONTEXT * ctx, int msgno)
1281 {
1282   MESSAGE *msg;
1283
1284   msg = safe_calloc (1, sizeof (MESSAGE));
1285   switch (msg->magic = ctx->magic) {
1286   case M_MBOX:
1287   case M_MMDF:
1288     msg->fp = ctx->fp;
1289     break;
1290
1291   case M_MH:
1292   case M_MAILDIR:
1293     {
1294       HEADER *cur = ctx->hdrs[msgno];
1295       char path[_POSIX_PATH_MAX];
1296
1297       snprintf (path, sizeof (path), "%s/%s", ctx->path, cur->path);
1298
1299       if ((msg->fp = fopen (path, "r")) == NULL && errno == ENOENT &&
1300           ctx->magic == M_MAILDIR)
1301         msg->fp = maildir_open_find_message (ctx->path, cur->path);
1302
1303       if (msg->fp == NULL) {
1304         mutt_perror (path);
1305         dprint (1, (debugfile, "mx_open_message: fopen: %s: %s (errno %d).\n",
1306                     path, strerror (errno), errno));
1307         FREE (&msg);
1308       }
1309     }
1310     break;
1311
1312 #ifdef USE_IMAP
1313   case M_IMAP:
1314     {
1315       if (imap_fetch_message (msg, ctx, msgno) != 0)
1316         FREE (&msg);
1317       break;
1318     }
1319 #endif /* USE_IMAP */
1320
1321 #ifdef USE_POP
1322   case M_POP:
1323     {
1324       if (pop_fetch_message (msg, ctx, msgno) != 0)
1325         FREE (&msg);
1326       break;
1327     }
1328 #endif /* USE_POP */
1329
1330 #ifdef USE_NNTP
1331   case M_NNTP:
1332     {
1333       if (nntp_fetch_message (msg, ctx, msgno) != 0)
1334         FREE (&msg);
1335       break;
1336     }
1337 #endif /* USE_NNTP */
1338
1339   default:
1340     dprint (1,
1341             (debugfile,
1342              "mx_open_message(): function not implemented for mailbox type %d.\n",
1343              ctx->magic));
1344     FREE (&msg);
1345     break;
1346   }
1347   return (msg);
1348 }
1349
1350 /* commit a message to a folder */
1351
1352 int mx_commit_message (MESSAGE * msg, CONTEXT * ctx)
1353 {
1354   int r = 0;
1355
1356   if (!(msg->write && ctx->append)) {
1357     dprint (1,
1358             (debugfile,
1359              "mx_commit_message(): msg->write = %d, ctx->append = %d\n",
1360              msg->write, ctx->append));
1361     return -1;
1362   }
1363
1364   switch (msg->magic) {
1365   case M_MMDF:
1366     {
1367       if (fputs (MMDF_SEP, msg->fp) == EOF)
1368         r = -1;
1369       break;
1370     }
1371
1372   case M_MBOX:
1373     {
1374       if (fputc ('\n', msg->fp) == EOF)
1375         r = -1;
1376       break;
1377     }
1378
1379 #ifdef USE_IMAP
1380   case M_IMAP:
1381     {
1382       if ((r = safe_fclose (&msg->fp)) == 0)
1383         r = imap_append_message (ctx, msg);
1384       break;
1385     }
1386 #endif
1387
1388   case M_MAILDIR:
1389     {
1390       r = maildir_commit_message (ctx, msg, NULL);
1391       break;
1392     }
1393
1394   case M_MH:
1395     {
1396       r = mh_commit_message (ctx, msg, NULL);
1397       break;
1398     }
1399   }
1400
1401   if (r == 0 && (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
1402       && (fflush (msg->fp) == EOF || fsync (fileno (msg->fp)) == -1)) {
1403     mutt_perror (_("Can't write message"));
1404
1405     r = -1;
1406   }
1407
1408   return r;
1409 }
1410
1411 /* close a pointer to a message */
1412 int mx_close_message (MESSAGE ** msg)
1413 {
1414   int r = 0;
1415
1416   if ((*msg)->magic == M_MH || (*msg)->magic == M_MAILDIR
1417 #ifdef USE_IMAP
1418       || (*msg)->magic == M_IMAP
1419 #endif
1420 #ifdef USE_POP
1421       || (*msg)->magic == M_POP
1422 #endif
1423 #ifdef USE_NNTP
1424       || (*msg)->magic == M_NNTP
1425 #endif
1426     ) {
1427     r = safe_fclose (&(*msg)->fp);
1428   }
1429   else
1430     (*msg)->fp = NULL;
1431
1432   if ((*msg)->path) {
1433     dprint (1, (debugfile, "mx_close_message (): unlinking %s\n",
1434                 (*msg)->path));
1435     unlink ((*msg)->path);
1436     FREE (&(*msg)->path);
1437   }
1438
1439   FREE (msg);
1440   return (r);
1441 }
1442
1443 void mx_alloc_memory (CONTEXT * ctx)
1444 {
1445   int i;
1446   size_t s = MAX (sizeof (HEADER *), sizeof (int));
1447
1448   if ((ctx->hdrmax + 25) * s < ctx->hdrmax * s) {
1449     mutt_error _("Integer overflow -- can't allocate memory.");
1450
1451     sleep (1);
1452     mutt_exit (1);
1453   }
1454
1455   if (ctx->hdrs) {
1456     safe_realloc (&ctx->hdrs, sizeof (HEADER *) * (ctx->hdrmax += 25));
1457     safe_realloc (&ctx->v2r, sizeof (int) * ctx->hdrmax);
1458   }
1459   else {
1460     ctx->hdrs = safe_calloc ((ctx->hdrmax += 25), sizeof (HEADER *));
1461     ctx->v2r = safe_calloc (ctx->hdrmax, sizeof (int));
1462   }
1463   for (i = ctx->msgcount; i < ctx->hdrmax; i++) {
1464     ctx->hdrs[i] = NULL;
1465     ctx->v2r[i] = -1;
1466   }
1467 }
1468
1469 /* this routine is called to update the counts in the context structure for
1470  * the last message header parsed.
1471  */
1472 void mx_update_context (CONTEXT * ctx, int new_messages)
1473 {
1474   HEADER *h;
1475   int msgno;
1476
1477   for (msgno = ctx->msgcount - new_messages; msgno < ctx->msgcount; msgno++) {
1478     h = ctx->hdrs[msgno];
1479
1480     if (WithCrypto) {
1481       /* NOTE: this _must_ be done before the check for mailcap! */
1482       h->security = crypt_query (h->content);
1483     }
1484
1485     if (!ctx->pattern) {
1486       ctx->v2r[ctx->vcount] = msgno;
1487       h->virtual = ctx->vcount++;
1488     }
1489     else
1490       h->virtual = -1;
1491     h->msgno = msgno;
1492
1493     if (h->env->supersedes) {
1494       HEADER *h2;
1495
1496       if (!ctx->id_hash)
1497         ctx->id_hash = mutt_make_id_hash (ctx);
1498
1499       h2 = hash_find (ctx->id_hash, h->env->supersedes);
1500
1501       /* FREE (&h->env->supersedes); should I ? */
1502       if (h2) {
1503         h2->superseded = 1;
1504         if (option (OPTSCORE))
1505           mutt_score_message (ctx, h2, 1);
1506       }
1507     }
1508
1509     /* add this message to the hash tables */
1510     if (ctx->id_hash && h->env->message_id)
1511       hash_insert (ctx->id_hash, h->env->message_id, h, 0);
1512     if (ctx->subj_hash && h->env->real_subj)
1513       hash_insert (ctx->subj_hash, h->env->real_subj, h, 1);
1514
1515     if (option (OPTSCORE))
1516       mutt_score_message (ctx, h, 0);
1517
1518     if (h->changed)
1519       ctx->changed = 1;
1520     if (h->flagged)
1521       ctx->flagged++;
1522     if (h->deleted)
1523       ctx->deleted++;
1524     if (!h->read) {
1525       ctx->unread++;
1526       if (!h->old)
1527         ctx->new++;
1528     }
1529   }
1530 }
1531
1532 /*
1533  * Return:
1534  * 1 if the specified mailbox contains 0 messages.
1535  * 0 if the mailbox contains messages
1536  * -1 on error
1537  */
1538 int mx_check_empty (const char *path)
1539 {
1540   int i = 0;
1541   if ((i = mx_get_idx (path)) >= 0 && MX_COMMAND(i,mx_check_empty))
1542     return (MX_COMMAND(i,mx_check_empty)(path));
1543   errno = EINVAL;
1544   return (-1);
1545 }
1546
1547 void mx_init (void) {
1548 #ifdef DEBUG
1549   int i = 0;
1550 #endif
1551   list_push_back (&MailboxFormats, (void*) mbox_reg_mx ());
1552   list_push_back (&MailboxFormats, (void*) mmdf_reg_mx ());
1553   list_push_back (&MailboxFormats, (void*) mh_reg_mx ());
1554   list_push_back (&MailboxFormats, (void*) maildir_reg_mx ());
1555 #ifdef USE_IMAP
1556   list_push_back (&MailboxFormats, (void*) imap_reg_mx ());
1557 #endif
1558 #ifdef USE_POP
1559   list_push_back (&MailboxFormats, (void*) pop_reg_mx ());
1560 #endif
1561 #ifdef USE_NNTP
1562   list_push_back (&MailboxFormats, (void*) nntp_reg_mx ());
1563 #endif
1564 #ifdef USE_COMPRESSED
1565   list_push_back (&MailboxFormats, (void*) compress_reg_mx ());
1566 #endif
1567 #ifdef DEBUG
1568   /* check module registration for completeness with debug versions */
1569 #define EXITWITHERR(m) do { fprintf(stderr, "error: incomplete mx module: %s is missing for type %i\n",m,i);exit(1); } while (0)
1570   for (i = 0; i < MailboxFormats->length; i++) {
1571     if (MX_COMMAND(i,type) < 1)         EXITWITHERR("type");
1572     if (!MX_COMMAND(i,mx_is_magic))     EXITWITHERR("mx_is_magic");
1573     if (!MX_COMMAND(i,mx_open_mailbox)) EXITWITHERR("mx_open_mailbox");
1574   }
1575 #undef EXITWITHERR
1576 #endif /* DEBUG */
1577 }