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