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