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