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