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