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