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