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