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