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