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