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