mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2002,2007,2009 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1999-2005 Thomas Roessler <roessler@does-not-exist.org>
4 * Copyright (C) 2010,2013 Michael R. Elkins <me@mutt.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21/*
22 * This file contains routines specific to MH and ``maildir'' style
23 * mailboxes.
24 */
25
26#if HAVE_CONFIG_H
27# include "config.h"
28#endif
29
30#include "mutt.h"
31#include "mailbox.h"
32#include "mx.h"
33#include "copy.h"
34#include "sort.h"
35#if USE_HCACHE
36#include "hcache.h"
37#endif
38#include "mutt_curses.h"
39#include "buffy.h"
40
41#include <sys/stat.h>
42#include <sys/types.h>
43#include <dirent.h>
44#include <limits.h>
45#include <unistd.h>
46#include <fcntl.h>
47
48#include <stdio.h>
49#include <stdlib.h>
50#include <ctype.h>
51#include <errno.h>
52#include <string.h>
53#include <utime.h>
54
55#if HAVE_SYS_TIME_H
56#include <sys/time.h>
57#endif
58
59#define INS_SORT_THRESHOLD 6
60
61static int maildir_check_mailbox (CONTEXT * ctx, int *index_hint);
62static int mh_check_mailbox (CONTEXT * ctx, int *index_hint);
63
64struct maildir
65{
66 HEADER *h;
67 char *canon_fname;
68 unsigned header_parsed:1;
69#ifdef HAVE_DIRENT_D_INO
70 ino_t inode;
71#endif /* HAVE_DIRENT_D_INO */
72 struct maildir *next;
73};
74
75struct mh_sequences
76{
77 int max;
78 short *flags;
79};
80
81struct mh_data
82{
83 time_t mtime_cur;
84 mode_t mh_umask;
85};
86
87/* mh_sequences support */
88
89#define MH_SEQ_UNSEEN (1 << 0)
90#define MH_SEQ_REPLIED (1 << 1)
91#define MH_SEQ_FLAGGED (1 << 2)
92
93static inline struct mh_data *mh_data (CONTEXT *ctx)
94{
95 return (struct mh_data*)ctx->data;
96}
97
98static void mhs_alloc (struct mh_sequences *mhs, int i)
99{
100 int j;
101 int newmax;
102
103 if (i > mhs->max || !mhs->flags)
104 {
105 newmax = i + 128;
106 j = mhs->flags ? mhs->max + 1 : 0;
107 safe_realloc (&mhs->flags, sizeof (mhs->flags[0]) * (newmax + 1));
108 while (j <= newmax)
109 mhs->flags[j++] = 0;
110
111 mhs->max = newmax;
112 }
113}
114
115static void mhs_free_sequences (struct mh_sequences *mhs)
116{
117 FREE (&mhs->flags);
118}
119
120static short mhs_check (struct mh_sequences *mhs, int i)
121{
122 if (!mhs->flags || i > mhs->max)
123 return 0;
124 else
125 return mhs->flags[i];
126}
127
128static short mhs_set (struct mh_sequences *mhs, int i, short f)
129{
130 mhs_alloc (mhs, i);
131 mhs->flags[i] |= f;
132 return mhs->flags[i];
133}
134
135#if 0
136
137/* unused */
138
139static short mhs_unset (struct mh_sequences *mhs, int i, short f)
140{
141 mhs_alloc (mhs, i);
142 mhs->flags[i] &= ~f;
143 return mhs->flags[i];
144}
145
146#endif
147
148static int mh_read_token (char *t, int *first, int *last)
149{
150 char *p;
151 if ((p = strchr (t, '-')))
152 {
153 *p++ = '\0';
154 if (mutt_atoi (t, first) < 0 || mutt_atoi (p, last) < 0)
155 return -1;
156 }
157 else
158 {
159 if (mutt_atoi (t, first) < 0)
160 return -1;
161 *last = *first;
162 }
163 return 0;
164}
165
166static int mh_read_sequences (struct mh_sequences *mhs, const char *path)
167{
168 FILE *fp;
169 int line = 1;
170 char *buff = NULL;
171 char *t;
172 size_t sz = 0;
173
174 short f;
175 int first, last, rc = 0;
176
177 char pathname[_POSIX_PATH_MAX];
178 snprintf (pathname, sizeof (pathname), "%s/.mh_sequences", path);
179
180 if (!(fp = fopen (pathname, "r")))
181 return 0; /* yes, ask callers to silently ignore the error */
182
183 while ((buff = mutt_read_line (buff, &sz, fp, &line, 0)))
184 {
185 if (!(t = strtok (buff, " \t:")))
186 continue;
187
188 if (!mutt_strcmp (t, MhUnseen))
189 f = MH_SEQ_UNSEEN;
190 else if (!mutt_strcmp (t, MhFlagged))
191 f = MH_SEQ_FLAGGED;
192 else if (!mutt_strcmp (t, MhReplied))
193 f = MH_SEQ_REPLIED;
194 else /* unknown sequence */
195 continue;
196
197 while ((t = strtok (NULL, " \t:")))
198 {
199 if (mh_read_token (t, &first, &last) < 0)
200 {
201 mhs_free_sequences (mhs);
202 rc = -1;
203 goto out;
204 }
205 for (; first <= last; first++)
206 mhs_set (mhs, first, f);
207 }
208 }
209
210 rc = 0;
211
212out:
213 FREE (&buff);
214 safe_fclose (&fp);
215 return rc;
216}
217
218static inline mode_t mh_umask (CONTEXT* ctx)
219{
220 struct stat st;
221 struct mh_data* data = mh_data (ctx);
222
223 if (data && data->mh_umask)
224 return data->mh_umask;
225
226 if (stat (ctx->path, &st))
227 {
228 dprint (1, (debugfile, "stat failed on %s\n", ctx->path));
229 return 077;
230 }
231
232 return 0777 & ~st.st_mode;
233}
234
235/*
236 * Returns 1 if the .mh_sequences last modification time is more recent than the last visit to this mailbox
237 * Returns 0 if the modifcation time is older
238 * Returns -1 on error
239 */
240static int mh_sequences_changed(BUFFY *b)
241{
242 char path[_POSIX_PATH_MAX];
243 struct stat sb;
244
245 if ((snprintf(path, sizeof(path), "%s/.mh_sequences", b->path) < sizeof(path)) &&
246 (stat(path, &sb) == 0))
247 return (sb.st_mtime > b->last_visited);
248 return -1;
249}
250
251/*
252 * Returns 1 if the modification time on the message file is older than the last visit to this mailbox
253 * Returns 0 if the modtime is newer
254 * Returns -1 on error
255 */
256static int mh_already_notified(BUFFY *b, int msgno)
257{
258 char path[_POSIX_PATH_MAX];
259 struct stat sb;
260
261 if ((snprintf(path, sizeof(path), "%s/%d", b->path, msgno) < sizeof(path)) &&
262 (stat(path, &sb) == 0))
263 return (sb.st_mtime <= b->last_visited);
264 return -1;
265}
266
267/* Checks new mail for a mh mailbox.
268 * check_stats: if true, also count total, new, and flagged mesages.
269 * Returns 1 if the mailbox has new mail.
270 */
271int mh_buffy (BUFFY *mailbox, int check_stats)
272{
273 int i;
274 struct mh_sequences mhs;
275 int check_new = 1;
276 int rc = 0;
277 DIR *dirp;
278 struct dirent *de;
279
280 /* when $mail_check_recent is set and the .mh_sequences file hasn't changed
281 * since the last mailbox visit, there is no "new mail" */
282 if (option(OPTMAILCHECKRECENT) && mh_sequences_changed(mailbox) <= 0)
283 {
284 rc = 0;
285 check_new = 0;
286 }
287
288 if (! (check_new || check_stats))
289 return rc;
290
291 memset (&mhs, 0, sizeof (mhs));
292 if (mh_read_sequences (&mhs, mailbox->path) < 0)
293 return 0;
294
295 if (check_stats)
296 {
297 mailbox->msg_count = 0;
298 mailbox->msg_unread = 0;
299 mailbox->msg_flagged = 0;
300 }
301
302 for (i = mhs.max; i > 0; i--)
303 {
304 if (check_stats &&
305 (mhs_check (&mhs, i) & MH_SEQ_FLAGGED))
306 mailbox->msg_flagged++;
307 if (mhs_check (&mhs, i) & MH_SEQ_UNSEEN)
308 {
309 if (check_stats)
310 mailbox->msg_unread++;
311 if (check_new)
312 {
313 /* if the first unseen message we encounter was in the mailbox during the
314 last visit, don't notify about it */
315 if (!option(OPTMAILCHECKRECENT) || mh_already_notified(mailbox, i) == 0)
316 {
317 mailbox->new = 1;
318 rc = 1;
319 }
320 /* Because we are traversing from high to low, we can stop
321 * checking for new mail after the first unseen message.
322 * Whether it resulted in "new mail" or not. */
323 check_new = 0;
324 if (!check_stats)
325 break;
326 }
327 }
328 }
329 mhs_free_sequences (&mhs);
330
331 if (check_stats)
332 {
333 if ((dirp = opendir (mailbox->path)) != NULL)
334 {
335 while ((de = readdir (dirp)) != NULL)
336 {
337 if (*de->d_name == '.')
338 continue;
339 if (mh_valid_message (de->d_name))
340 mailbox->msg_count++;
341 }
342 closedir (dirp);
343 }
344 }
345
346 return rc;
347}
348
349static int mh_mkstemp (CONTEXT * dest, FILE ** fp, char **tgt)
350{
351 int fd;
352 char path[_POSIX_PATH_MAX];
353 mode_t omask;
354
355 omask = umask (mh_umask (dest));
356 FOREVER
357 {
358 snprintf (path, _POSIX_PATH_MAX, "%s/.mutt-%s-%d-%d",
359 dest->path, NONULL (Hostname), (int) getpid (), Counter++);
360 if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0666)) == -1)
361 {
362 if (errno != EEXIST)
363 {
364 mutt_perror (path);
365 umask (omask);
366 return -1;
367 }
368 }
369 else
370 {
371 *tgt = safe_strdup (path);
372 break;
373 }
374 }
375 umask (omask);
376
377 if ((*fp = fdopen (fd, "w")) == NULL)
378 {
379 FREE (tgt); /* __FREE_CHECKED__ */
380 close (fd);
381 unlink (path);
382 return (-1);
383 }
384
385 return 0;
386}
387
388static void mhs_write_one_sequence (FILE * fp, struct mh_sequences *mhs,
389 short f, const char *tag)
390{
391 int i;
392 int first, last;
393 fprintf (fp, "%s:", tag);
394
395 first = -1;
396 last = -1;
397
398 for (i = 0; i <= mhs->max; i++)
399 {
400 if ((mhs_check (mhs, i) & f))
401 {
402 if (first < 0)
403 first = i;
404 else
405 last = i;
406 }
407 else if (first >= 0)
408 {
409 if (last < 0)
410 fprintf (fp, " %d", first);
411 else
412 fprintf (fp, " %d-%d", first, last);
413
414 first = -1;
415 last = -1;
416 }
417 }
418
419 if (first >= 0)
420 {
421 if (last < 0)
422 fprintf (fp, " %d", first);
423 else
424 fprintf (fp, " %d-%d", first, last);
425 }
426
427 fputc ('\n', fp);
428}
429
430/* XXX - we don't currently remove deleted messages from sequences we don't know. Should we? */
431
432static void mh_update_sequences (CONTEXT * ctx)
433{
434 FILE *ofp, *nfp;
435
436 char sequences[_POSIX_PATH_MAX];
437 char *tmpfname;
438 char *buff = NULL;
439 char *p;
440 size_t s;
441 int l = 0;
442 int i;
443
444 int unseen = 0;
445 int flagged = 0;
446 int replied = 0;
447
448 char seq_unseen[STRING];
449 char seq_replied[STRING];
450 char seq_flagged[STRING];
451
452
453 struct mh_sequences mhs;
454 memset (&mhs, 0, sizeof (mhs));
455
456 snprintf (seq_unseen, sizeof (seq_unseen), "%s:", NONULL (MhUnseen));
457 snprintf (seq_replied, sizeof (seq_replied), "%s:", NONULL (MhReplied));
458 snprintf (seq_flagged, sizeof (seq_flagged), "%s:", NONULL (MhFlagged));
459
460 if (mh_mkstemp (ctx, &nfp, &tmpfname) != 0)
461 {
462 /* error message? */
463 return;
464 }
465
466 snprintf (sequences, sizeof (sequences), "%s/.mh_sequences", ctx->path);
467
468
469 /* first, copy unknown sequences */
470 if ((ofp = fopen (sequences, "r")))
471 {
472 while ((buff = mutt_read_line (buff, &s, ofp, &l, 0)))
473 {
474 if (!mutt_strncmp (buff, seq_unseen, mutt_strlen (seq_unseen)))
475 continue;
476 if (!mutt_strncmp (buff, seq_flagged, mutt_strlen (seq_flagged)))
477 continue;
478 if (!mutt_strncmp (buff, seq_replied, mutt_strlen (seq_replied)))
479 continue;
480
481 fprintf (nfp, "%s\n", buff);
482 }
483 }
484 safe_fclose (&ofp);
485
486 /* now, update our unseen, flagged, and replied sequences */
487 for (l = 0; l < ctx->msgcount; l++)
488 {
489 if (ctx->hdrs[l]->deleted)
490 continue;
491
492 if ((p = strrchr (ctx->hdrs[l]->path, '/')))
493 p++;
494 else
495 p = ctx->hdrs[l]->path;
496
497 if (mutt_atoi (p, &i) < 0)
498 continue;
499
500 if (!ctx->hdrs[l]->read)
501 {
502 mhs_set (&mhs, i, MH_SEQ_UNSEEN);
503 unseen++;
504 }
505 if (ctx->hdrs[l]->flagged)
506 {
507 mhs_set (&mhs, i, MH_SEQ_FLAGGED);
508 flagged++;
509 }
510 if (ctx->hdrs[l]->replied)
511 {
512 mhs_set (&mhs, i, MH_SEQ_REPLIED);
513 replied++;
514 }
515 }
516
517 /* write out the new sequences */
518 if (unseen)
519 mhs_write_one_sequence (nfp, &mhs, MH_SEQ_UNSEEN, NONULL (MhUnseen));
520 if (flagged)
521 mhs_write_one_sequence (nfp, &mhs, MH_SEQ_FLAGGED, NONULL (MhFlagged));
522 if (replied)
523 mhs_write_one_sequence (nfp, &mhs, MH_SEQ_REPLIED, NONULL (MhReplied));
524
525 mhs_free_sequences (&mhs);
526
527
528 /* try to commit the changes - no guarantee here */
529 safe_fclose (&nfp);
530
531 unlink (sequences);
532 if (safe_rename (tmpfname, sequences) != 0)
533 {
534 /* report an error? */
535 unlink (tmpfname);
536 }
537
538 FREE (&tmpfname);
539}
540
541static void mh_sequences_add_one (CONTEXT * ctx, int n, short unseen,
542 short flagged, short replied)
543{
544 short unseen_done = 0;
545 short flagged_done = 0;
546 short replied_done = 0;
547
548 FILE *ofp = NULL, *nfp = NULL;
549
550 char *tmpfname;
551 char sequences[_POSIX_PATH_MAX];
552
553 char seq_unseen[STRING];
554 char seq_replied[STRING];
555 char seq_flagged[STRING];
556
557 char *buff = NULL;
558 int line;
559 size_t sz;
560
561 if (mh_mkstemp (ctx, &nfp, &tmpfname) == -1)
562 return;
563
564 snprintf (seq_unseen, sizeof (seq_unseen), "%s:", NONULL (MhUnseen));
565 snprintf (seq_replied, sizeof (seq_replied), "%s:", NONULL (MhReplied));
566 snprintf (seq_flagged, sizeof (seq_flagged), "%s:", NONULL (MhFlagged));
567
568 snprintf (sequences, sizeof (sequences), "%s/.mh_sequences", ctx->path);
569 if ((ofp = fopen (sequences, "r")))
570 {
571 while ((buff = mutt_read_line (buff, &sz, ofp, &line, 0)))
572 {
573 if (unseen && !strncmp (buff, seq_unseen, mutt_strlen (seq_unseen)))
574 {
575 fprintf (nfp, "%s %d\n", buff, n);
576 unseen_done = 1;
577 }
578 else if (flagged
579 && !strncmp (buff, seq_flagged, mutt_strlen (seq_flagged)))
580 {
581 fprintf (nfp, "%s %d\n", buff, n);
582 flagged_done = 1;
583 }
584 else if (replied
585 && !strncmp (buff, seq_replied, mutt_strlen (seq_replied)))
586 {
587 fprintf (nfp, "%s %d\n", buff, n);
588 replied_done = 1;
589 }
590 else
591 fprintf (nfp, "%s\n", buff);
592 }
593 }
594 safe_fclose (&ofp);
595 FREE (&buff);
596
597 if (!unseen_done && unseen)
598 fprintf (nfp, "%s: %d\n", NONULL (MhUnseen), n);
599 if (!flagged_done && flagged)
600 fprintf (nfp, "%s: %d\n", NONULL (MhFlagged), n);
601 if (!replied_done && replied)
602 fprintf (nfp, "%s: %d\n", NONULL (MhReplied), n);
603
604 safe_fclose (&nfp);
605
606 unlink (sequences);
607 if (safe_rename (tmpfname, sequences) != 0)
608 unlink (tmpfname);
609
610 FREE (&tmpfname);
611}
612
613static void mh_update_maildir (struct maildir *md, struct mh_sequences *mhs)
614{
615 int i;
616 short f;
617 char *p;
618
619 for (; md; md = md->next)
620 {
621 if ((p = strrchr (md->h->path, '/')))
622 p++;
623 else
624 p = md->h->path;
625
626 if (mutt_atoi (p, &i) < 0)
627 continue;
628 f = mhs_check (mhs, i);
629
630 md->h->read = (f & MH_SEQ_UNSEEN) ? 0 : 1;
631 md->h->flagged = (f & MH_SEQ_FLAGGED) ? 1 : 0;
632 md->h->replied = (f & MH_SEQ_REPLIED) ? 1 : 0;
633 }
634}
635
636/* maildir support */
637
638static void maildir_free_entry (struct maildir **md)
639{
640 if (!md || !*md)
641 return;
642
643 FREE (&(*md)->canon_fname);
644 if ((*md)->h)
645 mutt_free_header (&(*md)->h);
646
647 FREE (md); /* __FREE_CHECKED__ */
648}
649
650static void maildir_free_maildir (struct maildir **md)
651{
652 struct maildir *p, *q;
653
654 if (!md || !*md)
655 return;
656
657 for (p = *md; p; p = q)
658 {
659 q = p->next;
660 maildir_free_entry (&p);
661 }
662}
663
664static void maildir_parse_flags (HEADER * h, const char *path)
665{
666 char *p, *q = NULL;
667
668 h->flagged = 0;
669 h->read = 0;
670 h->replied = 0;
671
672 if ((p = strrchr (path, ':')) != NULL && mutt_strncmp (p + 1, "2,", 2) == 0)
673 {
674 p += 3;
675
676 mutt_str_replace (&h->maildir_flags, p);
677 q = h->maildir_flags;
678
679 while (*p)
680 {
681 switch (*p)
682 {
683 case 'F':
684
685 h->flagged = 1;
686 break;
687
688 case 'S': /* seen */
689
690 h->read = 1;
691 break;
692
693 case 'R': /* replied */
694
695 h->replied = 1;
696 break;
697
698 case 'T': /* trashed */
699 if (!h->flagged || !option(OPTFLAGSAFE))
700 {
701 h->trash = 1;
702 h->deleted = 1;
703 }
704 break;
705
706 default:
707 *q++ = *p;
708 break;
709 }
710 p++;
711 }
712 }
713
714 if (q == h->maildir_flags)
715 FREE (&h->maildir_flags);
716 else if (q)
717 *q = '\0';
718}
719
720static void maildir_update_mtime (CONTEXT * ctx)
721{
722 char buf[_POSIX_PATH_MAX];
723 struct stat st;
724 struct mh_data *data = mh_data (ctx);
725
726 if (ctx->magic == MUTT_MAILDIR)
727 {
728 snprintf (buf, sizeof (buf), "%s/%s", ctx->path, "cur");
729 if (stat (buf, &st) == 0)
730 data->mtime_cur = st.st_mtime;
731 snprintf (buf, sizeof (buf), "%s/%s", ctx->path, "new");
732 }
733 else
734 {
735 snprintf (buf, sizeof (buf), "%s/.mh_sequences", ctx->path);
736 if (stat (buf, &st) == 0)
737 data->mtime_cur = st.st_mtime;
738
739 strfcpy (buf, ctx->path, sizeof (buf));
740 }
741
742 if (stat (buf, &st) == 0)
743 ctx->mtime = st.st_mtime;
744}
745
746/*
747 * Actually parse a maildir message. This may also be used to fill
748 * out a fake header structure generated by lazy maildir parsing.
749 */
750static HEADER *maildir_parse_message (int magic, const char *fname,
751 int is_old, HEADER * _h)
752{
753 FILE *f;
754 HEADER *h = _h;
755 struct stat st;
756
757 if ((f = fopen (fname, "r")) != NULL)
758 {
759 if (!h)
760 h = mutt_new_header ();
761 h->env = mutt_read_rfc822_header (f, h, 0, 0);
762
763 fstat (fileno (f), &st);
764 safe_fclose (&f);
765
766 if (!h->received)
767 h->received = h->date_sent;
768
769 /* always update the length since we have fresh information available. */
770 h->content->length = st.st_size - h->content->offset;
771
772 h->index = -1;
773
774 if (magic == MUTT_MAILDIR)
775 {
776 /*
777 * maildir stores its flags in the filename, so ignore the
778 * flags in the header of the message
779 */
780
781 h->old = is_old;
782 maildir_parse_flags (h, fname);
783 }
784 return h;
785 }
786 return NULL;
787}
788
789/* Ignore the garbage files. A valid MH message consists of only
790 * digits. Deleted message get moved to a filename with a comma before
791 * it.
792 */
793
794int mh_valid_message (const char *s)
795{
796 for (; *s; s++)
797 {
798 if (!isdigit ((unsigned char) *s))
799 return 0;
800 }
801 return 1;
802}
803
804static int maildir_parse_dir (CONTEXT * ctx, struct maildir ***last,
805 const char *subdir, int *count,
806 progress_t *progress)
807{
808 DIR *dirp;
809 struct dirent *de;
810 char buf[_POSIX_PATH_MAX];
811 int is_old = 0;
812 struct maildir *entry;
813 HEADER *h;
814
815 if (subdir)
816 {
817 snprintf (buf, sizeof (buf), "%s/%s", ctx->path, subdir);
818 is_old = (mutt_strcmp ("cur", subdir) == 0);
819 }
820 else
821 strfcpy (buf, ctx->path, sizeof (buf));
822
823 if ((dirp = opendir (buf)) == NULL)
824 return -1;
825
826 while ((de = readdir (dirp)) != NULL)
827 {
828 if ((ctx->magic == MUTT_MH && !mh_valid_message (de->d_name))
829 || (ctx->magic == MUTT_MAILDIR && *de->d_name == '.'))
830 continue;
831
832 /* FOO - really ignore the return value? */
833 dprint (2,
834 (debugfile, "%s:%d: queueing %s\n", __FILE__, __LINE__,
835 de->d_name));
836
837 h = mutt_new_header ();
838 h->old = is_old;
839 if (ctx->magic == MUTT_MAILDIR)
840 maildir_parse_flags (h, de->d_name);
841
842 if (count)
843 {
844 (*count)++;
845 if (!ctx->quiet && progress)
846 mutt_progress_update (progress, *count, -1);
847 }
848
849 if (subdir)
850 {
851 char tmp[_POSIX_PATH_MAX];
852 snprintf (tmp, sizeof (tmp), "%s/%s", subdir, de->d_name);
853 h->path = safe_strdup (tmp);
854 }
855 else
856 h->path = safe_strdup (de->d_name);
857
858 entry = safe_calloc (sizeof (struct maildir), 1);
859 entry->h = h;
860#ifdef HAVE_DIRENT_D_INO
861 entry->inode = de->d_ino;
862#endif /* HAVE_DIRENT_D_INO */
863 **last = entry;
864 *last = &entry->next;
865 }
866
867 closedir (dirp);
868
869 return 0;
870}
871
872static int maildir_add_to_context (CONTEXT * ctx, struct maildir *md)
873{
874 int oldmsgcount = ctx->msgcount;
875
876 while (md)
877 {
878
879 dprint (2, (debugfile, "%s:%d maildir_add_to_context(): Considering %s\n",
880 __FILE__, __LINE__, NONULL (md->canon_fname)));
881
882 if (md->h)
883 {
884 dprint (2,
885 (debugfile,
886 "%s:%d Adding header structure. Flags: %s%s%s%s%s\n", __FILE__,
887 __LINE__, md->h->flagged ? "f" : "", md->h->deleted ? "D" : "",
888 md->h->replied ? "r" : "", md->h->old ? "O" : "",
889 md->h->read ? "R" : ""));
890 if (ctx->msgcount == ctx->hdrmax)
891 mx_alloc_memory (ctx);
892
893 ctx->hdrs[ctx->msgcount] = md->h;
894 ctx->hdrs[ctx->msgcount]->index = ctx->msgcount;
895 ctx->size +=
896 md->h->content->length + md->h->content->offset -
897 md->h->content->hdr_offset;
898
899 md->h = NULL;
900 ctx->msgcount++;
901 }
902 md = md->next;
903 }
904
905 if (ctx->msgcount > oldmsgcount)
906 {
907 mx_update_context (ctx, ctx->msgcount - oldmsgcount);
908 return 1;
909 }
910 return 0;
911}
912
913static int maildir_move_to_context (CONTEXT * ctx, struct maildir **md)
914{
915 int r;
916 r = maildir_add_to_context (ctx, *md);
917 maildir_free_maildir (md);
918 return r;
919}
920
921#if USE_HCACHE
922static size_t maildir_hcache_keylen (const char *fn)
923{
924 const char * p = strrchr (fn, ':');
925 return p ? (size_t) (p - fn) : mutt_strlen(fn);
926}
927#endif
928
929#if HAVE_DIRENT_D_INO
930static int md_cmp_inode (struct maildir *a, struct maildir *b)
931{
932 return a->inode - b->inode;
933}
934#endif
935
936static int md_cmp_path (struct maildir *a, struct maildir *b)
937{
938 return strcmp (a->h->path, b->h->path);
939}
940
941/*
942 * Merge two maildir lists according to the inode numbers.
943 */
944static struct maildir* maildir_merge_lists (struct maildir *left,
945 struct maildir *right,
946 int (*cmp) (struct maildir *,
947 struct maildir *))
948{
949 struct maildir* head;
950 struct maildir* tail;
951
952 if (left && right)
953 {
954 if (cmp (left, right) < 0)
955 {
956 head = left;
957 left = left->next;
958 }
959 else
960 {
961 head = right;
962 right = right->next;
963 }
964 }
965 else
966 {
967 if (left)
968 return left;
969 else
970 return right;
971 }
972
973 tail = head;
974
975 while (left && right)
976 {
977 if (cmp (left, right) < 0)
978 {
979 tail->next = left;
980 left = left->next;
981 }
982 else
983 {
984 tail->next = right;
985 right = right->next;
986 }
987 tail = tail->next;
988 }
989
990 if (left)
991 {
992 tail->next = left;
993 }
994 else
995 {
996 tail->next = right;
997 }
998
999 return head;
1000}
1001
1002static struct maildir* maildir_ins_sort (struct maildir* list,
1003 int (*cmp) (struct maildir *,
1004 struct maildir *))
1005{
1006 struct maildir *tmp, *last, *ret = NULL, *back;
1007
1008 ret = list;
1009 list = list->next;
1010 ret->next = NULL;
1011
1012 while (list)
1013 {
1014 last = NULL;
1015 back = list->next;
1016 for (tmp = ret; tmp && cmp (tmp, list) <= 0; tmp = tmp->next)
1017 last = tmp;
1018
1019 list->next = tmp;
1020 if (last)
1021 last->next = list;
1022 else
1023 ret = list;
1024
1025 list = back;
1026 }
1027
1028 return ret;
1029}
1030
1031/*
1032 * Sort maildir list according to inode.
1033 */
1034static struct maildir* maildir_sort (struct maildir* list, size_t len,
1035 int (*cmp) (struct maildir *,
1036 struct maildir *))
1037{
1038 struct maildir* left = list;
1039 struct maildir* right = list;
1040 size_t c = 0;
1041
1042 if (!list || !list->next)
1043 {
1044 return list;
1045 }
1046
1047 if (len != (size_t)(-1) && len <= INS_SORT_THRESHOLD)
1048 return maildir_ins_sort (list, cmp);
1049
1050 list = list->next;
1051 while (list && list->next)
1052 {
1053 right = right->next;
1054 list = list->next->next;
1055 c++;
1056 }
1057
1058 list = right;
1059 right = right->next;
1060 list->next = 0;
1061
1062 left = maildir_sort (left, c, cmp);
1063 right = maildir_sort (right, c, cmp);
1064 return maildir_merge_lists (left, right, cmp);
1065}
1066
1067/* Sorts mailbox into it's natural order.
1068 * Currently only defined for MH where files are numbered.
1069 */
1070static void mh_sort_natural (CONTEXT *ctx, struct maildir **md)
1071{
1072 if (!ctx || !md || !*md || ctx->magic != MUTT_MH || Sort != SORT_ORDER)
1073 return;
1074 dprint (4, (debugfile, "maildir: sorting %s into natural order\n",
1075 ctx->path));
1076 *md = maildir_sort (*md, (size_t) -1, md_cmp_path);
1077}
1078
1079#if HAVE_DIRENT_D_INO
1080static struct maildir *skip_duplicates (struct maildir *p, struct maildir **last)
1081{
1082 /*
1083 * Skip ahead to the next non-duplicate message.
1084 *
1085 * p should never reach NULL, because we couldn't have reached this point unless
1086 * there was a message that needed to be parsed.
1087 *
1088 * the check for p->header_parsed is likely unnecessary since the dupes will most
1089 * likely be at the head of the list. but it is present for consistency with
1090 * the check at the top of the for() loop in maildir_delayed_parsing().
1091 */
1092 while (!p->h || p->header_parsed) {
1093 *last = p;
1094 p = p->next;
1095 }
1096 return p;
1097}
1098#endif
1099
1100/*
1101 * This function does the second parsing pass
1102 */
1103static void maildir_delayed_parsing (CONTEXT * ctx, struct maildir **md,
1104 progress_t *progress)
1105{
1106 struct maildir *p, *last = NULL;
1107 char fn[_POSIX_PATH_MAX];
1108 int count;
1109#if HAVE_DIRENT_D_INO
1110 int sort = 0;
1111#endif
1112#if USE_HCACHE
1113 header_cache_t *hc = NULL;
1114 void *data;
1115 struct timeval *when = NULL;
1116 struct stat lastchanged;
1117 int ret;
1118#endif
1119
1120#if HAVE_DIRENT_D_INO
1121#define DO_SORT() do { \
1122 if (!sort) \
1123 { \
1124 dprint (4, (debugfile, "maildir: need to sort %s by inode\n", ctx->path)); \
1125 p = maildir_sort (p, (size_t) -1, md_cmp_inode); \
1126 if (!last) \
1127 *md = p; \
1128 else \
1129 last->next = p; \
1130 sort = 1; \
1131 p = skip_duplicates (p, &last); \
1132 snprintf (fn, sizeof (fn), "%s/%s", ctx->path, p->h->path); \
1133 } \
1134} while(0)
1135#else
1136#define DO_SORT() /* nothing */
1137#endif
1138
1139#if USE_HCACHE
1140 hc = mutt_hcache_open (HeaderCache, ctx->path, NULL);
1141#endif
1142
1143 for (p = *md, count = 0; p; p = p->next, count++)
1144 {
1145 if (! (p && p->h && !p->header_parsed))
1146 {
1147 last = p;
1148 continue;
1149 }
1150
1151 if (!ctx->quiet && progress)
1152 mutt_progress_update (progress, count, -1);
1153
1154 DO_SORT();
1155
1156 snprintf (fn, sizeof (fn), "%s/%s", ctx->path, p->h->path);
1157
1158#if USE_HCACHE
1159 if (option(OPTHCACHEVERIFY))
1160 {
1161 ret = stat(fn, &lastchanged);
1162 }
1163 else
1164 {
1165 lastchanged.st_mtime = 0;
1166 ret = 0;
1167 }
1168
1169 if (ctx->magic == MUTT_MH)
1170 data = mutt_hcache_fetch (hc, p->h->path, strlen);
1171 else
1172 data = mutt_hcache_fetch (hc, p->h->path + 3, &maildir_hcache_keylen);
1173 when = (struct timeval *) data;
1174
1175 if (data != NULL && !ret && lastchanged.st_mtime <= when->tv_sec)
1176 {
1177 p->h = mutt_hcache_restore ((unsigned char *)data, &p->h);
1178 if (ctx->magic == MUTT_MAILDIR)
1179 maildir_parse_flags (p->h, fn);
1180 }
1181 else
1182 {
1183#endif /* USE_HCACHE */
1184
1185 if (maildir_parse_message (ctx->magic, fn, p->h->old, p->h))
1186 {
1187 p->header_parsed = 1;
1188#if USE_HCACHE
1189 if (ctx->magic == MUTT_MH)
1190 mutt_hcache_store (hc, p->h->path, p->h, 0, strlen, MUTT_GENERATE_UIDVALIDITY);
1191 else
1192 mutt_hcache_store (hc, p->h->path + 3, p->h, 0, &maildir_hcache_keylen, MUTT_GENERATE_UIDVALIDITY);
1193#endif
1194 } else
1195 mutt_free_header (&p->h);
1196#if USE_HCACHE
1197 }
1198 FREE (&data);
1199#endif
1200 last = p;
1201 }
1202#if USE_HCACHE
1203 mutt_hcache_close (hc);
1204#endif
1205
1206#undef DO_SORT
1207
1208 mh_sort_natural (ctx, md);
1209}
1210
1211static int mh_close_mailbox (CONTEXT *ctx)
1212{
1213 FREE (&ctx->data);
1214
1215 return 0;
1216}
1217
1218/* Read a MH/maildir style mailbox.
1219 *
1220 * args:
1221 * ctx [IN/OUT] context for this mailbox
1222 * subdir [IN] NULL for MH mailboxes, otherwise the subdir of the
1223 * maildir mailbox to read from
1224 */
1225static int mh_read_dir (CONTEXT * ctx, const char *subdir)
1226{
1227 struct maildir *md;
1228 struct mh_sequences mhs;
1229 struct maildir **last;
1230 struct mh_data *data;
1231 int count;
1232 char msgbuf[STRING];
1233 progress_t progress;
1234
1235 memset (&mhs, 0, sizeof (mhs));
1236 if (!ctx->quiet)
1237 {
1238 snprintf (msgbuf, sizeof (msgbuf), _("Scanning %s..."), ctx->path);
1239 mutt_progress_init (&progress, msgbuf, MUTT_PROGRESS_MSG, ReadInc, 0);
1240 }
1241
1242 if (!ctx->data)
1243 {
1244 ctx->data = safe_calloc(sizeof (struct mh_data), 1);
1245 }
1246 data = mh_data (ctx);
1247
1248 maildir_update_mtime (ctx);
1249
1250 md = NULL;
1251 last = &md;
1252 count = 0;
1253 if (maildir_parse_dir (ctx, &last, subdir, &count, &progress) == -1)
1254 return -1;
1255
1256 if (!ctx->quiet)
1257 {
1258 snprintf (msgbuf, sizeof (msgbuf), _("Reading %s..."), ctx->path);
1259 mutt_progress_init (&progress, msgbuf, MUTT_PROGRESS_MSG, ReadInc, count);
1260 }
1261 maildir_delayed_parsing (ctx, &md, &progress);
1262
1263 if (ctx->magic == MUTT_MH)
1264 {
1265 if (mh_read_sequences (&mhs, ctx->path) < 0)
1266 {
1267 maildir_free_maildir (&md);
1268 return -1;
1269 }
1270 mh_update_maildir (md, &mhs);
1271 mhs_free_sequences (&mhs);
1272 }
1273
1274 maildir_move_to_context (ctx, &md);
1275
1276 if (!data->mh_umask)
1277 data->mh_umask = mh_umask (ctx);
1278
1279 return 0;
1280}
1281
1282/* read a maildir style mailbox */
1283static int maildir_read_dir (CONTEXT * ctx)
1284{
1285 /* maildir looks sort of like MH, except that there are two subdirectories
1286 * of the main folder path from which to read messages
1287 */
1288 if (mh_read_dir (ctx, "new") == -1 || mh_read_dir (ctx, "cur") == -1)
1289 return (-1);
1290
1291 return 0;
1292}
1293
1294static int maildir_open_mailbox (CONTEXT *ctx)
1295{
1296 return maildir_read_dir (ctx);
1297}
1298
1299static int maildir_open_mailbox_append (CONTEXT *ctx, int flags)
1300{
1301 char tmp[_POSIX_PATH_MAX];
1302
1303 if (flags & MUTT_APPENDNEW)
1304 {
1305 if (mkdir (ctx->path, S_IRWXU))
1306 {
1307 mutt_perror (ctx->path);
1308 return (-1);
1309 }
1310
1311 snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
1312 if (mkdir (tmp, S_IRWXU))
1313 {
1314 mutt_perror (tmp);
1315 rmdir (ctx->path);
1316 return (-1);
1317 }
1318
1319 snprintf (tmp, sizeof (tmp), "%s/new", ctx->path);
1320 if (mkdir (tmp, S_IRWXU))
1321 {
1322 mutt_perror (tmp);
1323 snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
1324 rmdir (tmp);
1325 rmdir (ctx->path);
1326 return (-1);
1327 }
1328
1329 snprintf (tmp, sizeof (tmp), "%s/tmp", ctx->path);
1330 if (mkdir (tmp, S_IRWXU))
1331 {
1332 mutt_perror (tmp);
1333 snprintf (tmp, sizeof (tmp), "%s/cur", ctx->path);
1334 rmdir (tmp);
1335 snprintf (tmp, sizeof (tmp), "%s/new", ctx->path);
1336 rmdir (tmp);
1337 rmdir (ctx->path);
1338 return (-1);
1339 }
1340 }
1341
1342 return 0;
1343}
1344
1345static int mh_open_mailbox (CONTEXT *ctx)
1346{
1347 return mh_read_dir (ctx, NULL);
1348}
1349
1350static int mh_open_mailbox_append (CONTEXT *ctx, int flags)
1351{
1352 char tmp[_POSIX_PATH_MAX];
1353 int i;
1354
1355 if (flags & MUTT_APPENDNEW)
1356 {
1357 if (mkdir (ctx->path, S_IRWXU))
1358 {
1359 mutt_perror (ctx->path);
1360 return (-1);
1361 }
1362
1363 snprintf (tmp, sizeof (tmp), "%s/.mh_sequences", ctx->path);
1364 if ((i = creat (tmp, S_IRWXU)) == -1)
1365 {
1366 mutt_perror (tmp);
1367 rmdir (ctx->path);
1368 return (-1);
1369 }
1370 close (i);
1371 }
1372
1373 return 0;
1374}
1375
1376
1377/*
1378 * Open a new (temporary) message in an MH folder.
1379 */
1380
1381static int mh_open_new_message (MESSAGE * msg, CONTEXT * dest, HEADER * hdr)
1382{
1383 return mh_mkstemp (dest, &msg->fp, &msg->path);
1384}
1385
1386static int ch_compar (const void *a, const void *b)
1387{
1388 return (int)( *((const char *) a) - *((const char *) b));
1389}
1390
1391static void maildir_flags (char *dest, size_t destlen, HEADER * hdr)
1392{
1393 *dest = '\0';
1394
1395 /*
1396 * The maildir specification requires that all files in the cur
1397 * subdirectory have the :unique string appeneded, regardless of whether
1398 * or not there are any flags. If .old is set, we know that this message
1399 * will end up in the cur directory, so we include it in the following
1400 * test even though there is no associated flag.
1401 */
1402
1403 if (hdr && (hdr->flagged || hdr->replied || hdr->read || hdr->deleted || hdr->old || hdr->maildir_flags))
1404 {
1405 char tmp[LONG_STRING];
1406 snprintf (tmp, sizeof (tmp),
1407 "%s%s%s%s%s",
1408 hdr->flagged ? "F" : "",
1409 hdr->replied ? "R" : "",
1410 hdr->read ? "S" : "", hdr->deleted ? "T" : "",
1411 NONULL(hdr->maildir_flags));
1412 if (hdr->maildir_flags)
1413 qsort (tmp, strlen (tmp), 1, ch_compar);
1414 snprintf (dest, destlen, ":2,%s", tmp);
1415 }
1416}
1417
1418static int maildir_mh_open_message (CONTEXT *ctx, MESSAGE *msg, int msgno,
1419 int is_maildir)
1420{
1421 HEADER *cur = ctx->hdrs[msgno];
1422 char path[_POSIX_PATH_MAX];
1423
1424 snprintf (path, sizeof (path), "%s/%s", ctx->path, cur->path);
1425
1426 msg->fp = fopen (path, "r");
1427 if (msg->fp == NULL && errno == ENOENT && is_maildir)
1428 msg->fp = maildir_open_find_message (ctx->path, cur->path);
1429
1430 if (!msg->fp)
1431 {
1432 mutt_perror (path);
1433 dprint (1, (debugfile, "maildir_mh_open_message: fopen: %s: %s (errno %d).\n",
1434 path, strerror (errno), errno));
1435 return -1;
1436 }
1437
1438 return 0;
1439}
1440
1441static int maildir_open_message (CONTEXT *ctx, MESSAGE *msg, int msgno)
1442{
1443 return maildir_mh_open_message (ctx, msg, msgno, 1);
1444}
1445
1446static int mh_open_message (CONTEXT *ctx, MESSAGE *msg, int msgno)
1447{
1448 return maildir_mh_open_message (ctx, msg, msgno, 0);
1449}
1450
1451static int mh_close_message (CONTEXT *ctx, MESSAGE *msg)
1452{
1453 return safe_fclose (&msg->fp);
1454}
1455
1456/*
1457 * Open a new (temporary) message in a maildir folder.
1458 *
1459 * Note that this uses _almost_ the maildir file name format, but
1460 * with a {cur,new} prefix.
1461 *
1462 */
1463
1464static int maildir_open_new_message (MESSAGE * msg, CONTEXT * dest, HEADER * hdr)
1465{
1466 int fd;
1467 char path[_POSIX_PATH_MAX];
1468 char suffix[16];
1469 char subdir[16];
1470 mode_t omask;
1471
1472 if (hdr)
1473 {
1474 short deleted = hdr->deleted;
1475 hdr->deleted = 0;
1476
1477 maildir_flags (suffix, sizeof (suffix), hdr);
1478
1479 hdr->deleted = deleted;
1480 }
1481 else
1482 *suffix = '\0';
1483
1484 if (hdr && (hdr->read || hdr->old))
1485 strfcpy (subdir, "cur", sizeof (subdir));
1486 else
1487 strfcpy (subdir, "new", sizeof (subdir));
1488
1489 omask = umask (mh_umask (dest));
1490 FOREVER
1491 {
1492 snprintf (path, _POSIX_PATH_MAX, "%s/tmp/%s.%lld.%u_%d.%s%s",
1493 dest->path, subdir, (long long)time (NULL), (unsigned int)getpid (),
1494 Counter++, NONULL (Hostname), suffix);
1495
1496 dprint (2, (debugfile, "maildir_open_new_message (): Trying %s.\n",
1497 path));
1498
1499 if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0666)) == -1)
1500 {
1501 if (errno != EEXIST)
1502 {
1503 umask (omask);
1504 mutt_perror (path);
1505 return -1;
1506 }
1507 }
1508 else
1509 {
1510 dprint (2, (debugfile, "maildir_open_new_message (): Success.\n"));
1511 msg->path = safe_strdup (path);
1512 break;
1513 }
1514 }
1515 umask (omask);
1516
1517 if ((msg->fp = fdopen (fd, "w")) == NULL)
1518 {
1519 FREE (&msg->path);
1520 close (fd);
1521 unlink (path);
1522 return (-1);
1523 }
1524
1525 return 0;
1526}
1527
1528
1529
1530/*
1531 * Commit a message to a maildir folder.
1532 *
1533 * msg->path contains the file name of a file in tmp/. We take the
1534 * flags from this file's name.
1535 *
1536 * ctx is the mail folder we commit to.
1537 *
1538 * hdr is a header structure to which we write the message's new
1539 * file name. This is used in the mh and maildir folder synch
1540 * routines. When this routine is invoked from mx_commit_message,
1541 * hdr is NULL.
1542 *
1543 * msg->path looks like this:
1544 *
1545 * tmp/{cur,new}.mutt-HOSTNAME-PID-COUNTER:flags
1546 *
1547 * See also maildir_open_new_message().
1548 *
1549 */
1550
1551static int _maildir_commit_message (CONTEXT * ctx, MESSAGE * msg, HEADER * hdr)
1552{
1553 char subdir[4];
1554 char suffix[16];
1555 char path[_POSIX_PATH_MAX];
1556 char full[_POSIX_PATH_MAX];
1557 char *s;
1558
1559 if (safe_fsync_close (&msg->fp))
1560 {
1561 mutt_perror (_("Could not flush message to disk"));
1562 return -1;
1563 }
1564
1565 /* extract the subdir */
1566 s = strrchr (msg->path, '/') + 1;
1567 strfcpy (subdir, s, 4);
1568
1569 /* extract the flags */
1570 if ((s = strchr (s, ':')))
1571 strfcpy (suffix, s, sizeof (suffix));
1572 else
1573 suffix[0] = '\0';
1574
1575 /* construct a new file name. */
1576 FOREVER
1577 {
1578 snprintf (path, _POSIX_PATH_MAX, "%s/%lld.%u_%d.%s%s", subdir,
1579 (long long)time (NULL), (unsigned int)getpid (), Counter++,
1580 NONULL (Hostname), suffix);
1581 snprintf (full, _POSIX_PATH_MAX, "%s/%s", ctx->path, path);
1582
1583 dprint (2, (debugfile, "_maildir_commit_message (): renaming %s to %s.\n",
1584 msg->path, full));
1585
1586 if (safe_rename (msg->path, full) == 0)
1587 {
1588 if (hdr)
1589 mutt_str_replace (&hdr->path, path);
1590 FREE (&msg->path);
1591
1592 /*
1593 * Adjust the mtime on the file to match the time at which this
1594 * message was received. Currently this is only set when copying
1595 * messages between mailboxes, so we test to ensure that it is
1596 * actually set.
1597 */
1598 if (msg->received)
1599 {
1600 struct utimbuf ut;
1601
1602 ut.actime = msg->received;
1603 ut.modtime = msg->received;
1604 if (utime (full, &ut))
1605 {
1606 mutt_perror (_("_maildir_commit_message(): unable to set time on file"));
1607 return -1;
1608 }
1609 }
1610
1611 return 0;
1612 }
1613 else if (errno != EEXIST)
1614 {
1615 mutt_perror (ctx->path);
1616 return -1;
1617 }
1618 }
1619}
1620
1621static int maildir_commit_message (CONTEXT * ctx, MESSAGE * msg)
1622{
1623 return _maildir_commit_message (ctx, msg, NULL);
1624}
1625
1626/*
1627 * commit a message to an MH folder.
1628 *
1629 */
1630
1631
1632static int _mh_commit_message (CONTEXT * ctx, MESSAGE * msg, HEADER * hdr,
1633 short updseq)
1634{
1635 DIR *dirp;
1636 struct dirent *de;
1637 char *cp, *dep;
1638 unsigned int n, hi = 0;
1639 char path[_POSIX_PATH_MAX];
1640 char tmp[16];
1641
1642 if (safe_fsync_close (&msg->fp))
1643 {
1644 mutt_perror (_("Could not flush message to disk"));
1645 return -1;
1646 }
1647
1648 if ((dirp = opendir (ctx->path)) == NULL)
1649 {
1650 mutt_perror (ctx->path);
1651 return (-1);
1652 }
1653
1654 /* figure out what the next message number is */
1655 while ((de = readdir (dirp)) != NULL)
1656 {
1657 dep = de->d_name;
1658 if (*dep == ',')
1659 dep++;
1660 cp = dep;
1661 while (*cp)
1662 {
1663 if (!isdigit ((unsigned char) *cp))
1664 break;
1665 cp++;
1666 }
1667 if (!*cp)
1668 {
1669 n = atoi (dep);
1670 if (n > hi)
1671 hi = n;
1672 }
1673 }
1674 closedir (dirp);
1675
1676 /*
1677 * Now try to rename the file to the proper name.
1678 *
1679 * Note: We may have to try multiple times, until we find a free
1680 * slot.
1681 */
1682
1683 FOREVER
1684 {
1685 hi++;
1686 snprintf (tmp, sizeof (tmp), "%d", hi);
1687 snprintf (path, sizeof (path), "%s/%s", ctx->path, tmp);
1688 if (safe_rename (msg->path, path) == 0)
1689 {
1690 if (hdr)
1691 mutt_str_replace (&hdr->path, tmp);
1692 FREE (&msg->path);
1693 break;
1694 }
1695 else if (errno != EEXIST)
1696 {
1697 mutt_perror (ctx->path);
1698 return -1;
1699 }
1700 }
1701 if (updseq)
1702 mh_sequences_add_one (ctx, hi, !msg->flags.read, msg->flags.flagged,
1703 msg->flags.replied);
1704 return 0;
1705}
1706
1707static int mh_commit_message (CONTEXT * ctx, MESSAGE * msg)
1708{
1709 return _mh_commit_message (ctx, msg, NULL, 1);
1710}
1711
1712
1713/* Sync a message in an MH folder.
1714 *
1715 * This code is also used for attachment deletion in maildir
1716 * folders.
1717 */
1718
1719static int mh_rewrite_message (CONTEXT * ctx, int msgno)
1720{
1721 HEADER *h = ctx->hdrs[msgno];
1722 MESSAGE *dest;
1723
1724 int rc;
1725 short restore = 1;
1726 char oldpath[_POSIX_PATH_MAX];
1727 char newpath[_POSIX_PATH_MAX];
1728 char partpath[_POSIX_PATH_MAX];
1729
1730 long old_body_offset = h->content->offset;
1731 long old_body_length = h->content->length;
1732 long old_hdr_lines = h->lines;
1733
1734 if ((dest = mx_open_new_message (ctx, h, 0)) == NULL)
1735 return -1;
1736
1737 if ((rc = mutt_copy_message (dest->fp, ctx, h,
1738 MUTT_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN)) == 0)
1739 {
1740 snprintf (oldpath, _POSIX_PATH_MAX, "%s/%s", ctx->path, h->path);
1741 strfcpy (partpath, h->path, _POSIX_PATH_MAX);
1742
1743 if (ctx->magic == MUTT_MAILDIR)
1744 rc = _maildir_commit_message (ctx, dest, h);
1745 else
1746 rc = _mh_commit_message (ctx, dest, h, 0);
1747
1748 mx_close_message (ctx, &dest);
1749
1750 if (rc == 0)
1751 {
1752 unlink (oldpath);
1753 restore = 0;
1754 }
1755
1756 /*
1757 * Try to move the new message to the old place.
1758 * (MH only.)
1759 *
1760 * This is important when we are just updating flags.
1761 *
1762 * Note that there is a race condition against programs which
1763 * use the first free slot instead of the maximum message
1764 * number. Mutt does _not_ behave like this.
1765 *
1766 * Anyway, if this fails, the message is in the folder, so
1767 * all what happens is that a concurrently running mutt will
1768 * lose flag modifications.
1769 */
1770
1771 if (ctx->magic == MUTT_MH && rc == 0)
1772 {
1773 snprintf (newpath, _POSIX_PATH_MAX, "%s/%s", ctx->path, h->path);
1774 if ((rc = safe_rename (newpath, oldpath)) == 0)
1775 mutt_str_replace (&h->path, partpath);
1776 }
1777 }
1778 else
1779 mx_close_message (ctx, &dest);
1780
1781 if (rc == -1 && restore)
1782 {
1783 h->content->offset = old_body_offset;
1784 h->content->length = old_body_length;
1785 h->lines = old_hdr_lines;
1786 }
1787
1788 mutt_free_body (&h->content->parts);
1789 return rc;
1790}
1791
1792static int mh_sync_message (CONTEXT * ctx, int msgno)
1793{
1794 HEADER *h = ctx->hdrs[msgno];
1795
1796 if (h->attach_del ||
1797 (h->env && (h->env->refs_changed || h->env->irt_changed)))
1798 if (mh_rewrite_message (ctx, msgno) != 0)
1799 return -1;
1800
1801 return 0;
1802}
1803
1804static int maildir_sync_message (CONTEXT * ctx, int msgno)
1805{
1806 HEADER *h = ctx->hdrs[msgno];
1807
1808 if (h->attach_del ||
1809 (h->env && (h->env->refs_changed || h->env->irt_changed)))
1810 {
1811 /* when doing attachment deletion/rethreading, fall back to the MH case. */
1812 if (mh_rewrite_message (ctx, msgno) != 0)
1813 return (-1);
1814 }
1815 else
1816 {
1817 /* we just have to rename the file. */
1818
1819 char newpath[_POSIX_PATH_MAX];
1820 char partpath[_POSIX_PATH_MAX];
1821 char fullpath[_POSIX_PATH_MAX];
1822 char oldpath[_POSIX_PATH_MAX];
1823 char suffix[16];
1824 char *p;
1825
1826 if ((p = strrchr (h->path, '/')) == NULL)
1827 {
1828 dprint (1,
1829 (debugfile,
1830 "maildir_sync_message: %s: unable to find subdir!\n",
1831 h->path));
1832 return (-1);
1833 }
1834 p++;
1835 strfcpy (newpath, p, sizeof (newpath));
1836
1837 /* kill the previous flags */
1838 if ((p = strchr (newpath, ':')) != NULL)
1839 *p = 0;
1840
1841 maildir_flags (suffix, sizeof (suffix), h);
1842
1843 snprintf (partpath, sizeof (partpath), "%s/%s%s",
1844 (h->read || h->old) ? "cur" : "new", newpath, suffix);
1845 snprintf (fullpath, sizeof (fullpath), "%s/%s", ctx->path, partpath);
1846 snprintf (oldpath, sizeof (oldpath), "%s/%s", ctx->path, h->path);
1847
1848 if (mutt_strcmp (fullpath, oldpath) == 0)
1849 {
1850 /* message hasn't really changed */
1851 return 0;
1852 }
1853
1854 /* record that the message is possibly marked as trashed on disk */
1855 h->trash = h->deleted;
1856
1857 if (rename (oldpath, fullpath) != 0)
1858 {
1859 mutt_perror ("rename");
1860 return (-1);
1861 }
1862 mutt_str_replace (&h->path, partpath);
1863 }
1864 return (0);
1865}
1866
1867int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
1868{
1869 char path[_POSIX_PATH_MAX], tmp[_POSIX_PATH_MAX];
1870 int i, j;
1871#if USE_HCACHE
1872 header_cache_t *hc = NULL;
1873#endif /* USE_HCACHE */
1874 char msgbuf[STRING];
1875 progress_t progress;
1876
1877 if (ctx->magic == MUTT_MH)
1878 i = mh_check_mailbox (ctx, index_hint);
1879 else
1880 i = maildir_check_mailbox (ctx, index_hint);
1881
1882 if (i != 0)
1883 return i;
1884
1885#if USE_HCACHE
1886 if (ctx->magic == MUTT_MAILDIR || ctx->magic == MUTT_MH)
1887 hc = mutt_hcache_open(HeaderCache, ctx->path, NULL);
1888#endif /* USE_HCACHE */
1889
1890 if (!ctx->quiet)
1891 {
1892 snprintf (msgbuf, sizeof (msgbuf), _("Writing %s..."), ctx->path);
1893 mutt_progress_init (&progress, msgbuf, MUTT_PROGRESS_MSG, WriteInc, ctx->msgcount);
1894 }
1895
1896 for (i = 0; i < ctx->msgcount; i++)
1897 {
1898 if (!ctx->quiet)
1899 mutt_progress_update (&progress, i, -1);
1900
1901 if (ctx->hdrs[i]->deleted
1902 && (ctx->magic != MUTT_MAILDIR || !option (OPTMAILDIRTRASH)))
1903 {
1904 snprintf (path, sizeof (path), "%s/%s", ctx->path, ctx->hdrs[i]->path);
1905 if (ctx->magic == MUTT_MAILDIR
1906 || (option (OPTMHPURGE) && ctx->magic == MUTT_MH))
1907 {
1908#if USE_HCACHE
1909 if (ctx->magic == MUTT_MAILDIR)
1910 mutt_hcache_delete (hc, ctx->hdrs[i]->path + 3, &maildir_hcache_keylen);
1911 else if (ctx->magic == MUTT_MH)
1912 mutt_hcache_delete (hc, ctx->hdrs[i]->path, strlen);
1913#endif /* USE_HCACHE */
1914 unlink (path);
1915 }
1916 else if (ctx->magic == MUTT_MH)
1917 {
1918 /* MH just moves files out of the way when you delete them */
1919 if (*ctx->hdrs[i]->path != ',')
1920 {
1921 snprintf (tmp, sizeof (tmp), "%s/,%s", ctx->path,
1922 ctx->hdrs[i]->path);
1923 unlink (tmp);
1924 rename (path, tmp);
1925 }
1926
1927 }
1928 }
1929 else if (ctx->hdrs[i]->changed || ctx->hdrs[i]->attach_del ||
1930 (ctx->magic == MUTT_MAILDIR
1931 && (option (OPTMAILDIRTRASH) || ctx->hdrs[i]->trash)
1932 && (ctx->hdrs[i]->deleted != ctx->hdrs[i]->trash)))
1933 {
1934 if (ctx->magic == MUTT_MAILDIR)
1935 {
1936 if (maildir_sync_message (ctx, i) == -1)
1937 goto err;
1938 }
1939 else
1940 {
1941 if (mh_sync_message (ctx, i) == -1)
1942 goto err;
1943 }
1944 }
1945
1946#if USE_HCACHE
1947 if (ctx->hdrs[i]->changed)
1948 {
1949 if (ctx->magic == MUTT_MAILDIR)
1950 mutt_hcache_store (hc, ctx->hdrs[i]->path + 3, ctx->hdrs[i],
1951 0, &maildir_hcache_keylen, MUTT_GENERATE_UIDVALIDITY);
1952 else if (ctx->magic == MUTT_MH)
1953 mutt_hcache_store (hc, ctx->hdrs[i]->path, ctx->hdrs[i], 0, strlen, MUTT_GENERATE_UIDVALIDITY);
1954 }
1955#endif
1956
1957 }
1958
1959#if USE_HCACHE
1960 if (ctx->magic == MUTT_MAILDIR || ctx->magic == MUTT_MH)
1961 mutt_hcache_close (hc);
1962#endif /* USE_HCACHE */
1963
1964 if (ctx->magic == MUTT_MH)
1965 mh_update_sequences (ctx);
1966
1967 /* XXX race condition? */
1968
1969 maildir_update_mtime (ctx);
1970
1971 /* adjust indices */
1972
1973 if (ctx->deleted)
1974 {
1975 for (i = 0, j = 0; i < ctx->msgcount; i++)
1976 {
1977 if (!ctx->hdrs[i]->deleted
1978 || (ctx->magic == MUTT_MAILDIR && option (OPTMAILDIRTRASH)))
1979 ctx->hdrs[i]->index = j++;
1980 }
1981 }
1982
1983 return 0;
1984
1985err:
1986#if USE_HCACHE
1987 if (ctx->magic == MUTT_MAILDIR || ctx->magic == MUTT_MH)
1988 mutt_hcache_close (hc);
1989#endif /* USE_HCACHE */
1990 return -1;
1991}
1992
1993static char *maildir_canon_filename (char *dest, const char *src, size_t l)
1994{
1995 char *t, *u;
1996
1997 if ((t = strrchr (src, '/')))
1998 src = t + 1;
1999
2000 strfcpy (dest, src, l);
2001 if ((u = strrchr (dest, ':')))
2002 *u = '\0';
2003
2004 return dest;
2005}
2006
2007static void maildir_update_tables (CONTEXT *ctx, int *index_hint)
2008{
2009 short old_sort;
2010 int old_count;
2011 int i, j;
2012
2013 if (Sort != SORT_ORDER)
2014 {
2015 old_sort = Sort;
2016 Sort = SORT_ORDER;
2017 mutt_sort_headers (ctx, 1);
2018 Sort = old_sort;
2019 }
2020
2021 old_count = ctx->msgcount;
2022 for (i = 0, j = 0; i < old_count; i++)
2023 {
2024 if (ctx->hdrs[i]->active && index_hint && *index_hint == i)
2025 *index_hint = j;
2026
2027 if (ctx->hdrs[i]->active)
2028 ctx->hdrs[i]->index = j++;
2029 }
2030
2031 mx_update_tables (ctx, 0);
2032 mutt_clear_threads (ctx);
2033}
2034
2035static void maildir_update_flags (CONTEXT *ctx, HEADER *o, HEADER *n)
2036{
2037 /* save the global state here so we can reset it at the
2038 * end of list block if required.
2039 */
2040 int context_changed = ctx->changed;
2041
2042 /* user didn't modify this message. alter the flags to match the
2043 * current state on disk. This may not actually do
2044 * anything. mutt_set_flag() will just ignore the call if the status
2045 * bits are already properly set, but it is still faster not to pass
2046 * through it */
2047 if (o->flagged != n->flagged)
2048 mutt_set_flag (ctx, o, MUTT_FLAG, n->flagged);
2049 if (o->replied != n->replied)
2050 mutt_set_flag (ctx, o, MUTT_REPLIED, n->replied);
2051 if (o->read != n->read)
2052 mutt_set_flag (ctx, o, MUTT_READ, n->read);
2053 if (o->old != n->old)
2054 mutt_set_flag (ctx, o, MUTT_OLD, n->old);
2055
2056 /* mutt_set_flag() will set this, but we don't need to
2057 * sync the changes we made because we just updated the
2058 * context to match the current on-disk state of the
2059 * message.
2060 */
2061 o->changed = 0;
2062
2063 /* if the mailbox was not modified before we made these
2064 * changes, unset the changed flag since nothing needs to
2065 * be synchronized.
2066 */
2067 if (!context_changed)
2068 ctx->changed = 0;
2069}
2070
2071
2072/* This function handles arrival of new mail and reopening of
2073 * maildir folders. The basic idea here is we check to see if either
2074 * the new or cur subdirectories have changed, and if so, we scan them
2075 * for the list of files. We check for newly added messages, and
2076 * then merge the flags messages we already knew about. We don't treat
2077 * either subdirectory differently, as mail could be copied directly into
2078 * the cur directory from another agent.
2079 */
2080static int maildir_check_mailbox (CONTEXT * ctx, int *index_hint)
2081{
2082 struct stat st_new; /* status of the "new" subdirectory */
2083 struct stat st_cur; /* status of the "cur" subdirectory */
2084 char buf[_POSIX_PATH_MAX];
2085 int changed = 0; /* bitmask representing which subdirectories
2086 have changed. 0x1 = new, 0x2 = cur */
2087 int occult = 0; /* messages were removed from the mailbox */
2088 int have_new = 0; /* messages were added to the mailbox */
2089 struct maildir *md; /* list of messages in the mailbox */
2090 struct maildir **last, *p;
2091 int i;
2092 HASH *fnames; /* hash table for quickly looking up the base filename
2093 for a maildir message */
2094 struct mh_data *data = mh_data (ctx);
2095
2096 /* XXX seems like this check belongs in mx_check_mailbox()
2097 * rather than here.
2098 */
2099 if (!option (OPTCHECKNEW))
2100 return 0;
2101
2102 snprintf (buf, sizeof (buf), "%s/new", ctx->path);
2103 if (stat (buf, &st_new) == -1)
2104 return -1;
2105
2106 snprintf (buf, sizeof (buf), "%s/cur", ctx->path);
2107 if (stat (buf, &st_cur) == -1)
2108 return -1;
2109
2110 /* determine which subdirectories need to be scanned */
2111 if (st_new.st_mtime > ctx->mtime)
2112 changed = 1;
2113 if (st_cur.st_mtime > data->mtime_cur)
2114 changed |= 2;
2115
2116 if (!changed)
2117 return 0; /* nothing to do */
2118
2119 /* update the modification times on the mailbox */
2120 data->mtime_cur = st_cur.st_mtime;
2121 ctx->mtime = st_new.st_mtime;
2122
2123 /* do a fast scan of just the filenames in
2124 * the subdirectories that have changed.
2125 */
2126 md = NULL;
2127 last = &md;
2128 if (changed & 1)
2129 maildir_parse_dir (ctx, &last, "new", NULL, NULL);
2130 if (changed & 2)
2131 maildir_parse_dir (ctx, &last, "cur", NULL, NULL);
2132
2133 /* we create a hash table keyed off the canonical (sans flags) filename
2134 * of each message we scanned. This is used in the loop over the
2135 * existing messages below to do some correlation.
2136 */
2137 fnames = hash_create (1031, 0);
2138
2139 for (p = md; p; p = p->next)
2140 {
2141 maildir_canon_filename (buf, p->h->path, sizeof (buf));
2142 p->canon_fname = safe_strdup (buf);
2143 hash_insert (fnames, p->canon_fname, p, 0);
2144 }
2145
2146 /* check for modifications and adjust flags */
2147 for (i = 0; i < ctx->msgcount; i++)
2148 {
2149 ctx->hdrs[i]->active = 0;
2150 maildir_canon_filename (buf, ctx->hdrs[i]->path, sizeof (buf));
2151 p = hash_find (fnames, buf);
2152 if (p && p->h)
2153 {
2154 /* message already exists, merge flags */
2155 ctx->hdrs[i]->active = 1;
2156
2157 /* check to see if the message has moved to a different
2158 * subdirectory. If so, update the associated filename.
2159 */
2160 if (mutt_strcmp (ctx->hdrs[i]->path, p->h->path))
2161 mutt_str_replace (&ctx->hdrs[i]->path, p->h->path);
2162
2163 /* if the user hasn't modified the flags on this message, update
2164 * the flags we just detected.
2165 */
2166 if (!ctx->hdrs[i]->changed)
2167 maildir_update_flags (ctx, ctx->hdrs[i], p->h);
2168
2169 if (ctx->hdrs[i]->deleted == ctx->hdrs[i]->trash)
2170 ctx->hdrs[i]->deleted = p->h->deleted;
2171 ctx->hdrs[i]->trash = p->h->trash;
2172
2173 /* this is a duplicate of an existing header, so remove it */
2174 mutt_free_header (&p->h);
2175 }
2176 /* This message was not in the list of messages we just scanned.
2177 * Check to see if we have enough information to know if the
2178 * message has disappeared out from underneath us.
2179 */
2180 else if (((changed & 1) && (!strncmp (ctx->hdrs[i]->path, "new/", 4))) ||
2181 ((changed & 2) && (!strncmp (ctx->hdrs[i]->path, "cur/", 4))))
2182 {
2183 /* This message disappeared, so we need to simulate a "reopen"
2184 * event. We know it disappeared because we just scanned the
2185 * subdirectory it used to reside in.
2186 */
2187 occult = 1;
2188 }
2189 else
2190 {
2191 /* This message resides in a subdirectory which was not
2192 * modified, so we assume that it is still present and
2193 * unchanged.
2194 */
2195 ctx->hdrs[i]->active = 1;
2196 }
2197 }
2198
2199 /* destroy the file name hash */
2200 hash_destroy (&fnames, NULL);
2201
2202 /* If we didn't just get new mail, update the tables. */
2203 if (occult)
2204 maildir_update_tables (ctx, index_hint);
2205
2206 /* do any delayed parsing we need to do. */
2207 maildir_delayed_parsing (ctx, &md, NULL);
2208
2209 /* Incorporate new messages */
2210 have_new = maildir_move_to_context (ctx, &md);
2211
2212 return occult ? MUTT_REOPENED : (have_new ? MUTT_NEW_MAIL : 0);
2213}
2214
2215/*
2216 * This function handles arrival of new mail and reopening of
2217 * mh/maildir folders. Things are getting rather complex because we
2218 * don't have a well-defined "mailbox order", so the tricks from
2219 * mbox.c and mx.c won't work here.
2220 *
2221 * Don't change this code unless you _really_ understand what
2222 * happens.
2223 *
2224 */
2225
2226static int mh_check_mailbox (CONTEXT * ctx, int *index_hint)
2227{
2228 char buf[_POSIX_PATH_MAX];
2229 struct stat st, st_cur;
2230 short modified = 0, have_new = 0, occult = 0;
2231 struct maildir *md, *p;
2232 struct maildir **last = NULL;
2233 struct mh_sequences mhs;
2234 HASH *fnames;
2235 int i;
2236 struct mh_data *data = mh_data (ctx);
2237
2238 if (!option (OPTCHECKNEW))
2239 return 0;
2240
2241 strfcpy (buf, ctx->path, sizeof (buf));
2242 if (stat (buf, &st) == -1)
2243 return -1;
2244
2245 /* create .mh_sequences when there isn't one. */
2246 snprintf (buf, sizeof (buf), "%s/.mh_sequences", ctx->path);
2247 if ((i = stat (buf, &st_cur)) == -1 && errno == ENOENT)
2248 {
2249 char *tmp;
2250 FILE *fp = NULL;
2251
2252 if (mh_mkstemp (ctx, &fp, &tmp) == 0)
2253 {
2254 safe_fclose (&fp);
2255 if (safe_rename (tmp, buf) == -1)
2256 unlink (tmp);
2257 FREE (&tmp);
2258 }
2259 }
2260
2261 if (i == -1 && stat (buf, &st_cur) == -1)
2262 modified = 1;
2263
2264 if (st.st_mtime > ctx->mtime || st_cur.st_mtime > data->mtime_cur)
2265 modified = 1;
2266
2267 if (!modified)
2268 return 0;
2269
2270 data->mtime_cur = st_cur.st_mtime;
2271 ctx->mtime = st.st_mtime;
2272
2273 memset (&mhs, 0, sizeof (mhs));
2274
2275 md = NULL;
2276 last = &md;
2277
2278 maildir_parse_dir (ctx, &last, NULL, NULL, NULL);
2279 maildir_delayed_parsing (ctx, &md, NULL);
2280
2281 if (mh_read_sequences (&mhs, ctx->path) < 0)
2282 return -1;
2283 mh_update_maildir (md, &mhs);
2284 mhs_free_sequences (&mhs);
2285
2286 /* check for modifications and adjust flags */
2287 fnames = hash_create (1031, 0);
2288
2289 for (p = md; p; p = p->next)
2290 {
2291 /* the hash key must survive past the header, which is freed below. */
2292 p->canon_fname = safe_strdup (p->h->path);
2293 hash_insert (fnames, p->canon_fname, p, 0);
2294 }
2295
2296 for (i = 0; i < ctx->msgcount; i++)
2297 {
2298 ctx->hdrs[i]->active = 0;
2299
2300 if ((p = hash_find (fnames, ctx->hdrs[i]->path)) && p->h &&
2301 (mbox_strict_cmp_headers (ctx->hdrs[i], p->h)))
2302 {
2303 ctx->hdrs[i]->active = 1;
2304 /* found the right message */
2305 if (!ctx->hdrs[i]->changed)
2306 maildir_update_flags (ctx, ctx->hdrs[i], p->h);
2307
2308 mutt_free_header (&p->h);
2309 }
2310 else /* message has disappeared */
2311 occult = 1;
2312 }
2313
2314 /* destroy the file name hash */
2315
2316 hash_destroy (&fnames, NULL);
2317
2318 /* If we didn't just get new mail, update the tables. */
2319 if (occult)
2320 maildir_update_tables (ctx, index_hint);
2321
2322 /* Incorporate new messages */
2323 have_new = maildir_move_to_context (ctx, &md);
2324
2325 return occult ? MUTT_REOPENED : (have_new ? MUTT_NEW_MAIL : 0);
2326}
2327
2328
2329
2330
2331/*
2332 * These functions try to find a message in a maildir folder when it
2333 * has moved under our feet. Note that this code is rather expensive, but
2334 * then again, it's called rarely.
2335 */
2336
2337static FILE *_maildir_open_find_message (const char *folder, const char *unique,
2338 const char *subfolder)
2339{
2340 char dir[_POSIX_PATH_MAX];
2341 char tunique[_POSIX_PATH_MAX];
2342 char fname[_POSIX_PATH_MAX];
2343
2344 DIR *dp;
2345 struct dirent *de;
2346
2347 FILE *fp = NULL;
2348 int oe = ENOENT;
2349
2350 snprintf (dir, sizeof (dir), "%s/%s", folder, subfolder);
2351
2352 if ((dp = opendir (dir)) == NULL)
2353 {
2354 errno = ENOENT;
2355 return NULL;
2356 }
2357
2358 while ((de = readdir (dp)))
2359 {
2360 maildir_canon_filename (tunique, de->d_name, sizeof (tunique));
2361
2362 if (!mutt_strcmp (tunique, unique))
2363 {
2364 snprintf (fname, sizeof (fname), "%s/%s/%s", folder, subfolder,
2365 de->d_name);
2366 fp = fopen (fname, "r"); /* __FOPEN_CHECKED__ */
2367 oe = errno;
2368 break;
2369 }
2370 }
2371
2372 closedir (dp);
2373
2374 errno = oe;
2375 return fp;
2376}
2377
2378FILE *maildir_open_find_message (const char *folder, const char *msg)
2379{
2380 char unique[_POSIX_PATH_MAX];
2381 FILE *fp;
2382
2383 static unsigned int new_hits = 0, cur_hits = 0; /* simple dynamic optimization */
2384
2385 maildir_canon_filename (unique, msg, sizeof (unique));
2386
2387 if (
2388 (fp =
2389 _maildir_open_find_message (folder, unique,
2390 new_hits > cur_hits ? "new" : "cur"))
2391 || errno != ENOENT)
2392 {
2393 if (new_hits < UINT_MAX && cur_hits < UINT_MAX)
2394 {
2395 new_hits += (new_hits > cur_hits ? 1 : 0);
2396 cur_hits += (new_hits > cur_hits ? 0 : 1);
2397 }
2398
2399 return fp;
2400 }
2401 if (
2402 (fp =
2403 _maildir_open_find_message (folder, unique,
2404 new_hits > cur_hits ? "cur" : "new"))
2405 || errno != ENOENT)
2406 {
2407 if (new_hits < UINT_MAX && cur_hits < UINT_MAX)
2408 {
2409 new_hits += (new_hits > cur_hits ? 0 : 1);
2410 cur_hits += (new_hits > cur_hits ? 1 : 0);
2411 }
2412
2413 return fp;
2414 }
2415
2416 return NULL;
2417}
2418
2419
2420/*
2421 * Returns:
2422 * 1 if there are no messages in the mailbox
2423 * 0 if there are messages in the mailbox
2424 * -1 on error
2425 */
2426int maildir_check_empty (const char *path)
2427{
2428 DIR *dp;
2429 struct dirent *de;
2430 int r = 1; /* assume empty until we find a message */
2431 char realpath[_POSIX_PATH_MAX];
2432 int iter = 0;
2433
2434 /* Strategy here is to look for any file not beginning with a period */
2435
2436 do {
2437 /* we do "cur" on the first iteration since its more likely that we'll
2438 * find old messages without having to scan both subdirs
2439 */
2440 snprintf (realpath, sizeof (realpath), "%s/%s", path,
2441 iter == 0 ? "cur" : "new");
2442 if ((dp = opendir (realpath)) == NULL)
2443 return -1;
2444 while ((de = readdir (dp)))
2445 {
2446 if (*de->d_name != '.')
2447 {
2448 r = 0;
2449 break;
2450 }
2451 }
2452 closedir (dp);
2453 iter++;
2454 } while (r && iter < 2);
2455
2456 return r;
2457}
2458
2459/*
2460 * Returns:
2461 * 1 if there are no messages in the mailbox
2462 * 0 if there are messages in the mailbox
2463 * -1 on error
2464 */
2465int mh_check_empty (const char *path)
2466{
2467 DIR *dp;
2468 struct dirent *de;
2469 int r = 1; /* assume empty until we find a message */
2470
2471 if ((dp = opendir (path)) == NULL)
2472 return -1;
2473 while ((de = readdir (dp)))
2474 {
2475 if (mh_valid_message (de->d_name))
2476 {
2477 r = 0;
2478 break;
2479 }
2480 }
2481 closedir (dp);
2482
2483 return r;
2484}
2485
2486int mx_is_maildir (const char *path)
2487{
2488 char tmp[_POSIX_PATH_MAX];
2489 struct stat st;
2490
2491 snprintf (tmp, sizeof (tmp), "%s/cur", path);
2492 if (stat (tmp, &st) == 0 && S_ISDIR (st.st_mode))
2493 return 1;
2494 return 0;
2495}
2496
2497int mx_is_mh (const char *path)
2498{
2499 char tmp[_POSIX_PATH_MAX];
2500
2501 snprintf (tmp, sizeof (tmp), "%s/.mh_sequences", path);
2502 if (access (tmp, F_OK) == 0)
2503 return 1;
2504
2505 snprintf (tmp, sizeof (tmp), "%s/.xmhcache", path);
2506 if (access (tmp, F_OK) == 0)
2507 return 1;
2508
2509 snprintf (tmp, sizeof (tmp), "%s/.mew_cache", path);
2510 if (access (tmp, F_OK) == 0)
2511 return 1;
2512
2513 snprintf (tmp, sizeof (tmp), "%s/.mew-cache", path);
2514 if (access (tmp, F_OK) == 0)
2515 return 1;
2516
2517 snprintf (tmp, sizeof (tmp), "%s/.sylpheed_cache", path);
2518 if (access (tmp, F_OK) == 0)
2519 return 1;
2520
2521 /*
2522 * ok, this isn't an mh folder, but mh mode can be used to read
2523 * Usenet news from the spool. ;-)
2524 */
2525
2526 snprintf (tmp, sizeof (tmp), "%s/.overview", path);
2527 if (access (tmp, F_OK) == 0)
2528 return 1;
2529
2530 return 0;
2531}
2532
2533struct mx_ops mx_maildir_ops = {
2534 .open = maildir_open_mailbox,
2535 .open_append = maildir_open_mailbox_append,
2536 .close = mh_close_mailbox,
2537 .open_msg = maildir_open_message,
2538 .close_msg = mh_close_message,
2539 .commit_msg = maildir_commit_message,
2540 .open_new_msg = maildir_open_new_message,
2541 .check = maildir_check_mailbox,
2542 .sync = mh_sync_mailbox,
2543};
2544
2545struct mx_ops mx_mh_ops = {
2546 .open = mh_open_mailbox,
2547 .open_append = mh_open_mailbox_append,
2548 .close = mh_close_mailbox,
2549 .open_msg = mh_open_message,
2550 .close_msg = mh_close_message,
2551 .commit_msg = mh_commit_message,
2552 .open_new_msg = mh_open_new_message,
2553 .check = mh_check_mailbox,
2554 .sync = mh_sync_mailbox,
2555};