mutt stable branch with some hacks
at master 2555 lines 58 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 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};