]> git.llucax.com Git - software/mutt-debian.git/blob - mh.c
debian/extra/rc/compressed-folders.rc: added support for xz-compressed folders (Close...
[software/mutt-debian.git] / mh.c
1 /*
2  * Copyright (C) 1996-2002,2007,2009 Michael R. Elkins <me@mutt.org>
3  * Copyright (C) 1999-2005 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19
20 /*
21  * This file contains routines specific to MH and ``maildir'' style
22  * mailboxes.
23  */
24
25 #if HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include "mutt.h"
30 #include "mailbox.h"
31 #include "mx.h"
32 #include "copy.h"
33 #include "sort.h"
34 #if USE_HCACHE
35 #include "hcache.h"
36 #endif
37 #include "mutt_curses.h"
38
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <dirent.h>
42 #include <limits.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <ctype.h>
49 #include <errno.h>
50 #include <string.h>
51 #include <utime.h>
52
53 #if HAVE_SYS_TIME_H
54 #include <sys/time.h>
55 #endif
56
57 #define         INS_SORT_THRESHOLD              6
58
59 struct maildir
60 {
61   HEADER *h;
62   char *canon_fname;
63   unsigned header_parsed:1;
64 #ifdef HAVE_DIRENT_D_INO
65   ino_t inode;
66 #endif /* HAVE_DIRENT_D_INO */
67   struct maildir *next;
68 };
69
70 struct mh_sequences
71 {
72   int max;
73   short *flags;
74 };
75
76 struct mh_data
77 {
78   time_t mtime_cur;
79   mode_t mh_umask;
80 };
81
82 /* mh_sequences support */
83
84 #define MH_SEQ_UNSEEN  (1 << 0)
85 #define MH_SEQ_REPLIED (1 << 1)
86 #define MH_SEQ_FLAGGED (1 << 2)
87
88 static inline struct mh_data *mh_data (CONTEXT *ctx)
89 {
90   return (struct mh_data*)ctx->data;
91 }
92
93 static void mhs_alloc (struct mh_sequences *mhs, int i)
94 {
95   int j;
96   int newmax;
97
98   if (i > mhs->max || !mhs->flags)
99   {
100     newmax = i + 128;
101     j = mhs->flags ? mhs->max + 1 : 0;
102     safe_realloc (&mhs->flags, sizeof (mhs->flags[0]) * (newmax + 1));
103     while (j <= newmax)
104       mhs->flags[j++] = 0;
105
106     mhs->max = newmax;
107   }
108 }
109
110 static void mhs_free_sequences (struct mh_sequences *mhs)
111 {
112   FREE (&mhs->flags);
113 }
114
115 static short mhs_check (struct mh_sequences *mhs, int i)
116 {
117   if (!mhs->flags || i > mhs->max)
118     return 0;
119   else
120     return mhs->flags[i];
121 }
122
123 static short mhs_set (struct mh_sequences *mhs, int i, short f)
124 {
125   mhs_alloc (mhs, i);
126   mhs->flags[i] |= f;
127   return mhs->flags[i];
128 }
129
130 #if 0
131
132 /* unused */
133
134 static short mhs_unset (struct mh_sequences *mhs, int i, short f)
135 {
136   mhs_alloc (mhs, i);
137   mhs->flags[i] &= ~f;
138   return mhs->flags[i];
139 }
140
141 #endif
142
143 static int mh_read_token (char *t, int *first, int *last)
144 {
145   char *p;
146   if ((p = strchr (t, '-')))
147   {
148     *p++ = '\0';
149     if (mutt_atoi (t, first) < 0 || mutt_atoi (p, last) < 0)
150       return -1;
151   }
152   else
153   {
154     if (mutt_atoi (t, first) < 0)
155       return -1;
156     *last = *first;
157   }
158   return 0;
159 }
160
161 static int mh_read_sequences (struct mh_sequences *mhs, const char *path)
162 {
163   FILE *fp;
164   int line = 1;
165   char *buff = NULL;
166   char *t;
167   size_t sz = 0;
168
169   short f;
170   int first, last, rc = 0;
171
172   char pathname[_POSIX_PATH_MAX];
173   snprintf (pathname, sizeof (pathname), "%s/.mh_sequences", path);
174
175   if (!(fp = fopen (pathname, "r")))
176     return 0; /* yes, ask callers to silently ignore the error */
177
178   while ((buff = mutt_read_line (buff, &sz, fp, &line, 0)))
179   {
180     if (!(t = strtok (buff, " \t:")))
181       continue;
182
183     if (!mutt_strcmp (t, MhUnseen))
184       f = MH_SEQ_UNSEEN;
185     else if (!mutt_strcmp (t, MhFlagged))
186       f = MH_SEQ_FLAGGED;
187     else if (!mutt_strcmp (t, MhReplied))
188       f = MH_SEQ_REPLIED;
189     else                        /* unknown sequence */
190       continue;
191
192     while ((t = strtok (NULL, " \t:")))
193     {
194       if (mh_read_token (t, &first, &last) < 0)
195       {
196         mhs_free_sequences (mhs);
197         rc = -1;
198         goto out;
199       }
200       for (; first <= last; first++)
201         mhs_set (mhs, first, f);
202     }
203   }
204
205   rc = 0;
206
207 out:
208   FREE (&buff);
209   safe_fclose (&fp);
210   return rc;
211 }
212
213 static inline mode_t mh_umask (CONTEXT* ctx)
214 {
215   struct stat st;
216   struct mh_data* data = mh_data (ctx);
217
218   if (data && data->mh_umask)
219     return data->mh_umask;
220
221   if (stat (ctx->path, &st))
222   {
223     dprint (1, (debugfile, "stat failed on %s\n", ctx->path));
224     return 077;
225   }
226
227   return 0777 & ~st.st_mode;
228 }
229
230 int mh_buffy (const char *path)
231 {
232   int i, r = 0;
233   struct mh_sequences mhs;
234   memset (&mhs, 0, sizeof (mhs));
235
236   if (mh_read_sequences (&mhs, path) < 0)
237     return 0;
238   for (i = 0; !r && i <= mhs.max; i++)
239     if (mhs_check (&mhs, i) & MH_SEQ_UNSEEN)
240       r = 1;
241   mhs_free_sequences (&mhs);
242   return r;
243 }
244
245 static int mh_mkstemp (CONTEXT * dest, FILE ** fp, char **tgt)
246 {
247   int fd;
248   char path[_POSIX_PATH_MAX];
249   mode_t omask;
250
251   omask = umask (mh_umask (dest));
252   FOREVER
253   {
254     snprintf (path, _POSIX_PATH_MAX, "%s/.mutt-%s-%d-%d",
255               dest->path, NONULL (Hostname), (int) getpid (), Counter++);
256     if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0666)) == -1)
257     {
258       if (errno != EEXIST)
259       {
260         mutt_perror (path);
261         umask (omask);
262         return -1;
263       }
264     }
265     else
266     {
267       *tgt = safe_strdup (path);
268       break;
269     }
270   }
271   umask (omask);
272
273   if ((*fp = fdopen (fd, "w")) == NULL)
274   {
275     FREE (tgt);         /* __FREE_CHECKED__ */
276     close (fd);
277     unlink (path);
278     return (-1);
279   }
280
281   return 0;
282 }
283
284 static void mhs_write_one_sequence (FILE * fp, struct mh_sequences *mhs,
285                                     short f, const char *tag)
286 {
287   int i;
288   int first, last;
289   fprintf (fp, "%s:", tag);
290
291   first = -1;
292   last = -1;
293
294   for (i = 0; i <= mhs->max; i++)
295   {
296     if ((mhs_check (mhs, i) & f))
297     {
298       if (first < 0)
299         first = i;
300       else
301         last = i;
302     }
303     else if (first >= 0)
304     {
305       if (last < 0)
306         fprintf (fp, " %d", first);
307       else
308         fprintf (fp, " %d-%d", first, last);
309
310       first = -1;
311       last = -1;
312     }
313   }
314
315   if (first >= 0)
316   {
317     if (last < 0)
318       fprintf (fp, " %d", first);
319     else
320       fprintf (fp, " %d-%d", first, last);
321   }
322
323   fputc ('\n', fp);
324 }
325
326 /* XXX - we don't currently remove deleted messages from sequences we don't know.  Should we? */
327
328 static void mh_update_sequences (CONTEXT * ctx)
329 {
330   FILE *ofp, *nfp;
331
332   char sequences[_POSIX_PATH_MAX];
333   char *tmpfname;
334   char *buff = NULL;
335   char *p;
336   size_t s;
337   int l = 0;
338   int i;
339
340   int unseen = 0;
341   int flagged = 0;
342   int replied = 0;
343
344   char seq_unseen[STRING];
345   char seq_replied[STRING];
346   char seq_flagged[STRING];
347
348
349   struct mh_sequences mhs;
350   memset (&mhs, 0, sizeof (mhs));
351
352   snprintf (seq_unseen, sizeof (seq_unseen), "%s:", NONULL (MhUnseen));
353   snprintf (seq_replied, sizeof (seq_replied), "%s:", NONULL (MhReplied));
354   snprintf (seq_flagged, sizeof (seq_flagged), "%s:", NONULL (MhFlagged));
355
356   if (mh_mkstemp (ctx, &nfp, &tmpfname) != 0)
357   {
358     /* error message? */
359     return;
360   }
361
362   snprintf (sequences, sizeof (sequences), "%s/.mh_sequences", ctx->path);
363
364
365   /* first, copy unknown sequences */
366   if ((ofp = fopen (sequences, "r")))
367   {
368     while ((buff = mutt_read_line (buff, &s, ofp, &l, 0)))
369     {
370       if (!mutt_strncmp (buff, seq_unseen, mutt_strlen (seq_unseen)))
371         continue;
372       if (!mutt_strncmp (buff, seq_flagged, mutt_strlen (seq_flagged)))
373         continue;
374       if (!mutt_strncmp (buff, seq_replied, mutt_strlen (seq_replied)))
375         continue;
376
377       fprintf (nfp, "%s\n", buff);
378     }
379   }
380   safe_fclose (&ofp);
381
382   /* now, update our unseen, flagged, and replied sequences */
383   for (l = 0; l < ctx->msgcount; l++)
384   {
385     if (ctx->hdrs[l]->deleted)
386       continue;
387
388     if ((p = strrchr (ctx->hdrs[l]->path, '/')))
389       p++;
390     else
391       p = ctx->hdrs[l]->path;
392
393     if (mutt_atoi (p, &i) < 0)
394       continue;
395
396     if (!ctx->hdrs[l]->read)
397     {
398       mhs_set (&mhs, i, MH_SEQ_UNSEEN);
399       unseen++;
400     }
401     if (ctx->hdrs[l]->flagged)
402     {
403       mhs_set (&mhs, i, MH_SEQ_FLAGGED);
404       flagged++;
405     }
406     if (ctx->hdrs[l]->replied)
407     {
408       mhs_set (&mhs, i, MH_SEQ_REPLIED);
409       replied++;
410     }
411   }
412
413   /* write out the new sequences */
414   if (unseen)
415     mhs_write_one_sequence (nfp, &mhs, MH_SEQ_UNSEEN, NONULL (MhUnseen));
416   if (flagged)
417     mhs_write_one_sequence (nfp, &mhs, MH_SEQ_FLAGGED, NONULL (MhFlagged));
418   if (replied)
419     mhs_write_one_sequence (nfp, &mhs, MH_SEQ_REPLIED, NONULL (MhReplied));
420
421   mhs_free_sequences (&mhs);
422
423
424   /* try to commit the changes - no guarantee here */
425   safe_fclose (&nfp);
426
427   unlink (sequences);
428   if (safe_rename (tmpfname, sequences) != 0)
429   {
430     /* report an error? */
431     unlink (tmpfname);
432   }
433
434   FREE (&tmpfname);
435 }
436
437 static void mh_sequences_add_one (CONTEXT * ctx, int n, short unseen,
438                                   short flagged, short replied)
439 {
440   short unseen_done = 0;
441   short flagged_done = 0;
442   short replied_done = 0;
443
444   FILE *ofp = NULL, *nfp = NULL;
445
446   char *tmpfname;
447   char sequences[_POSIX_PATH_MAX];
448
449   char seq_unseen[STRING];
450   char seq_replied[STRING];
451   char seq_flagged[STRING];
452
453   char *buff = NULL;
454   int line;
455   size_t sz;
456
457   if (mh_mkstemp (ctx, &nfp, &tmpfname) == -1)
458     return;
459
460   snprintf (seq_unseen, sizeof (seq_unseen), "%s:", NONULL (MhUnseen));
461   snprintf (seq_replied, sizeof (seq_replied), "%s:", NONULL (MhReplied));
462   snprintf (seq_flagged, sizeof (seq_flagged), "%s:", NONULL (MhFlagged));
463
464   snprintf (sequences, sizeof (sequences), "%s/.mh_sequences", ctx->path);
465   if ((ofp = fopen (sequences, "r")))
466   {
467     while ((buff = mutt_read_line (buff, &sz, ofp, &line, 0)))
468     {
469       if (unseen && !strncmp (buff, seq_unseen, mutt_strlen (seq_unseen)))
470       {
471         fprintf (nfp, "%s %d\n", buff, n);
472         unseen_done = 1;
473       }
474       else if (flagged
475                && !strncmp (buff, seq_flagged, mutt_strlen (seq_flagged)))
476       {
477         fprintf (nfp, "%s %d\n", buff, n);
478         flagged_done = 1;
479       }
480       else if (replied
481                && !strncmp (buff, seq_replied, mutt_strlen (seq_replied)))
482       {
483         fprintf (nfp, "%s %d\n", buff, n);
484         replied_done = 1;
485       }
486       else
487         fprintf (nfp, "%s\n", buff);
488     }
489   }
490   safe_fclose (&ofp);
491   FREE (&buff);
492
493   if (!unseen_done && unseen)
494     fprintf (nfp, "%s: %d\n", NONULL (MhUnseen), n);
495   if (!flagged_done && flagged)
496     fprintf (nfp, "%s: %d\n", NONULL (MhFlagged), n);
497   if (!replied_done && replied)
498     fprintf (nfp, "%s: %d\n", NONULL (MhReplied), n);
499
500   safe_fclose (&nfp);
501
502   unlink (sequences);
503   if (safe_rename (tmpfname, sequences) != 0)
504     unlink (tmpfname);
505
506   FREE (&tmpfname);
507 }
508
509 static void mh_update_maildir (struct maildir *md, struct mh_sequences *mhs)
510 {
511   int i;
512   short f;
513   char *p;
514
515   for (; md; md = md->next)
516   {
517     if ((p = strrchr (md->h->path, '/')))
518       p++;
519     else
520       p = md->h->path;
521
522     if (mutt_atoi (p, &i) < 0)
523       continue;
524     f = mhs_check (mhs, i);
525
526     md->h->read = (f & MH_SEQ_UNSEEN) ? 0 : 1;
527     md->h->flagged = (f & MH_SEQ_FLAGGED) ? 1 : 0;
528     md->h->replied = (f & MH_SEQ_REPLIED) ? 1 : 0;
529   }
530 }
531
532 /* maildir support */
533
534 static void maildir_free_entry (struct maildir **md)
535 {
536   if (!md || !*md)
537     return;
538
539   FREE (&(*md)->canon_fname);
540   if ((*md)->h)
541     mutt_free_header (&(*md)->h);
542
543   FREE (md);            /* __FREE_CHECKED__ */
544 }
545
546 static void maildir_free_maildir (struct maildir **md)
547 {
548   struct maildir *p, *q;
549
550   if (!md || !*md)
551     return;
552
553   for (p = *md; p; p = q)
554   {
555     q = p->next;
556     maildir_free_entry (&p);
557   }
558 }
559
560 static void maildir_parse_flags (HEADER * h, const char *path)
561 {
562   char *p, *q = NULL;
563
564   h->flagged = 0;
565   h->read = 0;
566   h->replied = 0;
567
568   if ((p = strrchr (path, ':')) != NULL && mutt_strncmp (p + 1, "2,", 2) == 0)
569   {
570     p += 3;
571     
572     mutt_str_replace (&h->maildir_flags, p);
573     q = h->maildir_flags;
574
575     while (*p)
576     {
577       switch (*p)
578       {
579       case 'F':
580
581         h->flagged = 1;
582         break;
583
584       case 'S':         /* seen */
585
586         h->read = 1;
587         break;
588
589       case 'R':         /* replied */
590
591         h->replied = 1;
592         break;
593
594       case 'T':         /* trashed */
595         h->trash = 1;
596         h->deleted = 1;
597         break;
598       
599       default:
600         *q++ = *p;
601         break;
602       }
603       p++;
604     }
605   }
606   
607   if (q == h->maildir_flags)
608     FREE (&h->maildir_flags);
609   else if (q)
610     *q = '\0';
611 }
612
613 static void maildir_update_mtime (CONTEXT * ctx)
614 {
615   char buf[_POSIX_PATH_MAX];
616   struct stat st;
617   struct mh_data *data = mh_data (ctx);
618
619   if (ctx->magic == M_MAILDIR)
620   {
621     snprintf (buf, sizeof (buf), "%s/%s", ctx->path, "cur");
622     if (stat (buf, &st) == 0)
623       data->mtime_cur = st.st_mtime;
624     snprintf (buf, sizeof (buf), "%s/%s", ctx->path, "new");
625   }
626   else
627   {
628     snprintf (buf, sizeof (buf), "%s/.mh_sequences", ctx->path);
629     if (stat (buf, &st) == 0)
630       data->mtime_cur = st.st_mtime;
631
632     strfcpy (buf, ctx->path, sizeof (buf));
633   }
634
635   if (stat (buf, &st) == 0)
636     ctx->mtime = st.st_mtime;
637 }
638
639 /* 
640  * Actually parse a maildir message.  This may also be used to fill
641  * out a fake header structure generated by lazy maildir parsing.
642  */
643 static HEADER *maildir_parse_message (int magic, const char *fname,
644                                       int is_old, HEADER * _h)
645 {
646   FILE *f;
647   HEADER *h = _h;
648   struct stat st;
649
650   if ((f = fopen (fname, "r")) != NULL)
651   {
652     if (!h)
653       h = mutt_new_header ();
654     h->env = mutt_read_rfc822_header (f, h, 0, 0);
655
656     fstat (fileno (f), &st);
657     safe_fclose (&f);
658
659     if (!h->received)
660       h->received = h->date_sent;
661
662     /* always update the length since we have fresh information available. */
663     h->content->length = st.st_size - h->content->offset;
664
665     h->index = -1;
666
667     if (magic == M_MAILDIR)
668     {
669       /* 
670        * maildir stores its flags in the filename, so ignore the
671        * flags in the header of the message 
672        */
673
674       h->old = is_old;
675       maildir_parse_flags (h, fname);
676     }
677     return h;
678   }
679   return NULL;
680 }
681
682 /* Ignore the garbage files.  A valid MH message consists of only
683  * digits.  Deleted message get moved to a filename with a comma before
684  * it.
685  */
686
687 int mh_valid_message (const char *s)
688 {
689   for (; *s; s++)
690   {
691     if (!isdigit ((unsigned char) *s))
692       return 0;
693   }
694   return 1;
695 }
696
697 static int maildir_parse_dir (CONTEXT * ctx, struct maildir ***last,
698                               const char *subdir, int *count,
699                               progress_t *progress)
700 {
701   DIR *dirp;
702   struct dirent *de;
703   char buf[_POSIX_PATH_MAX];
704   int is_old = 0;
705   struct maildir *entry;
706   HEADER *h;
707
708   if (subdir)
709   {
710     snprintf (buf, sizeof (buf), "%s/%s", ctx->path, subdir);
711     is_old = (mutt_strcmp ("cur", subdir) == 0);
712   }
713   else
714     strfcpy (buf, ctx->path, sizeof (buf));
715
716   if ((dirp = opendir (buf)) == NULL)
717     return -1;
718
719   while ((de = readdir (dirp)) != NULL)
720   {
721     if ((ctx->magic == M_MH && !mh_valid_message (de->d_name))
722         || (ctx->magic == M_MAILDIR && *de->d_name == '.'))
723       continue;
724
725     /* FOO - really ignore the return value? */
726     dprint (2,
727             (debugfile, "%s:%d: queueing %s\n", __FILE__, __LINE__,
728              de->d_name));
729
730     h = mutt_new_header ();
731     h->old = is_old;
732     if (ctx->magic == M_MAILDIR)
733       maildir_parse_flags (h, de->d_name);
734
735     if (count)
736     {
737       (*count)++;
738       if (!ctx->quiet && progress)
739         mutt_progress_update (progress, *count, -1);
740     }
741
742     if (subdir)
743     {
744       char tmp[_POSIX_PATH_MAX];
745       snprintf (tmp, sizeof (tmp), "%s/%s", subdir, de->d_name);
746       h->path = safe_strdup (tmp);
747     }
748     else
749       h->path = safe_strdup (de->d_name);
750
751     entry = safe_calloc (sizeof (struct maildir), 1);
752     entry->h = h;
753 #ifdef HAVE_DIRENT_D_INO
754     entry->inode = de->d_ino;
755 #endif /* HAVE_DIRENT_D_INO */
756     **last = entry;
757     *last = &entry->next;
758   }
759
760   closedir (dirp);
761
762   return 0;
763 }
764
765 static int maildir_add_to_context (CONTEXT * ctx, struct maildir *md)
766 {
767   int oldmsgcount = ctx->msgcount;
768
769   while (md)
770   {
771
772     dprint (2, (debugfile, "%s:%d maildir_add_to_context(): Considering %s\n",
773                 __FILE__, __LINE__, NONULL (md->canon_fname)));
774
775     if (md->h)
776     {
777       dprint (2,
778               (debugfile,
779                "%s:%d Adding header structure. Flags: %s%s%s%s%s\n", __FILE__,
780                __LINE__, md->h->flagged ? "f" : "", md->h->deleted ? "D" : "",
781                md->h->replied ? "r" : "", md->h->old ? "O" : "",
782                md->h->read ? "R" : ""));
783       if (ctx->msgcount == ctx->hdrmax)
784         mx_alloc_memory (ctx);
785
786       ctx->hdrs[ctx->msgcount] = md->h;
787       ctx->hdrs[ctx->msgcount]->index = ctx->msgcount;
788       ctx->size +=
789         md->h->content->length + md->h->content->offset -
790         md->h->content->hdr_offset;
791
792       md->h = NULL;
793       ctx->msgcount++;
794     }
795     md = md->next;
796   }
797
798   if (ctx->msgcount > oldmsgcount)
799   {
800     mx_update_context (ctx, ctx->msgcount - oldmsgcount);
801     return 1;
802   }
803   return 0;
804 }
805
806 static int maildir_move_to_context (CONTEXT * ctx, struct maildir **md)
807 {
808   int r;
809   r = maildir_add_to_context (ctx, *md);
810   maildir_free_maildir (md);
811   return r;
812 }
813
814 #if USE_HCACHE
815 static size_t maildir_hcache_keylen (const char *fn)
816 {
817   const char * p = strrchr (fn, ':');
818   return p ? (size_t) (p - fn) : mutt_strlen(fn);
819 }
820 #endif
821
822 #if HAVE_DIRENT_D_INO
823 static int md_cmp_inode (struct maildir *a, struct maildir *b)
824 {
825   return a->inode - b->inode;
826 }
827 #endif
828
829 static int md_cmp_path (struct maildir *a, struct maildir *b)
830 {
831   return strcmp (a->h->path, b->h->path);
832 }
833
834 /*
835  * Merge two maildir lists according to the inode numbers.
836  */
837 static struct maildir*  maildir_merge_lists (struct maildir *left,
838                                              struct maildir *right,
839                                              int (*cmp) (struct maildir *,
840                                                          struct maildir *))
841 {
842   struct maildir* head;
843   struct maildir* tail;
844
845   if (left && right) 
846   {
847     if (cmp (left, right) < 0)
848     {
849       head = left;
850       left = left->next;
851     }
852     else 
853     {
854       head = right;
855       right = right->next;
856     }
857   } 
858   else 
859   {
860     if (left) 
861       return left;
862     else 
863       return right;
864   }
865     
866   tail = head;
867
868   while (left && right) 
869   {
870     if (cmp (left, right) < 0)
871     {
872       tail->next = left;
873       left = left->next;
874     } 
875     else 
876     {
877       tail->next = right;
878       right = right->next;
879     }
880     tail = tail->next;
881   }
882
883   if (left) 
884   {
885     tail->next = left;
886   }
887   else
888   {
889     tail->next = right;
890   }
891
892   return head;
893 }
894
895 static struct maildir* maildir_ins_sort (struct maildir* list,
896                                          int (*cmp) (struct maildir *,
897                                                      struct maildir *))
898 {
899   struct maildir *tmp, *last, *ret = NULL, *back;
900
901   ret = list;
902   list = list->next;
903   ret->next = NULL;
904
905   while (list)
906   {
907     last = NULL;
908     back = list->next;
909     for (tmp = ret; tmp && cmp (tmp, list) <= 0; tmp = tmp->next)
910       last = tmp;
911
912     list->next = tmp;
913     if (last)
914       last->next = list;
915     else
916       ret = list;
917
918     list = back;
919   }
920
921   return ret;
922 }
923
924 /*
925  * Sort maildir list according to inode.
926  */
927 static struct maildir* maildir_sort (struct maildir* list, size_t len,
928                                      int (*cmp) (struct maildir *,
929                                                  struct maildir *))
930 {
931   struct maildir* left = list;
932   struct maildir* right = list;
933   size_t c = 0;
934
935   if (!list || !list->next) 
936   {
937     return list;
938   }
939
940   if (len != (size_t)(-1) && len <= INS_SORT_THRESHOLD)
941     return maildir_ins_sort (list, cmp);
942
943   list = list->next;
944   while (list && list->next) 
945   {
946     right = right->next;
947     list = list->next->next;
948     c++;
949   }
950
951   list = right;
952   right = right->next;
953   list->next = 0;
954
955   left = maildir_sort (left, c, cmp);
956   right = maildir_sort (right, c, cmp);
957   return maildir_merge_lists (left, right, cmp);
958 }
959
960 /* Sorts mailbox into it's natural order.
961  * Currently only defined for MH where files are numbered.
962  */
963 static void mh_sort_natural (CONTEXT *ctx, struct maildir **md)
964 {
965   if (!ctx || !md || !*md || ctx->magic != M_MH || Sort != SORT_ORDER)
966     return;
967   dprint (4, (debugfile, "maildir: sorting %s into natural order\n",
968               ctx->path));
969   *md = maildir_sort (*md, (size_t) -1, md_cmp_path);
970 }
971
972 #if HAVE_DIRENT_D_INO
973 static struct maildir *skip_duplicates (struct maildir *p, struct maildir **last)
974 {
975   /*
976    * Skip ahead to the next non-duplicate message.
977    *
978    * p should never reach NULL, because we couldn't have reached this point unless
979    * there was a message that needed to be parsed.
980    *
981    * the check for p->header_parsed is likely unnecessary since the dupes will most
982    * likely be at the head of the list.  but it is present for consistency with
983    * the check at the top of the for() loop in maildir_delayed_parsing().
984    */
985   while (!p->h || p->header_parsed) {
986     *last = p;
987     p = p->next;
988   }
989   return p;
990 }
991 #endif
992
993 /* 
994  * This function does the second parsing pass
995  */
996 static void maildir_delayed_parsing (CONTEXT * ctx, struct maildir **md,
997                               progress_t *progress)
998
999   struct maildir *p, *last = NULL;
1000   char fn[_POSIX_PATH_MAX];
1001   int count;
1002 #if HAVE_DIRENT_D_INO
1003   int sort = 0;
1004 #endif
1005 #if USE_HCACHE
1006   header_cache_t *hc = NULL;
1007   void *data;
1008   struct timeval *when = NULL;
1009   struct stat lastchanged;
1010   int ret;
1011 #endif
1012
1013 #if HAVE_DIRENT_D_INO
1014 #define DO_SORT()       do { \
1015   if (!sort) \
1016   { \
1017     dprint (4, (debugfile, "maildir: need to sort %s by inode\n", ctx->path)); \
1018     p = maildir_sort (p, (size_t) -1, md_cmp_inode); \
1019     if (!last) \
1020       *md = p; \
1021     else \
1022       last->next = p; \
1023     sort = 1; \
1024     p = skip_duplicates (p, &last); \
1025     snprintf (fn, sizeof (fn), "%s/%s", ctx->path, p->h->path); \
1026   } \
1027 } while(0)
1028 #else
1029 #define DO_SORT()       /* nothing */
1030 #endif
1031
1032 #if USE_HCACHE
1033   hc = mutt_hcache_open (HeaderCache, ctx->path, NULL);
1034 #endif
1035
1036   for (p = *md, count = 0; p; p = p->next, count++)
1037    {
1038     if (! (p && p->h && !p->header_parsed))
1039      {
1040       last = p;
1041       continue;
1042     }
1043
1044     if (!ctx->quiet && progress)
1045       mutt_progress_update (progress, count, -1);
1046
1047     DO_SORT();
1048
1049     snprintf (fn, sizeof (fn), "%s/%s", ctx->path, p->h->path);
1050
1051 #if USE_HCACHE
1052     if (option(OPTHCACHEVERIFY))
1053     {
1054        ret = stat(fn, &lastchanged);
1055     }
1056     else
1057     {
1058       lastchanged.st_mtime = 0;
1059       ret = 0;
1060     }
1061
1062     if (ctx->magic == M_MH)
1063       data = mutt_hcache_fetch (hc, p->h->path, strlen);
1064     else
1065       data = mutt_hcache_fetch (hc, p->h->path + 3, &maildir_hcache_keylen);
1066     when = (struct timeval *) data;
1067
1068     if (data != NULL && !ret && lastchanged.st_mtime <= when->tv_sec)
1069     {
1070       p->h = mutt_hcache_restore ((unsigned char *)data, &p->h);
1071       if (ctx->magic == M_MAILDIR)
1072         maildir_parse_flags (p->h, fn);
1073     }
1074     else
1075     {
1076 #endif /* USE_HCACHE */
1077
1078     if (maildir_parse_message (ctx->magic, fn, p->h->old, p->h))
1079     {
1080       p->header_parsed = 1;
1081 #if USE_HCACHE
1082       if (ctx->magic == M_MH)
1083         mutt_hcache_store (hc, p->h->path, p->h, 0, strlen);
1084       else
1085         mutt_hcache_store (hc, p->h->path + 3, p->h, 0, &maildir_hcache_keylen);
1086 #endif
1087     } else
1088       mutt_free_header (&p->h);
1089 #if USE_HCACHE
1090     }
1091     FREE (&data);
1092 #endif
1093     last = p;
1094    }
1095 #if USE_HCACHE
1096   mutt_hcache_close (hc);
1097 #endif
1098
1099 #undef DO_SORT
1100
1101   mh_sort_natural (ctx, md);
1102 }
1103
1104 static int mh_close_mailbox (CONTEXT *ctx)
1105 {
1106   FREE (&ctx->data);
1107
1108   return 0;
1109 }
1110
1111 /* Read a MH/maildir style mailbox.
1112  *
1113  * args:
1114  *      ctx [IN/OUT]    context for this mailbox
1115  *      subdir [IN]     NULL for MH mailboxes, otherwise the subdir of the
1116  *                      maildir mailbox to read from
1117  */
1118 int mh_read_dir (CONTEXT * ctx, const char *subdir)
1119 {
1120   struct maildir *md;
1121   struct mh_sequences mhs;
1122   struct maildir **last;
1123   struct mh_data *data;
1124   int count;
1125   char msgbuf[STRING];
1126   progress_t progress;
1127
1128   memset (&mhs, 0, sizeof (mhs));
1129   if (!ctx->quiet)
1130   {
1131     snprintf (msgbuf, sizeof (msgbuf), _("Scanning %s..."), ctx->path);
1132     mutt_progress_init (&progress, msgbuf, M_PROGRESS_MSG, ReadInc, 0);
1133   }
1134
1135   if (!ctx->data)
1136   {
1137     ctx->data = safe_calloc(sizeof (struct mh_data), 1);
1138     ctx->mx_close = mh_close_mailbox;
1139   }
1140   data = mh_data (ctx);
1141
1142   maildir_update_mtime (ctx);
1143
1144   md = NULL;
1145   last = &md;
1146   count = 0;
1147   if (maildir_parse_dir (ctx, &last, subdir, &count, &progress) == -1)
1148     return -1;
1149
1150   if (!ctx->quiet)
1151   {
1152     snprintf (msgbuf, sizeof (msgbuf), _("Reading %s..."), ctx->path);
1153     mutt_progress_init (&progress, msgbuf, M_PROGRESS_MSG, ReadInc, count);
1154   }
1155   maildir_delayed_parsing (ctx, &md, &progress);
1156
1157   if (ctx->magic == M_MH)
1158   {
1159     if (mh_read_sequences (&mhs, ctx->path) < 0)
1160       return -1;
1161     mh_update_maildir (md, &mhs);
1162     mhs_free_sequences (&mhs);
1163   }
1164
1165   maildir_move_to_context (ctx, &md);
1166
1167   if (!data->mh_umask)
1168     data->mh_umask = mh_umask (ctx);
1169
1170   return 0;
1171 }
1172
1173 /* read a maildir style mailbox */
1174 int maildir_read_dir (CONTEXT * ctx)
1175 {
1176   /* maildir looks sort of like MH, except that there are two subdirectories
1177    * of the main folder path from which to read messages
1178    */
1179   if (mh_read_dir (ctx, "new") == -1 || mh_read_dir (ctx, "cur") == -1)
1180     return (-1);
1181
1182   return 0;
1183 }
1184
1185 /*
1186  * Open a new (temporary) message in an MH folder.
1187  */
1188
1189 int mh_open_new_message (MESSAGE * msg, CONTEXT * dest, HEADER * hdr)
1190 {
1191   return mh_mkstemp (dest, &msg->fp, &msg->path);
1192 }
1193
1194 static int ch_compar (const void *a, const void *b)
1195 {
1196   return (int)( *((const char *) a) - *((const char *) b));
1197 }
1198
1199 static void maildir_flags (char *dest, size_t destlen, HEADER * hdr)
1200 {
1201   *dest = '\0';
1202
1203   /*
1204    * The maildir specification requires that all files in the cur
1205    * subdirectory have the :unique string appeneded, regardless of whether
1206    * or not there are any flags.  If .old is set, we know that this message
1207    * will end up in the cur directory, so we include it in the following
1208    * test even though there is no associated flag.
1209    */
1210   
1211   if (hdr && (hdr->flagged || hdr->replied || hdr->read || hdr->deleted || hdr->old || hdr->maildir_flags))
1212   {
1213     char tmp[LONG_STRING];
1214     snprintf (tmp, sizeof (tmp),
1215               "%s%s%s%s%s",
1216               hdr->flagged ? "F" : "",
1217               hdr->replied ? "R" : "",
1218               hdr->read ? "S" : "", hdr->deleted ? "T" : "",
1219               NONULL(hdr->maildir_flags));
1220     if (hdr->maildir_flags)
1221       qsort (tmp, strlen (tmp), 1, ch_compar);
1222     snprintf (dest, destlen, ":2,%s", tmp);
1223   }
1224 }
1225
1226
1227 /*
1228  * Open a new (temporary) message in a maildir folder.
1229  * 
1230  * Note that this uses _almost_ the maildir file name format, but
1231  * with a {cur,new} prefix.
1232  *
1233  */
1234
1235 int maildir_open_new_message (MESSAGE * msg, CONTEXT * dest, HEADER * hdr)
1236 {
1237   int fd;
1238   char path[_POSIX_PATH_MAX];
1239   char suffix[16];
1240   char subdir[16];
1241   mode_t omask;
1242
1243   if (hdr)
1244   {
1245     short deleted = hdr->deleted;
1246     hdr->deleted = 0;
1247
1248     maildir_flags (suffix, sizeof (suffix), hdr);
1249
1250     hdr->deleted = deleted;
1251   }
1252   else
1253     *suffix = '\0';
1254
1255   if (hdr && (hdr->read || hdr->old))
1256     strfcpy (subdir, "cur", sizeof (subdir));
1257   else
1258     strfcpy (subdir, "new", sizeof (subdir));
1259
1260   omask = umask (mh_umask (dest));
1261   FOREVER
1262   {
1263     snprintf (path, _POSIX_PATH_MAX, "%s/tmp/%s.%lld.%u_%d.%s%s",
1264               dest->path, subdir, (long long)time (NULL), (unsigned int)getpid (),
1265               Counter++, NONULL (Hostname), suffix);
1266
1267     dprint (2, (debugfile, "maildir_open_new_message (): Trying %s.\n",
1268                 path));
1269
1270     if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0666)) == -1)
1271     {
1272       if (errno != EEXIST)
1273       {
1274         umask (omask);
1275         mutt_perror (path);
1276         return -1;
1277       }
1278     }
1279     else
1280     {
1281       dprint (2, (debugfile, "maildir_open_new_message (): Success.\n"));
1282       msg->path = safe_strdup (path);
1283       break;
1284     }
1285   }
1286   umask (omask);
1287
1288   if ((msg->fp = fdopen (fd, "w")) == NULL)
1289   {
1290     FREE (&msg->path);
1291     close (fd);
1292     unlink (path);
1293     return (-1);
1294   }
1295
1296   return 0;
1297 }
1298
1299
1300
1301 /*
1302  * Commit a message to a maildir folder.
1303  * 
1304  * msg->path contains the file name of a file in tmp/. We take the
1305  * flags from this file's name. 
1306  *
1307  * ctx is the mail folder we commit to.
1308  * 
1309  * hdr is a header structure to which we write the message's new
1310  * file name.  This is used in the mh and maildir folder synch
1311  * routines.  When this routine is invoked from mx_commit_message,
1312  * hdr is NULL. 
1313  *
1314  * msg->path looks like this:
1315  * 
1316  *    tmp/{cur,new}.mutt-HOSTNAME-PID-COUNTER:flags
1317  * 
1318  * See also maildir_open_new_message().
1319  * 
1320  */
1321
1322 int maildir_commit_message (CONTEXT * ctx, MESSAGE * msg, HEADER * hdr)
1323 {
1324   char subdir[4];
1325   char suffix[16];
1326   char path[_POSIX_PATH_MAX];
1327   char full[_POSIX_PATH_MAX];
1328   char *s;
1329
1330   if (safe_fsync_close (&msg->fp))
1331   {
1332     mutt_perror (_("Could not flush message to disk"));
1333     return -1;
1334   }
1335
1336   /* extract the subdir */
1337   s = strrchr (msg->path, '/') + 1;
1338   strfcpy (subdir, s, 4);
1339
1340   /* extract the flags */
1341   if ((s = strchr (s, ':')))
1342     strfcpy (suffix, s, sizeof (suffix));
1343   else
1344     suffix[0] = '\0';
1345
1346   /* construct a new file name. */
1347   FOREVER
1348   {
1349     snprintf (path, _POSIX_PATH_MAX, "%s/%lld.%u_%d.%s%s", subdir,
1350               (long long)time (NULL), (unsigned int)getpid (), Counter++,
1351               NONULL (Hostname), suffix);
1352     snprintf (full, _POSIX_PATH_MAX, "%s/%s", ctx->path, path);
1353
1354     dprint (2, (debugfile, "maildir_commit_message (): renaming %s to %s.\n",
1355                 msg->path, full));
1356
1357     if (safe_rename (msg->path, full) == 0)
1358     {
1359       if (hdr)
1360         mutt_str_replace (&hdr->path, path);
1361       FREE (&msg->path);
1362
1363       /*
1364        * Adjust the mtime on the file to match the time at which this
1365        * message was received.  Currently this is only set when copying
1366        * messages between mailboxes, so we test to ensure that it is
1367        * actually set.
1368        */
1369       if (msg->received)
1370       {
1371         struct utimbuf ut;
1372
1373         ut.actime = msg->received;
1374         ut.modtime = msg->received;
1375         if (utime (full, &ut))
1376         {
1377           mutt_perror (_("maildir_commit_message(): unable to set time on file"));
1378           return -1;
1379         }
1380       }
1381
1382       return 0;
1383     }
1384     else if (errno != EEXIST)
1385     {
1386       mutt_perror (ctx->path);
1387       return -1;
1388     }
1389   }
1390 }
1391
1392 /* 
1393  * commit a message to an MH folder.
1394  * 
1395  */
1396
1397
1398 static int _mh_commit_message (CONTEXT * ctx, MESSAGE * msg, HEADER * hdr,
1399                                short updseq)
1400 {
1401   DIR *dirp;
1402   struct dirent *de;
1403   char *cp, *dep;
1404   unsigned int n, hi = 0;
1405   char path[_POSIX_PATH_MAX];
1406   char tmp[16];
1407
1408   if (safe_fsync_close (&msg->fp))
1409   {
1410     mutt_perror (_("Could not flush message to disk"));
1411     return -1;
1412   }
1413
1414   if ((dirp = opendir (ctx->path)) == NULL)
1415   {
1416     mutt_perror (ctx->path);
1417     return (-1);
1418   }
1419
1420   /* figure out what the next message number is */
1421   while ((de = readdir (dirp)) != NULL)
1422   {
1423     dep = de->d_name;
1424     if (*dep == ',')
1425       dep++;
1426     cp = dep;
1427     while (*cp)
1428     {
1429       if (!isdigit ((unsigned char) *cp))
1430         break;
1431       cp++;
1432     }
1433     if (!*cp)
1434     {
1435       n = atoi (dep);
1436       if (n > hi)
1437         hi = n;
1438     }
1439   }
1440   closedir (dirp);
1441
1442   /* 
1443    * Now try to rename the file to the proper name.
1444    * 
1445    * Note: We may have to try multiple times, until we find a free
1446    * slot.
1447    */
1448
1449   FOREVER
1450   {
1451     hi++;
1452     snprintf (tmp, sizeof (tmp), "%d", hi);
1453     snprintf (path, sizeof (path), "%s/%s", ctx->path, tmp);
1454     if (safe_rename (msg->path, path) == 0)
1455     {
1456       if (hdr)
1457         mutt_str_replace (&hdr->path, tmp);
1458       FREE (&msg->path);
1459       break;
1460     }
1461     else if (errno != EEXIST)
1462     {
1463       mutt_perror (ctx->path);
1464       return -1;
1465     }
1466   }
1467   if (updseq)
1468     mh_sequences_add_one (ctx, hi, !msg->flags.read, msg->flags.flagged,
1469                           msg->flags.replied);
1470   return 0;
1471 }
1472
1473 int mh_commit_message (CONTEXT * ctx, MESSAGE * msg, HEADER * hdr)
1474 {
1475   return _mh_commit_message (ctx, msg, hdr, 1);
1476 }
1477
1478
1479 /* Sync a message in an MH folder.
1480  * 
1481  * This code is also used for attachment deletion in maildir
1482  * folders.
1483  */
1484
1485 static int mh_rewrite_message (CONTEXT * ctx, int msgno)
1486 {
1487   HEADER *h = ctx->hdrs[msgno];
1488   MESSAGE *dest;
1489
1490   int rc;
1491   short restore = 1;
1492   char oldpath[_POSIX_PATH_MAX];
1493   char newpath[_POSIX_PATH_MAX];
1494   char partpath[_POSIX_PATH_MAX];
1495
1496   long old_body_offset = h->content->offset;
1497   long old_body_length = h->content->length;
1498   long old_hdr_lines = h->lines;
1499
1500   if ((dest = mx_open_new_message (ctx, h, 0)) == NULL)
1501     return -1;
1502
1503   if ((rc = mutt_copy_message (dest->fp, ctx, h,
1504                                M_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN)) == 0)
1505   {
1506     snprintf (oldpath, _POSIX_PATH_MAX, "%s/%s", ctx->path, h->path);
1507     strfcpy (partpath, h->path, _POSIX_PATH_MAX);
1508
1509     if (ctx->magic == M_MAILDIR)
1510       rc = maildir_commit_message (ctx, dest, h);
1511     else
1512       rc = _mh_commit_message (ctx, dest, h, 0);
1513
1514     mx_close_message (&dest);
1515
1516     if (rc == 0)
1517     {
1518       unlink (oldpath);
1519       restore = 0;
1520     }
1521
1522     /* 
1523      * Try to move the new message to the old place.
1524      * (MH only.)
1525      *
1526      * This is important when we are just updating flags.
1527      *
1528      * Note that there is a race condition against programs which
1529      * use the first free slot instead of the maximum message
1530      * number.  Mutt does _not_ behave like this.
1531      * 
1532      * Anyway, if this fails, the message is in the folder, so
1533      * all what happens is that a concurrently runnung mutt will
1534      * lose flag modifications.
1535      */
1536
1537     if (ctx->magic == M_MH && rc == 0)
1538     {
1539       snprintf (newpath, _POSIX_PATH_MAX, "%s/%s", ctx->path, h->path);
1540       if ((rc = safe_rename (newpath, oldpath)) == 0)
1541         mutt_str_replace (&h->path, partpath);
1542     }
1543   }
1544   else
1545     mx_close_message (&dest);
1546
1547   if (rc == -1 && restore)
1548   {
1549     h->content->offset = old_body_offset;
1550     h->content->length = old_body_length;
1551     h->lines = old_hdr_lines;
1552   }
1553
1554   mutt_free_body (&h->content->parts);
1555   return rc;
1556 }
1557
1558 static int mh_sync_message (CONTEXT * ctx, int msgno)
1559 {
1560   HEADER *h = ctx->hdrs[msgno];
1561
1562   if (h->attach_del || 
1563       (h->env && (h->env->refs_changed || h->env->irt_changed)))
1564     if (mh_rewrite_message (ctx, msgno) != 0)
1565       return -1;
1566
1567   return 0;
1568 }
1569
1570 static int maildir_sync_message (CONTEXT * ctx, int msgno)
1571 {
1572   HEADER *h = ctx->hdrs[msgno];
1573
1574   if (h->attach_del || 
1575       (h->env && (h->env->refs_changed || h->env->irt_changed)))
1576   {
1577     /* when doing attachment deletion/rethreading, fall back to the MH case. */
1578     if (mh_rewrite_message (ctx, msgno) != 0)
1579       return (-1);
1580   }
1581   else
1582   {
1583     /* we just have to rename the file. */
1584
1585     char newpath[_POSIX_PATH_MAX];
1586     char partpath[_POSIX_PATH_MAX];
1587     char fullpath[_POSIX_PATH_MAX];
1588     char oldpath[_POSIX_PATH_MAX];
1589     char suffix[16];
1590     char *p;
1591
1592     if ((p = strrchr (h->path, '/')) == NULL)
1593     {
1594       dprint (1,
1595               (debugfile,
1596                "maildir_sync_message: %s: unable to find subdir!\n",
1597                h->path));
1598       return (-1);
1599     }
1600     p++;
1601     strfcpy (newpath, p, sizeof (newpath));
1602
1603     /* kill the previous flags */
1604     if ((p = strchr (newpath, ':')) != NULL)
1605       *p = 0;
1606
1607     maildir_flags (suffix, sizeof (suffix), h);
1608
1609     snprintf (partpath, sizeof (partpath), "%s/%s%s",
1610               (h->read || h->old) ? "cur" : "new", newpath, suffix);
1611     snprintf (fullpath, sizeof (fullpath), "%s/%s", ctx->path, partpath);
1612     snprintf (oldpath, sizeof (oldpath), "%s/%s", ctx->path, h->path);
1613
1614     if (mutt_strcmp (fullpath, oldpath) == 0)
1615     {
1616       /* message hasn't really changed */
1617       return 0;
1618     }
1619
1620     /* record that the message is possibly marked as trashed on disk */
1621     h->trash = h->deleted;
1622
1623     if (rename (oldpath, fullpath) != 0)
1624     {
1625       mutt_perror ("rename");
1626       return (-1);
1627     }
1628     mutt_str_replace (&h->path, partpath);
1629   }
1630   return (0);
1631 }
1632
1633 int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
1634 {
1635   char path[_POSIX_PATH_MAX], tmp[_POSIX_PATH_MAX];
1636   int i, j;
1637 #if USE_HCACHE
1638   header_cache_t *hc = NULL;
1639 #endif /* USE_HCACHE */
1640   char msgbuf[STRING];
1641   progress_t progress;
1642
1643   if (ctx->magic == M_MH)
1644     i = mh_check_mailbox (ctx, index_hint);
1645   else 
1646     i = maildir_check_mailbox (ctx, index_hint);
1647       
1648   if (i != 0)
1649     return i;
1650
1651 #if USE_HCACHE
1652   if (ctx->magic == M_MAILDIR || ctx->magic == M_MH)
1653     hc = mutt_hcache_open(HeaderCache, ctx->path, NULL);
1654 #endif /* USE_HCACHE */
1655
1656   if (!ctx->quiet)
1657   {
1658     snprintf (msgbuf, sizeof (msgbuf), _("Writing %s..."), ctx->path);
1659     mutt_progress_init (&progress, msgbuf, M_PROGRESS_MSG, WriteInc, ctx->msgcount);
1660   }
1661
1662   for (i = 0; i < ctx->msgcount; i++)
1663   {
1664     if (!ctx->quiet)
1665       mutt_progress_update (&progress, i, -1);
1666
1667     if (ctx->hdrs[i]->deleted
1668         && (ctx->magic != M_MAILDIR || !option (OPTMAILDIRTRASH)))
1669     {
1670       snprintf (path, sizeof (path), "%s/%s", ctx->path, ctx->hdrs[i]->path);
1671       if (ctx->magic == M_MAILDIR
1672           || (option (OPTMHPURGE) && ctx->magic == M_MH))
1673       {
1674 #if USE_HCACHE
1675         if (ctx->magic == M_MAILDIR)
1676           mutt_hcache_delete (hc, ctx->hdrs[i]->path + 3, &maildir_hcache_keylen);
1677         else if (ctx->magic == M_MH)
1678           mutt_hcache_delete (hc, ctx->hdrs[i]->path, strlen);
1679 #endif /* USE_HCACHE */
1680         unlink (path);
1681       }
1682       else if (ctx->magic == M_MH)
1683       {
1684         /* MH just moves files out of the way when you delete them */
1685         if (*ctx->hdrs[i]->path != ',')
1686         {
1687           snprintf (tmp, sizeof (tmp), "%s/,%s", ctx->path,
1688                     ctx->hdrs[i]->path);
1689           unlink (tmp);
1690           rename (path, tmp);
1691         }
1692
1693       }
1694     }
1695     else if (ctx->hdrs[i]->changed || ctx->hdrs[i]->attach_del ||
1696              (ctx->magic == M_MAILDIR
1697               && (option (OPTMAILDIRTRASH) || ctx->hdrs[i]->trash)
1698               && (ctx->hdrs[i]->deleted != ctx->hdrs[i]->trash)))
1699     {
1700       if (ctx->magic == M_MAILDIR)
1701       {
1702         if (maildir_sync_message (ctx, i) == -1)
1703           goto err;
1704       }
1705       else
1706       {
1707         if (mh_sync_message (ctx, i) == -1)
1708           goto err;
1709       }
1710     }
1711
1712 #if USE_HCACHE
1713     if (ctx->hdrs[i]->changed)
1714     {
1715       if (ctx->magic == M_MAILDIR)
1716         mutt_hcache_store (hc, ctx->hdrs[i]->path + 3, ctx->hdrs[i],
1717                            0, &maildir_hcache_keylen);
1718       else if (ctx->magic == M_MH)
1719         mutt_hcache_store (hc, ctx->hdrs[i]->path, ctx->hdrs[i], 0, strlen);
1720     }
1721 #endif
1722
1723   }
1724
1725 #if USE_HCACHE
1726   if (ctx->magic == M_MAILDIR || ctx->magic == M_MH)
1727     mutt_hcache_close (hc);
1728 #endif /* USE_HCACHE */
1729
1730   if (ctx->magic == M_MH)
1731     mh_update_sequences (ctx);
1732
1733   /* XXX race condition? */
1734
1735   maildir_update_mtime (ctx);
1736
1737   /* adjust indices */
1738
1739   if (ctx->deleted)
1740   {
1741     for (i = 0, j = 0; i < ctx->msgcount; i++)
1742     {
1743       if (!ctx->hdrs[i]->deleted
1744           || (ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH)))
1745         ctx->hdrs[i]->index = j++;
1746     }
1747   }
1748
1749   return 0;
1750
1751 err:
1752 #if USE_HCACHE
1753   if (ctx->magic == M_MAILDIR || ctx->magic == M_MH)
1754     mutt_hcache_close (hc);
1755 #endif /* USE_HCACHE */
1756   return -1;
1757 }
1758
1759 static char *maildir_canon_filename (char *dest, const char *src, size_t l)
1760 {
1761   char *t, *u;
1762
1763   if ((t = strrchr (src, '/')))
1764     src = t + 1;
1765
1766   strfcpy (dest, src, l);
1767   if ((u = strrchr (dest, ':')))
1768     *u = '\0';
1769
1770   return dest;
1771 }
1772
1773 static void maildir_update_tables (CONTEXT *ctx, int *index_hint)
1774 {
1775   short old_sort;
1776   int old_count;
1777   int i, j;
1778   
1779   if (Sort != SORT_ORDER)
1780   {
1781     old_sort = Sort;
1782     Sort = SORT_ORDER;
1783     mutt_sort_headers (ctx, 1);
1784     Sort = old_sort;
1785   }
1786   
1787   old_count = ctx->msgcount;
1788   for (i = 0, j = 0; i < old_count; i++)
1789   {
1790     if (ctx->hdrs[i]->active && index_hint && *index_hint == i)
1791       *index_hint = j;
1792     
1793     if (ctx->hdrs[i]->active)
1794       ctx->hdrs[i]->index = j++;
1795   }
1796
1797   mx_update_tables (ctx, 0);
1798   mutt_clear_threads (ctx);
1799 }
1800
1801 static void maildir_update_flags (CONTEXT *ctx, HEADER *o, HEADER *n)
1802 {
1803   /* save the global state here so we can reset it at the
1804    * end of list block if required.
1805    */
1806   int context_changed = ctx->changed;
1807   
1808   /* user didn't modify this message.  alter the flags to match the
1809    * current state on disk.  This may not actually do
1810    * anything. mutt_set_flag() will just ignore the call if the status
1811    * bits are already properly set, but it is still faster not to pass
1812    * through it */
1813   if (o->flagged != n->flagged)
1814     mutt_set_flag (ctx, o, M_FLAG, n->flagged);
1815   if (o->replied != n->replied)
1816     mutt_set_flag (ctx, o, M_REPLIED, n->replied);
1817   if (o->read != n->read)
1818     mutt_set_flag (ctx, o, M_READ, n->read);
1819   if (o->old != n->old)
1820     mutt_set_flag (ctx, o, M_OLD, n->old);
1821
1822   /* mutt_set_flag() will set this, but we don't need to
1823    * sync the changes we made because we just updated the
1824    * context to match the current on-disk state of the
1825    * message.
1826    */
1827   o->changed = 0;
1828   
1829   /* if the mailbox was not modified before we made these
1830    * changes, unset the changed flag since nothing needs to
1831    * be synchronized.
1832    */
1833   if (!context_changed)
1834     ctx->changed = 0;
1835 }
1836
1837
1838 /* This function handles arrival of new mail and reopening of
1839  * maildir folders.  The basic idea here is we check to see if either
1840  * the new or cur subdirectories have changed, and if so, we scan them
1841  * for the list of files.  We check for newly added messages, and
1842  * then merge the flags messages we already knew about.  We don't treat
1843  * either subdirectory differently, as mail could be copied directly into
1844  * the cur directory from another agent.
1845  */
1846 int maildir_check_mailbox (CONTEXT * ctx, int *index_hint)
1847 {
1848   struct stat st_new;           /* status of the "new" subdirectory */
1849   struct stat st_cur;           /* status of the "cur" subdirectory */
1850   char buf[_POSIX_PATH_MAX];
1851   int changed = 0;              /* bitmask representing which subdirectories
1852                                    have changed.  0x1 = new, 0x2 = cur */
1853   int occult = 0;               /* messages were removed from the mailbox */
1854   int have_new = 0;             /* messages were added to the mailbox */
1855   struct maildir *md;           /* list of messages in the mailbox */
1856   struct maildir **last, *p;
1857   int i;
1858   HASH *fnames;                 /* hash table for quickly looking up the base filename
1859                                    for a maildir message */
1860   struct mh_data *data = mh_data (ctx);
1861
1862   /* XXX seems like this check belongs in mx_check_mailbox()
1863    * rather than here.
1864    */
1865   if (!option (OPTCHECKNEW))
1866     return 0;
1867
1868   snprintf (buf, sizeof (buf), "%s/new", ctx->path);
1869   if (stat (buf, &st_new) == -1)
1870     return -1;
1871
1872   snprintf (buf, sizeof (buf), "%s/cur", ctx->path);
1873   if (stat (buf, &st_cur) == -1)
1874     return -1;
1875
1876   /* determine which subdirectories need to be scanned */
1877   if (st_new.st_mtime > ctx->mtime)
1878     changed = 1;
1879   if (st_cur.st_mtime > data->mtime_cur)
1880     changed |= 2;
1881
1882   if (!changed)
1883     return 0;                   /* nothing to do */
1884
1885   /* update the modification times on the mailbox */
1886   data->mtime_cur = st_cur.st_mtime;
1887   ctx->mtime = st_new.st_mtime;
1888
1889   /* do a fast scan of just the filenames in
1890    * the subdirectories that have changed.
1891    */
1892   md = NULL;
1893   last = &md;
1894   if (changed & 1)
1895     maildir_parse_dir (ctx, &last, "new", NULL, NULL);
1896   if (changed & 2)
1897     maildir_parse_dir (ctx, &last, "cur", NULL, NULL);
1898
1899   /* we create a hash table keyed off the canonical (sans flags) filename
1900    * of each message we scanned.  This is used in the loop over the
1901    * existing messages below to do some correlation.
1902    */
1903   fnames = hash_create (1031, 0);
1904
1905   for (p = md; p; p = p->next)
1906   {
1907     maildir_canon_filename (buf, p->h->path, sizeof (buf));
1908     p->canon_fname = safe_strdup (buf);
1909     hash_insert (fnames, p->canon_fname, p, 0);
1910   }
1911
1912   /* check for modifications and adjust flags */
1913   for (i = 0; i < ctx->msgcount; i++)
1914   {
1915     ctx->hdrs[i]->active = 0;
1916     maildir_canon_filename (buf, ctx->hdrs[i]->path, sizeof (buf));
1917     p = hash_find (fnames, buf);
1918     if (p && p->h)
1919     {
1920       /* message already exists, merge flags */
1921       ctx->hdrs[i]->active = 1;
1922
1923       /* check to see if the message has moved to a different
1924        * subdirectory.  If so, update the associated filename.
1925        */
1926       if (mutt_strcmp (ctx->hdrs[i]->path, p->h->path))
1927         mutt_str_replace (&ctx->hdrs[i]->path, p->h->path);
1928
1929       /* if the user hasn't modified the flags on this message, update
1930        * the flags we just detected.
1931        */
1932       if (!ctx->hdrs[i]->changed)
1933         maildir_update_flags (ctx, ctx->hdrs[i], p->h);
1934
1935       if (ctx->hdrs[i]->deleted == ctx->hdrs[i]->trash)
1936         ctx->hdrs[i]->deleted = p->h->deleted;
1937       ctx->hdrs[i]->trash = p->h->trash;
1938
1939       /* this is a duplicate of an existing header, so remove it */
1940       mutt_free_header (&p->h);
1941     }
1942     /* This message was not in the list of messages we just scanned.
1943      * Check to see if we have enough information to know if the
1944      * message has disappeared out from underneath us.
1945      */
1946     else if (((changed & 1) && (!strncmp (ctx->hdrs[i]->path, "new/", 4))) ||
1947              ((changed & 2) && (!strncmp (ctx->hdrs[i]->path, "cur/", 4))))
1948     {
1949       /* This message disappeared, so we need to simulate a "reopen"
1950        * event.  We know it disappeared because we just scanned the
1951        * subdirectory it used to reside in.
1952        */
1953       occult = 1;
1954     }
1955     else
1956     {
1957       /* This message resides in a subdirectory which was not
1958        * modified, so we assume that it is still present and
1959        * unchanged.
1960        */
1961       ctx->hdrs[i]->active = 1;
1962     }
1963   }
1964
1965   /* destroy the file name hash */
1966   hash_destroy (&fnames, NULL);
1967
1968   /* If we didn't just get new mail, update the tables. */
1969   if (occult)
1970     maildir_update_tables (ctx, index_hint);
1971   
1972   /* do any delayed parsing we need to do. */
1973   maildir_delayed_parsing (ctx, &md, NULL);
1974
1975   /* Incorporate new messages */
1976   have_new = maildir_move_to_context (ctx, &md);
1977
1978   return occult ? M_REOPENED : (have_new ? M_NEW_MAIL : 0);
1979 }
1980
1981 /* 
1982  * This function handles arrival of new mail and reopening of
1983  * mh/maildir folders. Things are getting rather complex because we
1984  * don't have a well-defined "mailbox order", so the tricks from
1985  * mbox.c and mx.c won't work here.
1986  *
1987  * Don't change this code unless you _really_ understand what
1988  * happens.
1989  *
1990  */
1991
1992 int mh_check_mailbox (CONTEXT * ctx, int *index_hint)
1993 {
1994   char buf[_POSIX_PATH_MAX];
1995   struct stat st, st_cur;
1996   short modified = 0, have_new = 0, occult = 0;
1997   struct maildir *md, *p;
1998   struct maildir **last = NULL;
1999   struct mh_sequences mhs;
2000   HASH *fnames;
2001   int i;
2002   struct mh_data *data = mh_data (ctx);
2003
2004   if (!option (OPTCHECKNEW))
2005     return 0;
2006
2007   strfcpy (buf, ctx->path, sizeof (buf));
2008   if (stat (buf, &st) == -1)
2009     return -1;
2010   
2011   /* create .mh_sequences when there isn't one. */
2012   snprintf (buf, sizeof (buf), "%s/.mh_sequences", ctx->path);
2013   if ((i = stat (buf, &st_cur)) == -1 && errno == ENOENT)
2014   {
2015     char *tmp;
2016     FILE *fp = NULL;
2017     
2018     if (mh_mkstemp (ctx, &fp, &tmp) == 0)
2019     {
2020       safe_fclose (&fp);
2021       if (safe_rename (tmp, buf) == -1)
2022         unlink (tmp);
2023       FREE (&tmp);
2024     }
2025   }
2026
2027   if (i == -1 && stat (buf, &st_cur) == -1)
2028     modified = 1;
2029
2030   if (st.st_mtime > ctx->mtime || st_cur.st_mtime > data->mtime_cur)
2031     modified = 1;
2032
2033   if (!modified)
2034     return 0;
2035
2036   data->mtime_cur = st_cur.st_mtime;
2037   ctx->mtime = st.st_mtime;
2038
2039   memset (&mhs, 0, sizeof (mhs));
2040
2041   md   = NULL;
2042   last = &md;
2043
2044   maildir_parse_dir (ctx, &last, NULL, NULL, NULL);
2045   maildir_delayed_parsing (ctx, &md, NULL);
2046
2047   if (mh_read_sequences (&mhs, ctx->path) < 0)
2048     return -1;
2049   mh_update_maildir (md, &mhs);
2050   mhs_free_sequences (&mhs);
2051
2052   /* check for modifications and adjust flags */
2053   fnames = hash_create (1031, 0);
2054
2055   for (p = md; p; p = p->next)
2056     hash_insert (fnames, p->h->path, p, 0);
2057
2058   for (i = 0; i < ctx->msgcount; i++)
2059   {
2060     ctx->hdrs[i]->active = 0;
2061
2062     if ((p = hash_find (fnames, ctx->hdrs[i]->path)) && p->h &&
2063         (mbox_strict_cmp_headers (ctx->hdrs[i], p->h)))
2064     {
2065       ctx->hdrs[i]->active = 1;
2066       /* found the right message */
2067       if (!ctx->hdrs[i]->changed)
2068         maildir_update_flags (ctx, ctx->hdrs[i], p->h);
2069
2070       mutt_free_header (&p->h);
2071     }
2072     else /* message has disappeared */
2073       occult = 1;
2074   }
2075
2076   /* destroy the file name hash */
2077
2078   hash_destroy (&fnames, NULL);
2079
2080   /* If we didn't just get new mail, update the tables. */
2081   if (occult)
2082     maildir_update_tables (ctx, index_hint);
2083
2084   /* Incorporate new messages */
2085   have_new = maildir_move_to_context (ctx, &md);
2086
2087   return occult ? M_REOPENED : (have_new ? M_NEW_MAIL : 0);
2088 }
2089
2090
2091
2092
2093 /*
2094  * These functions try to find a message in a maildir folder when it
2095  * has moved under our feet.  Note that this code is rather expensive, but
2096  * then again, it's called rarely.
2097  */
2098
2099 static FILE *_maildir_open_find_message (const char *folder, const char *unique,
2100                                   const char *subfolder)
2101 {
2102   char dir[_POSIX_PATH_MAX];
2103   char tunique[_POSIX_PATH_MAX];
2104   char fname[_POSIX_PATH_MAX];
2105
2106   DIR *dp;
2107   struct dirent *de;
2108
2109   FILE *fp = NULL;
2110   int oe = ENOENT;
2111
2112   snprintf (dir, sizeof (dir), "%s/%s", folder, subfolder);
2113
2114   if ((dp = opendir (dir)) == NULL)
2115   {
2116     errno = ENOENT;
2117     return NULL;
2118   }
2119
2120   while ((de = readdir (dp)))
2121   {
2122     maildir_canon_filename (tunique, de->d_name, sizeof (tunique));
2123
2124     if (!mutt_strcmp (tunique, unique))
2125     {
2126       snprintf (fname, sizeof (fname), "%s/%s/%s", folder, subfolder,
2127                 de->d_name);
2128       fp = fopen (fname, "r");  /* __FOPEN_CHECKED__ */
2129       oe = errno;
2130       break;
2131     }
2132   }
2133
2134   closedir (dp);
2135
2136   errno = oe;
2137   return fp;
2138 }
2139
2140 FILE *maildir_open_find_message (const char *folder, const char *msg)
2141 {
2142   char unique[_POSIX_PATH_MAX];
2143   FILE *fp;
2144
2145   static unsigned int new_hits = 0, cur_hits = 0;       /* simple dynamic optimization */
2146
2147   maildir_canon_filename (unique, msg, sizeof (unique));
2148
2149   if (
2150       (fp =
2151        _maildir_open_find_message (folder, unique,
2152                                    new_hits > cur_hits ? "new" : "cur"))
2153       || errno != ENOENT)
2154   {
2155     if (new_hits < UINT_MAX && cur_hits < UINT_MAX)
2156     {
2157       new_hits += (new_hits > cur_hits ? 1 : 0);
2158       cur_hits += (new_hits > cur_hits ? 0 : 1);
2159     }
2160
2161     return fp;
2162   }
2163   if (
2164       (fp =
2165        _maildir_open_find_message (folder, unique,
2166                                    new_hits > cur_hits ? "cur" : "new"))
2167       || errno != ENOENT)
2168   {
2169     if (new_hits < UINT_MAX && cur_hits < UINT_MAX)
2170     {
2171       new_hits += (new_hits > cur_hits ? 0 : 1);
2172       cur_hits += (new_hits > cur_hits ? 1 : 0);
2173     }
2174
2175     return fp;
2176   }
2177
2178   return NULL;
2179 }
2180
2181
2182 /*
2183  * Returns:
2184  * 1 if there are no messages in the mailbox
2185  * 0 if there are messages in the mailbox
2186  * -1 on error
2187  */
2188 int maildir_check_empty (const char *path)
2189 {
2190   DIR *dp;
2191   struct dirent *de;
2192   int r = 1; /* assume empty until we find a message */
2193   char realpath[_POSIX_PATH_MAX];
2194   int iter = 0;
2195
2196   /* Strategy here is to look for any file not beginning with a period */
2197
2198   do {
2199     /* we do "cur" on the first iteration since its more likely that we'll
2200      * find old messages without having to scan both subdirs
2201      */
2202     snprintf (realpath, sizeof (realpath), "%s/%s", path,
2203               iter == 0 ? "cur" : "new");
2204     if ((dp = opendir (realpath)) == NULL)
2205       return -1;
2206     while ((de = readdir (dp)))
2207     {
2208       if (*de->d_name != '.')
2209       {
2210         r = 0;
2211         break;
2212       }
2213     }
2214     closedir (dp);
2215     iter++;
2216   } while (r && iter < 2);
2217
2218   return r;
2219 }
2220
2221 /*
2222  * Returns:
2223  * 1 if there are no messages in the mailbox
2224  * 0 if there are messages in the mailbox
2225  * -1 on error
2226  */
2227 int mh_check_empty (const char *path)
2228 {
2229   DIR *dp;
2230   struct dirent *de;
2231   int r = 1; /* assume empty until we find a message */
2232   
2233   if ((dp = opendir (path)) == NULL)
2234     return -1;
2235   while ((de = readdir (dp)))
2236   {
2237     if (mh_valid_message (de->d_name))
2238     {
2239       r = 0;
2240       break;
2241     }
2242   }
2243   closedir (dp);
2244   
2245   return r;
2246 }
2247
2248 int mx_is_maildir (const char *path)
2249 {
2250   char tmp[_POSIX_PATH_MAX];
2251   struct stat st;
2252
2253   snprintf (tmp, sizeof (tmp), "%s/cur", path);
2254   if (stat (tmp, &st) == 0 && S_ISDIR (st.st_mode))
2255     return 1;
2256   return 0;
2257 }
2258
2259 int mx_is_mh (const char *path)
2260 {
2261   char tmp[_POSIX_PATH_MAX];
2262
2263   snprintf (tmp, sizeof (tmp), "%s/.mh_sequences", path);
2264   if (access (tmp, F_OK) == 0)
2265     return 1;
2266
2267   snprintf (tmp, sizeof (tmp), "%s/.xmhcache", path);
2268   if (access (tmp, F_OK) == 0)
2269     return 1;
2270
2271   snprintf (tmp, sizeof (tmp), "%s/.mew_cache", path);
2272   if (access (tmp, F_OK) == 0)
2273     return 1;
2274
2275   snprintf (tmp, sizeof (tmp), "%s/.mew-cache", path);
2276   if (access (tmp, F_OK) == 0)
2277     return 1;
2278
2279   snprintf (tmp, sizeof (tmp), "%s/.sylpheed_cache", path);
2280   if (access (tmp, F_OK) == 0)
2281     return 1;
2282
2283   /*
2284    * ok, this isn't an mh folder, but mh mode can be used to read
2285    * Usenet news from the spool. ;-)
2286    */
2287
2288   snprintf (tmp, sizeof (tmp), "%s/.overview", path);
2289   if (access (tmp, F_OK) == 0)
2290     return 1;
2291
2292   return 0;
2293 }