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