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