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