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     snprintf (fn, sizeof (fn), "%s/%s", ctx->path, p->h->path);
915
916 #if USE_HCACHE
917     if (option(OPTHCACHEVERIFY)) {
918       ret = stat(fn, &lastchanged);
919     } else {
920       lastchanged.st_mtime = 0;
921       ret = 0;
922     }
923
924     if (data != NULL && !ret && lastchanged.st_mtime <= when->tv_sec)
925     {
926       p->h = mutt_hcache_restore ((unsigned char *)data, &p->h);
927       maildir_parse_flags (p->h, fn);
928     } else
929 #endif
930     if (maildir_parse_message (ctx->magic, fn, p->h->old, p->h))
931     {
932       p->header_parsed = 1;
933       maildir_parse_flags (p->h, fn);
934 #if USE_HCACHE
935       mutt_hcache_store (hc, p->h->path + 3, p->h, 0, &maildir_hcache_keylen);
936 #endif
937     } else
938       mutt_free_header (&p->h);
939 #if USE_HCACHE
940     FREE(&data);
941 #endif
942   }
943 #if USE_HCACHE
944   mutt_hcache_close (hc);
945 #endif
946 }
947
948 /* Read a MH/maildir style mailbox.
949  *
950  * args:
951  *      ctx [IN/OUT]    context for this mailbox
952  *      subdir [IN]     NULL for MH mailboxes, otherwise the subdir of the
953  *                      maildir mailbox to read from
954  */
955 int mh_read_dir (CONTEXT * ctx, const char *subdir)
956 {
957   struct maildir *md;
958   struct mh_sequences mhs;
959   struct maildir **last;
960   int count;
961
962
963   memset (&mhs, 0, sizeof (mhs));
964
965   maildir_update_mtime (ctx);
966
967   md = NULL;
968   last = &md;
969   count = 0;
970   if (maildir_parse_dir (ctx, &last, subdir, &count) == -1)
971     return -1;
972
973   if (ctx->magic == M_MH)
974   {
975     mh_read_sequences (&mhs, ctx->path);
976     mh_update_maildir (md, &mhs);
977     mhs_free_sequences (&mhs);
978   }
979
980   md = maildir_sort_inode(md);
981
982   if (ctx->magic == M_MAILDIR)
983     maildir_delayed_parsing (ctx, md);
984
985   maildir_move_to_context (ctx, &md);
986   return 0;
987 }
988
989 /* read a maildir style mailbox */
990 int maildir_read_dir (CONTEXT * ctx)
991 {
992   /* maildir looks sort of like MH, except that there are two subdirectories
993    * of the main folder path from which to read messages
994    */
995   if (mh_read_dir (ctx, "new") == -1 || mh_read_dir (ctx, "cur") == -1)
996     return (-1);
997
998   return 0;
999 }
1000
1001 /*
1002  * Open a new (temporary) message in an MH folder.
1003  */
1004
1005 int mh_open_new_message (MESSAGE * msg, CONTEXT * dest, HEADER * hdr)
1006 {
1007   return mh_mkstemp (dest, &msg->fp, &msg->path);
1008 }
1009
1010 int ch_compar (const void *a, const void *b)
1011 {
1012   return (int)( *((const char *) a) - *((const char *) b));
1013 }
1014
1015 static void maildir_flags (char *dest, size_t destlen, HEADER * hdr)
1016 {
1017   *dest = '\0';
1018
1019   /*
1020    * The maildir specification requires that all files in the cur
1021    * subdirectory have the :unique string appeneded, regardless of whether
1022    * or not there are any flags.  If .old is set, we know that this message
1023    * will end up in the cur directory, so we include it in the following
1024    * test even though there is no associated flag.
1025    */
1026   
1027   if (hdr && (hdr->flagged || hdr->replied || hdr->read || hdr->deleted || hdr->old || hdr->maildir_flags))
1028   {
1029     char tmp[LONG_STRING];
1030     snprintf (tmp, sizeof (tmp),
1031               "%s%s%s%s%s",
1032               hdr->flagged ? "F" : "",
1033               hdr->replied ? "R" : "",
1034               hdr->read ? "S" : "", hdr->deleted ? "T" : "",
1035               NONULL(hdr->maildir_flags));
1036     if (hdr->maildir_flags)
1037       qsort (tmp, strlen (tmp), 1, ch_compar);
1038     snprintf (dest, destlen, ":2,%s", tmp);
1039   }
1040 }
1041
1042
1043 /*
1044  * Open a new (temporary) message in a maildir folder.
1045  * 
1046  * Note that this uses _almost_ the maildir file name format, but
1047  * with a {cur,new} prefix.
1048  *
1049  */
1050
1051 int maildir_open_new_message (MESSAGE * msg, CONTEXT * dest, HEADER * hdr)
1052 {
1053   int fd;
1054   char path[_POSIX_PATH_MAX];
1055   char suffix[16];
1056   char subdir[16];
1057
1058   if (hdr)
1059   {
1060     short deleted = hdr->deleted;
1061     hdr->deleted = 0;
1062
1063     maildir_flags (suffix, sizeof (suffix), hdr);
1064
1065     hdr->deleted = deleted;
1066   }
1067   else
1068     *suffix = '\0';
1069
1070   if (hdr && (hdr->read || hdr->old))
1071     strfcpy (subdir, "cur", sizeof (subdir));
1072   else
1073     strfcpy (subdir, "new", sizeof (subdir));
1074
1075   FOREVER
1076   {
1077     snprintf (path, _POSIX_PATH_MAX, "%s/tmp/%s.%ld.%d_%d.%s%s",
1078               dest->path, subdir, time (NULL), getpid (), Counter++,
1079               NONULL (Hostname), suffix);
1080
1081     dprint (2, (debugfile, "maildir_open_new_message (): Trying %s.\n",
1082                 path));
1083
1084     umask(Umask);
1085     if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0666)) == -1)
1086     {
1087       if (errno != EEXIST)
1088       {
1089         mutt_perror (path);
1090         return -1;
1091       }
1092     }
1093     else
1094     {
1095       dprint (2, (debugfile, "maildir_open_new_message (): Success.\n"));
1096       msg->path = safe_strdup (path);
1097       break;
1098     }
1099   }
1100
1101   if ((msg->fp = fdopen (fd, "w")) == NULL)
1102   {
1103     FREE (&msg->path);
1104     close (fd);
1105     unlink (path);
1106     return (-1);
1107   }
1108
1109   return 0;
1110 }
1111
1112
1113
1114 /*
1115  * Commit a message to a maildir folder.
1116  * 
1117  * msg->path contains the file name of a file in tmp/. We take the
1118  * flags from this file's name. 
1119  *
1120  * ctx is the mail folder we commit to.
1121  * 
1122  * hdr is a header structure to which we write the message's new
1123  * file name.  This is used in the mh and maildir folder synch
1124  * routines.  When this routine is invoked from mx_commit_message,
1125  * hdr is NULL. 
1126  *
1127  * msg->path looks like this:
1128  * 
1129  *    tmp/{cur,new}.mutt-HOSTNAME-PID-COUNTER:flags
1130  * 
1131  * See also maildir_open_new_message().
1132  * 
1133  */
1134
1135 int maildir_commit_message (CONTEXT * ctx, MESSAGE * msg, HEADER * hdr)
1136 {
1137   char subdir[4];
1138   char suffix[16];
1139   char path[_POSIX_PATH_MAX];
1140   char full[_POSIX_PATH_MAX];
1141   char *s;
1142
1143   if (safe_fclose (&msg->fp) != 0)
1144     return -1;
1145
1146   /* extract the subdir */
1147   s = strrchr (msg->path, '/') + 1;
1148   strfcpy (subdir, s, 4);
1149
1150   /* extract the flags */
1151   if ((s = strchr (s, ':')))
1152     strfcpy (suffix, s, sizeof (suffix));
1153   else
1154     suffix[0] = '\0';
1155
1156   /* construct a new file name. */
1157   FOREVER
1158   {
1159     snprintf (path, _POSIX_PATH_MAX, "%s/%ld.%d_%d.%s%s", subdir,
1160               time (NULL), getpid (), Counter++, NONULL (Hostname), suffix);
1161     snprintf (full, _POSIX_PATH_MAX, "%s/%s", ctx->path, path);
1162
1163     dprint (2, (debugfile, "maildir_commit_message (): renaming %s to %s.\n",
1164                 msg->path, full));
1165
1166     if (safe_rename (msg->path, full) == 0)
1167     {
1168       if (hdr)
1169         mutt_str_replace (&hdr->path, path);
1170       FREE (&msg->path);
1171
1172       /*
1173        * Adjust the mtime on the file to match the time at which this
1174        * message was received.  Currently this is only set when copying
1175        * messages between mailboxes, so we test to ensure that it is
1176        * actually set.
1177        */
1178       if (msg->received)
1179       {
1180         struct utimbuf ut;
1181
1182         ut.actime = msg->received;
1183         ut.modtime = msg->received;
1184         if (utime (full, &ut))
1185         {
1186           mutt_perror (_("maildir_commit_message(): unable to set time on file"));
1187           return -1;
1188         }
1189       }
1190
1191       return 0;
1192     }
1193     else if (errno != EEXIST)
1194     {
1195       mutt_perror (ctx->path);
1196       return -1;
1197     }
1198   }
1199 }
1200
1201 /* 
1202  * commit a message to an MH folder.
1203  * 
1204  */
1205
1206
1207 static int _mh_commit_message (CONTEXT * ctx, MESSAGE * msg, HEADER * hdr,
1208                                short updseq)
1209 {
1210   DIR *dirp;
1211   struct dirent *de;
1212   char *cp, *dep;
1213   unsigned int n, hi = 0;
1214   char path[_POSIX_PATH_MAX];
1215   char tmp[16];
1216
1217   if (safe_fclose (&msg->fp) != 0)
1218     return -1;
1219
1220   if ((dirp = opendir (ctx->path)) == NULL)
1221   {
1222     mutt_perror (ctx->path);
1223     return (-1);
1224   }
1225
1226   /* figure out what the next message number is */
1227   while ((de = readdir (dirp)) != NULL)
1228   {
1229     dep = de->d_name;
1230     if (*dep == ',')
1231       dep++;
1232     cp = dep;
1233     while (*cp)
1234     {
1235       if (!isdigit ((unsigned char) *cp))
1236         break;
1237       cp++;
1238     }
1239     if (!*cp)
1240     {
1241       n = atoi (dep);
1242       if (n > hi)
1243         hi = n;
1244     }
1245   }
1246   closedir (dirp);
1247
1248   /* 
1249    * Now try to rename the file to the proper name.
1250    * 
1251    * Note: We may have to try multiple times, until we find a free
1252    * slot.
1253    */
1254
1255   FOREVER
1256   {
1257     hi++;
1258     snprintf (tmp, sizeof (tmp), "%d", hi);
1259     snprintf (path, sizeof (path), "%s/%s", ctx->path, tmp);
1260     if (safe_rename (msg->path, path) == 0)
1261     {
1262       if (hdr)
1263         mutt_str_replace (&hdr->path, tmp);
1264       FREE (&msg->path);
1265       break;
1266     }
1267     else if (errno != EEXIST)
1268     {
1269       mutt_perror (ctx->path);
1270       return -1;
1271     }
1272   }
1273   if (updseq)
1274     mh_sequences_add_one (ctx, hi, !msg->flags.read, msg->flags.flagged,
1275                           msg->flags.replied);
1276   return 0;
1277 }
1278
1279 int mh_commit_message (CONTEXT * ctx, MESSAGE * msg, HEADER * hdr)
1280 {
1281   return _mh_commit_message (ctx, msg, hdr, 1);
1282 }
1283
1284
1285 /* Sync a message in an MH folder.
1286  * 
1287  * This code is also used for attachment deletion in maildir
1288  * folders.
1289  */
1290
1291 static int mh_rewrite_message (CONTEXT * ctx, int msgno)
1292 {
1293   HEADER *h = ctx->hdrs[msgno];
1294   MESSAGE *dest;
1295
1296   int rc;
1297   short restore = 1;
1298   char oldpath[_POSIX_PATH_MAX];
1299   char newpath[_POSIX_PATH_MAX];
1300   char partpath[_POSIX_PATH_MAX];
1301
1302   long old_body_offset = h->content->offset;
1303   long old_body_length = h->content->length;
1304   long old_hdr_lines = h->lines;
1305
1306   if ((dest = mx_open_new_message (ctx, h, 0)) == NULL)
1307     return -1;
1308
1309   if ((rc = mutt_copy_message (dest->fp, ctx, h,
1310                                M_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN)) == 0)
1311   {
1312     snprintf (oldpath, _POSIX_PATH_MAX, "%s/%s", ctx->path, h->path);
1313     strfcpy (partpath, h->path, _POSIX_PATH_MAX);
1314
1315     if (ctx->magic == M_MAILDIR)
1316       rc = maildir_commit_message (ctx, dest, h);
1317     else
1318       rc = _mh_commit_message (ctx, dest, h, 0);
1319
1320     mx_close_message (&dest);
1321
1322     if (rc == 0)
1323     {
1324       unlink (oldpath);
1325       restore = 0;
1326     }
1327
1328     /* 
1329      * Try to move the new message to the old place.
1330      * (MH only.)
1331      *
1332      * This is important when we are just updating flags.
1333      *
1334      * Note that there is a race condition against programs which
1335      * use the first free slot instead of the maximum message
1336      * number.  Mutt does _not_ behave like this.
1337      * 
1338      * Anyway, if this fails, the message is in the folder, so
1339      * all what happens is that a concurrently runnung mutt will
1340      * lose flag modifications.
1341      */
1342
1343     if (ctx->magic == M_MH && rc == 0)
1344     {
1345       snprintf (newpath, _POSIX_PATH_MAX, "%s/%s", ctx->path, h->path);
1346       if ((rc = safe_rename (newpath, oldpath)) == 0)
1347         mutt_str_replace (&h->path, partpath);
1348     }
1349   }
1350   else
1351     mx_close_message (&dest);
1352
1353   if (rc == -1 && restore)
1354   {
1355     h->content->offset = old_body_offset;
1356     h->content->length = old_body_length;
1357     h->lines = old_hdr_lines;
1358   }
1359
1360   mutt_free_body (&h->content->parts);
1361   return rc;
1362 }
1363
1364 static int mh_sync_message (CONTEXT * ctx, int msgno)
1365 {
1366   HEADER *h = ctx->hdrs[msgno];
1367
1368   if (h->attach_del || h->refs_changed || h->irt_changed)
1369     if (mh_rewrite_message (ctx, msgno) != 0)
1370       return -1;
1371
1372   return 0;
1373 }
1374
1375 static int maildir_sync_message (CONTEXT * ctx, int msgno)
1376 {
1377   HEADER *h = ctx->hdrs[msgno];
1378
1379   if (h->attach_del || h->refs_changed || h->irt_changed)
1380   {
1381     /* when doing attachment deletion/rethreading, fall back to the MH case. */
1382     if (mh_rewrite_message (ctx, msgno) != 0)
1383       return (-1);
1384   }
1385   else
1386   {
1387     /* we just have to rename the file. */
1388
1389     char newpath[_POSIX_PATH_MAX];
1390     char partpath[_POSIX_PATH_MAX];
1391     char fullpath[_POSIX_PATH_MAX];
1392     char oldpath[_POSIX_PATH_MAX];
1393     char suffix[16];
1394     char *p;
1395
1396     if ((p = strrchr (h->path, '/')) == NULL)
1397     {
1398       dprint (1,
1399               (debugfile,
1400                "maildir_sync_message: %s: unable to find subdir!\n",
1401                h->path));
1402       return (-1);
1403     }
1404     p++;
1405     strfcpy (newpath, p, sizeof (newpath));
1406
1407     /* kill the previous flags */
1408     if ((p = strchr (newpath, ':')) != NULL)
1409       *p = 0;
1410
1411     maildir_flags (suffix, sizeof (suffix), h);
1412
1413     snprintf (partpath, sizeof (partpath), "%s/%s%s",
1414               (h->read || h->old) ? "cur" : "new", newpath, suffix);
1415     snprintf (fullpath, sizeof (fullpath), "%s/%s", ctx->path, partpath);
1416     snprintf (oldpath, sizeof (oldpath), "%s/%s", ctx->path, h->path);
1417
1418     if (mutt_strcmp (fullpath, oldpath) == 0)
1419     {
1420       /* message hasn't really changed */
1421       return 0;
1422     }
1423
1424     /* record that the message is possibly marked as trashed on disk */
1425     h->trash = h->deleted;
1426
1427     if (rename (oldpath, fullpath) != 0)
1428     {
1429       mutt_perror ("rename");
1430       return (-1);
1431     }
1432     mutt_str_replace (&h->path, partpath);
1433   }
1434   return (0);
1435 }
1436
1437 int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
1438 {
1439   char path[_POSIX_PATH_MAX], tmp[_POSIX_PATH_MAX];
1440   int i, j;
1441 #if USE_HCACHE
1442   void *hc = NULL;
1443 #endif /* USE_HCACHE */
1444
1445   if (ctx->magic == M_MH)
1446     i = mh_check_mailbox (ctx, index_hint);
1447   else 
1448     i = maildir_check_mailbox (ctx, index_hint);
1449       
1450   if (i != 0)
1451     return i;
1452
1453 #if USE_HCACHE
1454   if (ctx->magic == M_MAILDIR)
1455     hc = mutt_hcache_open(HeaderCache, ctx->path);
1456 #endif /* USE_HCACHE */
1457
1458   for (i = 0; i < ctx->msgcount; i++)
1459   {
1460     if (ctx->hdrs[i]->deleted
1461         && (ctx->magic != M_MAILDIR || !option (OPTMAILDIRTRASH)))
1462     {
1463       snprintf (path, sizeof (path), "%s/%s", ctx->path, ctx->hdrs[i]->path);
1464       if (ctx->magic == M_MAILDIR
1465           || (option (OPTMHPURGE) && ctx->magic == M_MH))
1466       {
1467 #if USE_HCACHE
1468         if (ctx->magic == M_MAILDIR)
1469           mutt_hcache_delete (hc, ctx->hdrs[i]->path + 3, &maildir_hcache_keylen);
1470 #endif /* USE_HCACHE */
1471         unlink (path);
1472       }
1473       else if (ctx->magic == M_MH)
1474       {
1475         /* MH just moves files out of the way when you delete them */
1476         if (*ctx->hdrs[i]->path != ',')
1477         {
1478           snprintf (tmp, sizeof (tmp), "%s/,%s", ctx->path,
1479                     ctx->hdrs[i]->path);
1480           unlink (tmp);
1481           rename (path, tmp);
1482         }
1483
1484       }
1485     }
1486     else if (ctx->hdrs[i]->changed || ctx->hdrs[i]->attach_del ||
1487              (ctx->magic == M_MAILDIR
1488               && (option (OPTMAILDIRTRASH) || ctx->hdrs[i]->trash)
1489               && (ctx->hdrs[i]->deleted != ctx->hdrs[i]->trash)))
1490     {
1491       if (ctx->magic == M_MAILDIR)
1492       {
1493         if (maildir_sync_message (ctx, i) == -1)
1494           goto err;
1495       }
1496       else
1497       {
1498         if (mh_sync_message (ctx, i) == -1)
1499           goto err;
1500       }
1501     }
1502   }
1503
1504 #if USE_HCACHE
1505   if (ctx->magic == M_MAILDIR)
1506     mutt_hcache_close (hc);
1507 #endif /* USE_HCACHE */
1508
1509   if (ctx->magic == M_MH)
1510     mh_update_sequences (ctx);
1511
1512   /* XXX race condition? */
1513
1514   maildir_update_mtime (ctx);
1515
1516   /* adjust indices */
1517
1518   if (ctx->deleted)
1519   {
1520     for (i = 0, j = 0; i < ctx->msgcount; i++)
1521     {
1522       if (!ctx->hdrs[i]->deleted
1523           || (ctx->magic == M_MAILDIR && option (OPTMAILDIRTRASH)))
1524         ctx->hdrs[i]->index = j++;
1525     }
1526   }
1527
1528   return 0;
1529
1530 err:
1531 #if USE_HCACHE
1532   if (ctx->magic == M_MAILDIR)
1533     mutt_hcache_close (hc);
1534 #endif /* USE_HCACHE */
1535   return -1;
1536 }
1537
1538 static char *maildir_canon_filename (char *dest, const char *src, size_t l)
1539 {
1540   char *t, *u;
1541
1542   if ((t = strrchr (src, '/')))
1543     src = t + 1;
1544
1545   strfcpy (dest, src, l);
1546   if ((u = strrchr (dest, ':')))
1547     *u = '\0';
1548
1549   return dest;
1550 }
1551
1552 static void maildir_update_tables (CONTEXT *ctx, int *index_hint)
1553 {
1554   short old_sort;
1555   int old_count;
1556   int i, j;
1557   
1558   if (Sort != SORT_ORDER)
1559   {
1560     old_sort = Sort;
1561     Sort = SORT_ORDER;
1562     mutt_sort_headers (ctx, 1);
1563     Sort = old_sort;
1564   }
1565   
1566   old_count = ctx->msgcount;
1567   for (i = 0, j = 0; i < old_count; i++)
1568   {
1569     if (ctx->hdrs[i]->active && index_hint && *index_hint == i)
1570       *index_hint = j;
1571     
1572     if (ctx->hdrs[i]->active)
1573       ctx->hdrs[i]->index = j++;
1574   }
1575
1576   mx_update_tables (ctx, 0);
1577   mutt_clear_threads (ctx);
1578 }
1579
1580 static void maildir_update_flags (CONTEXT *ctx, HEADER *o, HEADER *n)
1581 {
1582   /* save the global state here so we can reset it at the
1583    * end of list block if required.
1584    */
1585   int context_changed = ctx->changed;
1586   
1587   /* user didn't modify this message.  alter the flags to
1588    * match the current state on disk.  This may not actually
1589    * do anything, but we can't tell right now.  mutt_set_flag()
1590    * will just ignore the call if the status bits are
1591    * already properly set.
1592    */
1593   mutt_set_flag (ctx, o, M_FLAG, n->flagged);
1594   mutt_set_flag (ctx, o, M_REPLIED, n->replied);
1595   mutt_set_flag (ctx, o, M_READ, n->read);
1596   mutt_set_flag (ctx, o, M_OLD, n->old);
1597
1598   /* mutt_set_flag() will set this, but we don't need to
1599    * sync the changes we made because we just updated the
1600    * context to match the current on-disk state of the
1601    * message.
1602    */
1603   o->changed = 0;
1604   
1605   /* if the mailbox was not modified before we made these
1606    * changes, unset the changed flag since nothing needs to
1607    * be synchronized.
1608    */
1609   if (!context_changed)
1610     ctx->changed = 0;
1611 }
1612
1613
1614 /* This function handles arrival of new mail and reopening of
1615  * maildir folders.  The basic idea here is we check to see if either
1616  * the new or cur subdirectories have changed, and if so, we scan them
1617  * for the list of files.  We check for newly added messages, and
1618  * then merge the flags messages we already knew about.  We don't treat
1619  * either subdirectory differently, as mail could be copied directly into
1620  * the cur directory from another agent.
1621  */
1622 int maildir_check_mailbox (CONTEXT * ctx, int *index_hint)
1623 {
1624   struct stat st_new;           /* status of the "new" subdirectory */
1625   struct stat st_cur;           /* status of the "cur" subdirectory */
1626   char buf[_POSIX_PATH_MAX];
1627   int changed = 0;              /* bitmask representing which subdirectories
1628                                    have changed.  0x1 = new, 0x2 = cur */
1629   int occult = 0;               /* messages were removed from the mailbox */
1630   int have_new = 0;             /* messages were added to the mailbox */
1631   struct maildir *md;           /* list of messages in the mailbox */
1632   struct maildir **last, *p;
1633   int i;
1634   HASH *fnames;                 /* hash table for quickly looking up the base filename
1635                                    for a maildir message */
1636
1637   /* XXX seems like this check belongs in mx_check_mailbox()
1638    * rather than here.
1639    */
1640   if (!option (OPTCHECKNEW))
1641     return 0;
1642
1643   snprintf (buf, sizeof (buf), "%s/new", ctx->path);
1644   if (stat (buf, &st_new) == -1)
1645     return -1;
1646
1647   snprintf (buf, sizeof (buf), "%s/cur", ctx->path);
1648   if (stat (buf, &st_cur) == -1)
1649     return -1;
1650
1651   /* determine which subdirectories need to be scanned */
1652   if (st_new.st_mtime > ctx->mtime)
1653     changed = 1;
1654   if (st_cur.st_mtime > ctx->mtime_cur)
1655     changed |= 2;
1656
1657   if (!changed)
1658     return 0;                   /* nothing to do */
1659
1660   /* update the modification times on the mailbox */
1661   ctx->mtime_cur = st_cur.st_mtime;
1662   ctx->mtime = st_new.st_mtime;
1663
1664   /* do a fast scan of just the filenames in
1665    * the subdirectories that have changed.
1666    */
1667   md = NULL;
1668   last = &md;
1669   if (changed & 1)
1670     maildir_parse_dir (ctx, &last, "new", NULL);
1671   if (changed & 2)
1672     maildir_parse_dir (ctx, &last, "cur", NULL);
1673
1674   /* we create a hash table keyed off the canonical (sans flags) filename
1675    * of each message we scanned.  This is used in the loop over the
1676    * existing messages below to do some correlation.
1677    */
1678   fnames = hash_create (1031);
1679
1680   for (p = md; p; p = p->next)
1681   {
1682     maildir_canon_filename (buf, p->h->path, sizeof (buf));
1683     p->canon_fname = safe_strdup (buf);
1684     hash_insert (fnames, p->canon_fname, p, 0);
1685   }
1686
1687   /* check for modifications and adjust flags */
1688   for (i = 0; i < ctx->msgcount; i++)
1689   {
1690     ctx->hdrs[i]->active = 0;
1691     maildir_canon_filename (buf, ctx->hdrs[i]->path, sizeof (buf));
1692     p = hash_find (fnames, buf);
1693     if (p && p->h)
1694     {
1695       /* message already exists, merge flags */
1696       ctx->hdrs[i]->active = 1;
1697
1698       /* check to see if the message has moved to a different
1699        * subdirectory.  If so, update the associated filename.
1700        */
1701       if (mutt_strcmp (ctx->hdrs[i]->path, p->h->path))
1702         mutt_str_replace (&ctx->hdrs[i]->path, p->h->path);
1703
1704       /* if the user hasn't modified the flags on this message, update
1705        * the flags we just detected.
1706        */
1707       if (!ctx->hdrs[i]->changed)
1708         maildir_update_flags (ctx, ctx->hdrs[i], p->h);
1709
1710       if (ctx->hdrs[i]->deleted == ctx->hdrs[i]->trash)
1711         ctx->hdrs[i]->deleted = p->h->deleted;
1712       ctx->hdrs[i]->trash = p->h->trash;
1713
1714       /* this is a duplicate of an existing header, so remove it */
1715       mutt_free_header (&p->h);
1716     }
1717     /* This message was not in the list of messages we just scanned.
1718      * Check to see if we have enough information to know if the
1719      * message has disappeared out from underneath us.
1720      */
1721     else if (((changed & 1) && (!strncmp (ctx->hdrs[i]->path, "new/", 4))) ||
1722              ((changed & 2) && (!strncmp (ctx->hdrs[i]->path, "cur/", 4))))
1723     {
1724       /* This message disappeared, so we need to simulate a "reopen"
1725        * event.  We know it disappeared because we just scanned the
1726        * subdirectory it used to reside in.
1727        */
1728       occult = 1;
1729     }
1730     else
1731     {
1732       /* This message resides in a subdirectory which was not
1733        * modified, so we assume that it is still present and
1734        * unchanged.
1735        */
1736       ctx->hdrs[i]->active = 1;
1737     }
1738   }
1739
1740   /* destroy the file name hash */
1741   hash_destroy (&fnames, NULL);
1742
1743   /* If we didn't just get new mail, update the tables. */
1744   if (occult)
1745     maildir_update_tables (ctx, index_hint);
1746   
1747   /* do any delayed parsing we need to do. */
1748   maildir_delayed_parsing (ctx, md);
1749
1750   /* Incorporate new messages */
1751   have_new = maildir_move_to_context (ctx, &md);
1752
1753   return occult ? M_REOPENED : (have_new ? M_NEW_MAIL : 0);
1754 }
1755
1756 /* 
1757  * This function handles arrival of new mail and reopening of
1758  * mh/maildir folders. Things are getting rather complex because we
1759  * don't have a well-defined "mailbox order", so the tricks from
1760  * mbox.c and mx.c won't work here.
1761  *
1762  * Don't change this code unless you _really_ understand what
1763  * happens.
1764  *
1765  */
1766
1767 int mh_check_mailbox (CONTEXT * ctx, int *index_hint)
1768 {
1769   char buf[_POSIX_PATH_MAX];
1770   struct stat st, st_cur;
1771   short modified = 0, have_new = 0, occult = 0;
1772   struct maildir *md, *p;
1773   struct maildir **last = NULL;
1774   struct mh_sequences mhs;
1775   HASH *fnames;
1776   int i;
1777
1778   if (!option (OPTCHECKNEW))
1779     return 0;
1780
1781   strfcpy (buf, ctx->path, sizeof (buf));
1782   if (stat (buf, &st) == -1)
1783     return -1;
1784   
1785   /* create .mh_sequences when there isn't one. */
1786   snprintf (buf, sizeof (buf), "%s/.mh_sequences", ctx->path);
1787   if ((i = stat (buf, &st_cur) == -1) && errno == ENOENT)
1788   {
1789     char *tmp;
1790     FILE *fp = NULL;
1791     
1792     if (mh_mkstemp (ctx, &fp, &tmp) == 0)
1793     {
1794       safe_fclose (&fp);
1795       if (safe_rename (tmp, buf) == -1)
1796         unlink (tmp);
1797       FREE (&tmp);
1798     }
1799   }
1800
1801   if (i == -1 && stat (buf, &st_cur) == -1)
1802     modified = 1;
1803
1804   if (st.st_mtime > ctx->mtime || st_cur.st_mtime > ctx->mtime_cur)
1805     modified = 1;
1806
1807   if (!modified)
1808     return 0;
1809
1810   ctx->mtime_cur = st_cur.st_mtime;
1811   ctx->mtime = st.st_mtime;
1812
1813   memset (&mhs, 0, sizeof (mhs));
1814   
1815   md   = NULL;
1816   last = &md;
1817   maildir_parse_dir (ctx, &last, NULL, NULL);
1818   mh_read_sequences (&mhs, ctx->path);
1819   mh_update_maildir (md, &mhs);
1820   mhs_free_sequences (&mhs);
1821
1822   /* check for modifications and adjust flags */
1823   fnames = hash_create (1031);
1824
1825   for (p = md; p; p = p->next)
1826     hash_insert (fnames, p->h->path, p, 0);
1827
1828   for (i = 0; i < ctx->msgcount; i++)
1829   {
1830     ctx->hdrs[i]->active = 0;
1831
1832     if ((p = hash_find (fnames, ctx->hdrs[i]->path)) && p->h &&
1833         (mbox_strict_cmp_headers (ctx->hdrs[i], p->h)))
1834     {
1835       ctx->hdrs[i]->active = 1;
1836       /* found the right message */
1837       if (!ctx->hdrs[i]->changed)
1838         maildir_update_flags (ctx, ctx->hdrs[i], p->h);
1839
1840       mutt_free_header (&p->h);
1841     }
1842     else /* message has disappeared */
1843       occult = 1;
1844   }
1845
1846   /* destroy the file name hash */
1847
1848   hash_destroy (&fnames, NULL);
1849
1850   /* If we didn't just get new mail, update the tables. */
1851   if (occult)
1852     maildir_update_tables (ctx, index_hint);
1853
1854   /* Incorporate new messages */
1855   have_new = maildir_move_to_context (ctx, &md);
1856
1857   return occult ? M_REOPENED : (have_new ? M_NEW_MAIL : 0);
1858 }
1859
1860
1861
1862
1863 /*
1864  * These functions try to find a message in a maildir folder when it
1865  * has moved under our feet.  Note that this code is rather expensive, but
1866  * then again, it's called rarely.
1867  */
1868
1869 FILE *_maildir_open_find_message (const char *folder, const char *unique,
1870                                   const char *subfolder)
1871 {
1872   char dir[_POSIX_PATH_MAX];
1873   char tunique[_POSIX_PATH_MAX];
1874   char fname[_POSIX_PATH_MAX];
1875
1876   DIR *dp;
1877   struct dirent *de;
1878
1879   FILE *fp = NULL;
1880   int oe = ENOENT;
1881
1882   snprintf (dir, sizeof (dir), "%s/%s", folder, subfolder);
1883
1884   if ((dp = opendir (dir)) == NULL)
1885   {
1886     errno = ENOENT;
1887     return NULL;
1888   }
1889
1890   while ((de = readdir (dp)))
1891   {
1892     maildir_canon_filename (tunique, de->d_name, sizeof (tunique));
1893
1894     if (!mutt_strcmp (tunique, unique))
1895     {
1896       snprintf (fname, sizeof (fname), "%s/%s/%s", folder, subfolder,
1897                 de->d_name);
1898       fp = fopen (fname, "r");  /* __FOPEN_CHECKED__ */
1899       oe = errno;
1900       break;
1901     }
1902   }
1903
1904   closedir (dp);
1905
1906   errno = oe;
1907   return fp;
1908 }
1909
1910 FILE *maildir_open_find_message (const char *folder, const char *msg)
1911 {
1912   char unique[_POSIX_PATH_MAX];
1913   FILE *fp;
1914
1915   static unsigned int new_hits = 0, cur_hits = 0;       /* simple dynamic optimization */
1916
1917   maildir_canon_filename (unique, msg, sizeof (unique));
1918
1919   if (
1920       (fp =
1921        _maildir_open_find_message (folder, unique,
1922                                    new_hits > cur_hits ? "new" : "cur"))
1923       || errno != ENOENT)
1924   {
1925     if (new_hits < UINT_MAX && cur_hits < UINT_MAX)
1926     {
1927       new_hits += (new_hits > cur_hits ? 1 : 0);
1928       cur_hits += (new_hits > cur_hits ? 0 : 1);
1929     }
1930
1931     return fp;
1932   }
1933   if (
1934       (fp =
1935        _maildir_open_find_message (folder, unique,
1936                                    new_hits > cur_hits ? "cur" : "new"))
1937       || errno != ENOENT)
1938   {
1939     if (new_hits < UINT_MAX && cur_hits < UINT_MAX)
1940     {
1941       new_hits += (new_hits > cur_hits ? 0 : 1);
1942       cur_hits += (new_hits > cur_hits ? 1 : 0);
1943     }
1944
1945     return fp;
1946   }
1947
1948   return NULL;
1949 }
1950
1951
1952 /*
1953  * Returns:
1954  * 1 if there are no messages in the mailbox
1955  * 0 if there are messages in the mailbox
1956  * -1 on error
1957  */
1958 int maildir_check_empty (const char *path)
1959 {
1960   DIR *dp;
1961   struct dirent *de;
1962   int r = 1; /* assume empty until we find a message */
1963   char realpath[_POSIX_PATH_MAX];
1964   int iter = 0;
1965
1966   /* Strategy here is to look for any file not beginning with a period */
1967
1968   do {
1969     /* we do "cur" on the first iteration since its more likely that we'll
1970      * find old messages without having to scan both subdirs
1971      */
1972     snprintf (realpath, sizeof (realpath), "%s/%s", path,
1973               iter == 0 ? "cur" : "new");
1974     if ((dp = opendir (realpath)) == NULL)
1975       return -1;
1976     while ((de = readdir (dp)))
1977     {
1978       if (*de->d_name != '.')
1979       {
1980         r = 0;
1981         break;
1982       }
1983     }
1984     closedir (dp);
1985     iter++;
1986   } while (r && iter < 2);
1987
1988   return r;
1989 }
1990
1991 /*
1992  * Returns:
1993  * 1 if there are no messages in the mailbox
1994  * 0 if there are messages in the mailbox
1995  * -1 on error
1996  */
1997 int mh_check_empty (const char *path)
1998 {
1999   DIR *dp;
2000   struct dirent *de;
2001   int r = 1; /* assume empty until we find a message */
2002   
2003   if ((dp = opendir (path)) == NULL)
2004     return -1;
2005   while ((de = readdir (dp)))
2006   {
2007     if (mh_valid_message (de->d_name))
2008     {
2009       r = 0;
2010       break;
2011     }
2012   }
2013   closedir (dp);
2014   
2015   return r;
2016 }