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