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