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     else if (ctx->last_tag && ctx->last_tag->deleted)
1309       ctx->last_tag = NULL; /* reset last tagged msg now useless */
1310   }
1311
1312   /* really only for IMAP - imap_sync_mailbox results in a call to
1313    * mx_update_tables, so ctx->deleted is 0 when it comes back */
1314   msgcount = ctx->msgcount;
1315   deleted = ctx->deleted;
1316
1317   if (purge && ctx->deleted)
1318   {
1319     if (trash_append (ctx) == -1)
1320       return -1;
1321   } 
1322
1323 #ifdef USE_IMAP
1324   if (ctx->magic == M_IMAP)
1325     rc = imap_sync_mailbox (ctx, purge, index_hint);
1326   else
1327 #endif
1328     rc = sync_mailbox (ctx, index_hint);
1329   if (rc == 0)
1330   {
1331 #ifdef USE_IMAP
1332     if (ctx->magic == M_IMAP && !purge)
1333       mutt_message _("Mailbox checkpointed.");
1334     else
1335 #endif
1336     mutt_message (_("%d kept, %d deleted."), msgcount - deleted,
1337       deleted);
1338
1339     mutt_sleep (0);
1340     
1341     if (ctx->msgcount == ctx->deleted &&
1342         (ctx->magic == M_MBOX || ctx->magic == M_MMDF) &&
1343         !mutt_is_spool (ctx->path) && !option (OPTSAVEEMPTY))
1344     {
1345       unlink (ctx->path);
1346       mx_fastclose_mailbox (ctx);
1347       return 0;
1348     }
1349
1350     /* if we haven't deleted any messages, we don't need to resort */
1351     /* ... except for certain folder formats which need "unsorted" 
1352      * sort order in order to synchronize folders.
1353      * 
1354      * MH and maildir are safe.  mbox-style seems to need re-sorting,
1355      * at least with the new threading code.
1356      */
1357     if (purge || (ctx->magic != M_MAILDIR && ctx->magic != M_MH))
1358     {
1359 #ifdef USE_IMAP
1360       /* IMAP does this automatically after handling EXPUNGE */
1361       if (ctx->magic != M_IMAP)
1362 #endif
1363       {
1364         mx_update_tables (ctx, 1);
1365         mutt_sort_headers (ctx, 1); /* rethread from scratch */
1366       }
1367     }
1368   }
1369
1370   return (rc);
1371 }
1372
1373
1374 /* {maildir,mh}_open_new_message are in mh.c. */
1375
1376 int mbox_open_new_message (MESSAGE *msg, CONTEXT *dest, HEADER *hdr)
1377 {
1378   msg->fp = dest->fp;
1379   return 0;
1380 }
1381
1382 #ifdef USE_IMAP
1383 int imap_open_new_message (MESSAGE *msg, CONTEXT *dest, HEADER *hdr)
1384 {
1385   char tmp[_POSIX_PATH_MAX];
1386
1387   mutt_mktemp(tmp);
1388   if ((msg->fp = safe_fopen (tmp, "w")) == NULL)
1389     return (-1);
1390   msg->path = safe_strdup(tmp);
1391   return 0;
1392 }
1393 #endif
1394
1395 /* args:
1396  *      dest    destintation mailbox
1397  *      hdr     message being copied (required for maildir support, because
1398  *              the filename depends on the message flags)
1399  */
1400 MESSAGE *mx_open_new_message (CONTEXT *dest, HEADER *hdr, int flags)
1401 {
1402   MESSAGE *msg;
1403   int (*func) (MESSAGE *, CONTEXT *, HEADER *);
1404   ADDRESS *p = NULL;
1405
1406   switch (dest->magic)
1407   {
1408     case M_MMDF:
1409     case M_MBOX:
1410       func = mbox_open_new_message;
1411       break;
1412     case M_MAILDIR:
1413       func = maildir_open_new_message;
1414       break;
1415     case M_MH:
1416       func = mh_open_new_message;
1417       break;
1418 #ifdef USE_IMAP
1419     case M_IMAP:
1420       func = imap_open_new_message;
1421       break;
1422 #endif
1423     default:
1424       dprint (1, (debugfile, "mx_open_new_message(): function unimplemented for mailbox type %d.\n",
1425                   dest->magic));
1426       return (NULL);
1427   }
1428
1429   msg = safe_calloc (1, sizeof (MESSAGE));
1430   msg->magic = dest->magic;
1431   msg->write = 1;
1432
1433   if (hdr)
1434   {
1435     msg->flags.flagged = hdr->flagged;
1436     msg->flags.replied = hdr->replied;
1437     msg->flags.read    = hdr->read;
1438     msg->received = hdr->received;
1439   }
1440
1441   if(msg->received == 0)
1442     time(&msg->received);
1443   
1444   if (func (msg, dest, hdr) == 0)
1445   {
1446     if (dest->magic == M_MMDF)
1447       fputs (MMDF_SEP, msg->fp);
1448
1449     if ((msg->magic == M_MBOX || msg->magic ==  M_MMDF) &&
1450         flags & M_ADD_FROM)
1451     {
1452       if (hdr)
1453       {
1454         if (hdr->env->return_path)
1455           p = hdr->env->return_path;
1456         else if (hdr->env->sender)
1457           p = hdr->env->sender;
1458         else
1459           p = hdr->env->from;
1460       }
1461
1462       fprintf (msg->fp, "From %s %s", p ? p->mailbox : NONULL(Username), ctime (&msg->received));
1463     }
1464   }
1465   else
1466     FREE (&msg);
1467
1468   return msg;
1469 }
1470
1471 /* check for new mail */
1472 int mx_check_mailbox (CONTEXT *ctx, int *index_hint, int lock)
1473 {
1474   int rc;
1475
1476 #ifdef USE_COMPRESSED
1477   if (ctx->compressinfo)
1478     return mutt_check_mailbox_compressed (ctx);
1479 #endif
1480
1481   if (ctx)
1482   {
1483     if (ctx->locked) lock = 0;
1484
1485     switch (ctx->magic)
1486     {
1487       case M_MBOX:
1488       case M_MMDF:
1489
1490         if (lock)
1491         {
1492           mutt_block_signals ();
1493           if (mbox_lock_mailbox (ctx, 0, 0) == -1)
1494           {
1495             mutt_unblock_signals ();
1496             return M_LOCKED;
1497           }
1498         }
1499         
1500         rc = mbox_check_mailbox (ctx, index_hint);
1501
1502         if (lock)
1503         {
1504           mutt_unblock_signals ();
1505           mbox_unlock_mailbox (ctx);
1506         }
1507         
1508         return rc;
1509
1510
1511       case M_MH:
1512         return (mh_check_mailbox (ctx, index_hint));
1513       case M_MAILDIR:
1514         return (maildir_check_mailbox (ctx, index_hint));
1515
1516 #ifdef USE_IMAP
1517       case M_IMAP:
1518         return (imap_check_mailbox (ctx, index_hint, 0));
1519 #endif /* USE_IMAP */
1520
1521 #ifdef USE_POP
1522       case M_POP:
1523         return (pop_check_mailbox (ctx, index_hint));
1524 #endif /* USE_POP */
1525
1526 #ifdef USE_NNTP
1527       case M_NNTP:
1528         return (nntp_check_mailbox (ctx));
1529 #endif /* USE_NNTP */
1530     }
1531   }
1532
1533   dprint (1, (debugfile, "mx_check_mailbox: null or invalid context.\n"));
1534   return (-1);
1535 }
1536
1537 /* return a stream pointer for a message */
1538 MESSAGE *mx_open_message (CONTEXT *ctx, int msgno)
1539 {
1540   MESSAGE *msg;
1541   
1542   msg = safe_calloc (1, sizeof (MESSAGE));
1543   switch (msg->magic = ctx->magic)
1544   {
1545     case M_MBOX:
1546     case M_MMDF:
1547       msg->fp = ctx->fp;
1548       break;
1549
1550     case M_MH:
1551     case M_MAILDIR:
1552     {
1553       HEADER *cur = ctx->hdrs[msgno];
1554       char path[_POSIX_PATH_MAX];
1555       
1556       snprintf (path, sizeof (path), "%s/%s", ctx->path, cur->path);
1557       
1558       if ((msg->fp = fopen (path, "r")) == NULL && errno == ENOENT &&
1559           ctx->magic == M_MAILDIR)
1560         msg->fp = maildir_open_find_message (ctx->path, cur->path);
1561       
1562       if (msg->fp == NULL)
1563       {
1564         mutt_perror (path);
1565         dprint (1, (debugfile, "mx_open_message: fopen: %s: %s (errno %d).\n",
1566                     path, strerror (errno), errno));
1567         FREE (&msg);
1568       }
1569     }
1570     break;
1571     
1572 #ifdef USE_IMAP
1573     case M_IMAP:
1574     {
1575       if (imap_fetch_message (msg, ctx, msgno) != 0)
1576         FREE (&msg);
1577       break;
1578     }
1579 #endif /* USE_IMAP */
1580
1581 #ifdef USE_POP
1582     case M_POP:
1583     {
1584       if (pop_fetch_message (msg, ctx, msgno) != 0)
1585         FREE (&msg);
1586       break;
1587     }
1588 #endif /* USE_POP */
1589
1590 #ifdef USE_NNTP
1591     case M_NNTP:
1592     {
1593       if (nntp_fetch_message (msg, ctx, msgno) != 0)
1594         FREE (&msg);
1595       break;
1596     }
1597 #endif /* USE_NNTP */
1598
1599     default:
1600       dprint (1, (debugfile, "mx_open_message(): function not implemented for mailbox type %d.\n", ctx->magic));
1601       FREE (&msg);
1602       break;
1603   }
1604   return (msg);
1605 }
1606
1607 /* commit a message to a folder */
1608
1609 int mx_commit_message (MESSAGE *msg, CONTEXT *ctx)
1610 {
1611   int r = 0;
1612
1613   if (!(msg->write && ctx->append))
1614   {
1615     dprint (1, (debugfile, "mx_commit_message(): msg->write = %d, ctx->append = %d\n",
1616                 msg->write, ctx->append));
1617     return -1;
1618   }
1619
1620   switch (msg->magic)
1621   {
1622     case M_MMDF:
1623     {
1624       if (fputs (MMDF_SEP, msg->fp) == EOF)
1625         r = -1;
1626       break;
1627     }
1628     
1629     case M_MBOX:
1630     {
1631       if (fputc ('\n', msg->fp) == EOF)
1632         r = -1;
1633       break;
1634     }
1635
1636 #ifdef USE_IMAP
1637     case M_IMAP:
1638     {
1639       if ((r = safe_fclose (&msg->fp)) == 0)
1640         r = imap_append_message (ctx, msg);
1641       break;
1642     }
1643 #endif
1644     
1645     case M_MAILDIR:
1646     {
1647       r = maildir_commit_message (ctx, msg, NULL);
1648       break;
1649     }
1650     
1651     case M_MH:
1652     {
1653       r = mh_commit_message (ctx, msg, NULL);
1654       break;
1655     }
1656   }
1657   
1658   if (r == 0 && (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
1659       && (fflush (msg->fp) == EOF || fsync (fileno (msg->fp)) == -1))
1660   {
1661     mutt_perror _("Can't write message");
1662     r = -1;
1663   }
1664  
1665   return r;
1666 }
1667
1668 /* close a pointer to a message */
1669 int mx_close_message (MESSAGE **msg)
1670 {
1671   int r = 0;
1672
1673   if ((*msg)->magic == M_MH || (*msg)->magic == M_MAILDIR
1674 #ifdef USE_IMAP
1675       || (*msg)->magic == M_IMAP
1676 #endif
1677 #ifdef USE_POP
1678       || (*msg)->magic == M_POP
1679 #endif
1680 #ifdef USE_NNTP
1681       || (*msg)->magic == M_NNTP
1682 #endif
1683       )
1684   {
1685     r = safe_fclose (&(*msg)->fp);
1686   }
1687   else
1688     (*msg)->fp = NULL;
1689
1690   if ((*msg)->path)
1691   {
1692     dprint (1, (debugfile, "mx_close_message (): unlinking %s\n",
1693                 (*msg)->path));
1694     unlink ((*msg)->path);
1695     FREE (&(*msg)->path);
1696   }
1697
1698   FREE (msg);
1699   return (r);
1700 }
1701
1702 void mx_alloc_memory (CONTEXT *ctx)
1703 {
1704   int i;
1705   size_t s = MAX (sizeof (HEADER *), sizeof (int));
1706   
1707   if ((ctx->hdrmax + 25) * s < ctx->hdrmax * s)
1708   {
1709     mutt_error _("Integer overflow -- can't allocate memory.");
1710     sleep (1);
1711     mutt_exit (1);
1712   }
1713   
1714   if (ctx->hdrs)
1715   {
1716     safe_realloc (&ctx->hdrs, sizeof (HEADER *) * (ctx->hdrmax += 25));
1717     safe_realloc (&ctx->v2r, sizeof (int) * ctx->hdrmax);
1718   }
1719   else
1720   {
1721     ctx->hdrs = safe_calloc ((ctx->hdrmax += 25), sizeof (HEADER *));
1722     ctx->v2r = safe_calloc (ctx->hdrmax, sizeof (int));
1723   }
1724   for (i = ctx->msgcount ; i < ctx->hdrmax ; i++)
1725   {
1726     ctx->hdrs[i] = NULL;
1727     ctx->v2r[i] = -1;
1728   }
1729 }
1730
1731 /* this routine is called to update the counts in the context structure for
1732  * the last message header parsed.
1733  */
1734 void mx_update_context (CONTEXT *ctx, int new_messages)
1735 {
1736   HEADER *h;
1737   int msgno;
1738
1739   for (msgno = ctx->msgcount - new_messages; msgno < ctx->msgcount; msgno++)
1740   {
1741     h = ctx->hdrs[msgno];
1742
1743     if (WithCrypto)
1744     {
1745       /* NOTE: this _must_ be done before the check for mailcap! */
1746       h->security = crypt_query (h->content);
1747     }
1748
1749     if (!ctx->pattern)
1750     {
1751       ctx->v2r[ctx->vcount] = msgno;
1752       h->virtual = ctx->vcount++;
1753     }
1754     else
1755       h->virtual = -1;
1756     h->msgno = msgno;
1757
1758     if (h->env->supersedes)
1759     {
1760       HEADER *h2;
1761
1762       if (!ctx->id_hash)        
1763         ctx->id_hash = mutt_make_id_hash (ctx);
1764
1765       h2 = hash_find (ctx->id_hash, h->env->supersedes);
1766
1767       /* FREE (&h->env->supersedes); should I ? */
1768       if (h2)
1769       {
1770         h2->superseded = 1;
1771         if (option (OPTSCORE)) 
1772           mutt_score_message (ctx, h2, 1);
1773       }
1774     }
1775
1776     /* add this message to the hash tables */
1777     if (ctx->id_hash && h->env->message_id)
1778       hash_insert (ctx->id_hash, h->env->message_id, h, 0);
1779     if (ctx->subj_hash && h->env->real_subj)
1780       hash_insert (ctx->subj_hash, h->env->real_subj, h, 1);
1781
1782     if (option (OPTSCORE)) 
1783       mutt_score_message (ctx, h, 0);
1784
1785     if (h->changed)
1786       ctx->changed = 1;
1787     if (h->flagged)
1788       ctx->flagged++;
1789     if (h->deleted)
1790       ctx->deleted++;
1791     if (!h->read)
1792     {
1793       ctx->unread++;
1794       if (!h->old)
1795         ctx->new++;
1796     }
1797   }
1798 }
1799
1800 /*
1801  * Return:
1802  * 1 if the specified mailbox contains 0 messages.
1803  * 0 if the mailbox contains messages
1804  * -1 on error
1805  */
1806 int mx_check_empty (const char *path)
1807 {
1808   switch (mx_get_magic (path))
1809   {
1810     case M_MBOX:
1811     case M_MMDF:
1812       return mbox_check_empty (path);
1813     case M_MH:
1814       return mh_check_empty (path);
1815     case M_MAILDIR:
1816       return maildir_check_empty (path);
1817     default:
1818       errno = EINVAL;
1819       return -1;
1820   }
1821   /* not reached */
1822 }