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