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