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