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