mutt stable branch with some hacks
at jcs 1596 lines 37 kB view raw
1/* 2 * Copyright (C) 1996-2002,2010,2013 Michael R. Elkins <me@mutt.org> 3 * Copyright (C) 1999-2003 Thomas Roessler <roessler@does-not-exist.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 */ 19 20#if HAVE_CONFIG_H 21# include "config.h" 22#endif 23 24#include "mutt.h" 25#include "mx.h" 26#include "rfc2047.h" 27#include "sort.h" 28#include "mailbox.h" 29#include "copy.h" 30#include "keymap.h" 31#include "url.h" 32#ifdef USE_SIDEBAR 33#include "sidebar.h" 34#endif 35 36#ifdef USE_COMPRESSED 37#include "compress.h" 38#endif 39 40#ifdef USE_IMAP 41#include "imap.h" 42#endif 43 44#ifdef USE_POP 45#include "pop.h" 46#endif 47 48#include "buffy.h" 49 50#ifdef USE_DOTLOCK 51#include "dotlock.h" 52#endif 53 54#include "mutt_crypt.h" 55 56#include <dirent.h> 57#include <fcntl.h> 58#include <sys/file.h> 59#include <sys/stat.h> 60#include <errno.h> 61#include <unistd.h> 62#include <stdlib.h> 63#include <string.h> 64#include <ctype.h> 65#include <utime.h> 66 67struct mx_ops* mx_get_ops (int magic) 68{ 69 switch (magic) 70 { 71#ifdef USE_IMAP 72 case MUTT_IMAP: 73 return &mx_imap_ops; 74#endif 75 case MUTT_MAILDIR: 76 return &mx_maildir_ops; 77 case MUTT_MBOX: 78 return &mx_mbox_ops; 79 case MUTT_MH: 80 return &mx_mh_ops; 81 case MUTT_MMDF: 82 return &mx_mmdf_ops; 83#ifdef USE_POP 84 case MUTT_POP: 85 return &mx_pop_ops; 86#endif 87#ifdef USE_COMPRESSED 88 case MUTT_COMPRESSED: 89 return &mx_comp_ops; 90#endif 91 default: 92 return NULL; 93 } 94} 95 96#define mutt_is_spool(s) (mutt_strcmp (Spoolfile, s) == 0) 97 98#ifdef USE_DOTLOCK 99/* parameters: 100 * path - file to lock 101 * retry - should retry if unable to lock? 102 */ 103 104#ifdef DL_STANDALONE 105 106static int invoke_dotlock (const char *path, int dummy, int flags, int retry) 107{ 108 BUFFER *cmd = NULL; 109 BUFFER *f = NULL; 110 char r[SHORT_STRING]; 111 int rc; 112 113 cmd = mutt_buffer_pool_get (); 114 f = mutt_buffer_pool_get (); 115 116 if (flags & DL_FL_RETRY) 117 snprintf (r, sizeof (r), "-r %d ", retry ? MAXLOCKATTEMPT : 0); 118 119 mutt_buffer_quote_filename (f, path); 120 121 mutt_buffer_printf (cmd, 122 "%s %s%s%s%s%s%s%s", 123 NONULL (MuttDotlock), 124 flags & DL_FL_TRY ? "-t " : "", 125 flags & DL_FL_UNLOCK ? "-u " : "", 126 flags & DL_FL_USEPRIV ? "-p " : "", 127 flags & DL_FL_FORCE ? "-f " : "", 128 flags & DL_FL_UNLINK ? "-d " : "", 129 flags & DL_FL_RETRY ? r : "", 130 mutt_b2s (f)); 131 132 rc = mutt_system (mutt_b2s (cmd)); 133 134 mutt_buffer_pool_release (&cmd); 135 mutt_buffer_pool_release (&f); 136 137 return rc; 138} 139 140#else 141 142#define invoke_dotlock dotlock_invoke 143 144#endif 145 146static int dotlock_file (const char *path, int fd, int retry) 147{ 148 int r; 149 int flags = DL_FL_USEPRIV | DL_FL_RETRY; 150 151 if (retry) retry = 1; 152 153retry_lock: 154 if ((r = invoke_dotlock(path, fd, flags, retry)) == DL_EX_EXIST) 155 { 156 if (!option (OPTNOCURSES)) 157 { 158 char msg[LONG_STRING]; 159 160 snprintf(msg, sizeof(msg), _("Lock count exceeded, remove lock for %s?"), 161 path); 162 if (retry && mutt_yesorno(msg, MUTT_YES) == MUTT_YES) 163 { 164 flags |= DL_FL_FORCE; 165 retry--; 166 mutt_clear_error (); 167 goto retry_lock; 168 } 169 } 170 else 171 { 172 mutt_error ( _("Can't dotlock %s.\n"), path); 173 } 174 } 175 return (r == DL_EX_OK ? 0 : -1); 176} 177 178static int undotlock_file (const char *path, int fd) 179{ 180 return (invoke_dotlock(path, fd, DL_FL_USEPRIV | DL_FL_UNLOCK, 0) == DL_EX_OK ? 181 0 : -1); 182} 183 184#endif /* USE_DOTLOCK */ 185 186/* Args: 187 * excl if excl != 0, request an exclusive lock 188 * dot if dot != 0, try to dotlock the file 189 * timeout should retry locking? 190 */ 191int mx_lock_file (const char *path, int fd, int excl, int dot, int timeout) 192{ 193#if defined (USE_FCNTL) || defined (USE_FLOCK) 194 int count; 195 int attempt; 196 struct stat sb = { 0 }, prev_sb = { 0 }; /* silence gcc warnings */ 197#endif 198 int r = 0; 199 200#ifdef USE_FCNTL 201 struct flock lck; 202 203 memset (&lck, 0, sizeof (struct flock)); 204 lck.l_type = excl ? F_WRLCK : F_RDLCK; 205 lck.l_whence = SEEK_SET; 206 207 count = 0; 208 attempt = 0; 209 while (fcntl (fd, F_SETLK, &lck) == -1) 210 { 211 dprint(1,(debugfile, "mx_lock_file(): fcntl errno %d.\n", errno)); 212 if (errno != EAGAIN && errno != EACCES) 213 { 214 mutt_perror ("fcntl"); 215 return -1; 216 } 217 218 if (fstat (fd, &sb) != 0) 219 sb.st_size = 0; 220 221 if (count == 0) 222 prev_sb = sb; 223 224 /* only unlock file if it is unchanged */ 225 if (prev_sb.st_size == sb.st_size && ++count >= (timeout?MAXLOCKATTEMPT:0)) 226 { 227 if (timeout) 228 mutt_error _("Timeout exceeded while attempting fcntl lock!"); 229 return -1; 230 } 231 232 prev_sb = sb; 233 234 mutt_message (_("Waiting for fcntl lock... %d"), ++attempt); 235 sleep (1); 236 } 237#endif /* USE_FCNTL */ 238 239#ifdef USE_FLOCK 240 count = 0; 241 attempt = 0; 242 while (flock (fd, (excl ? LOCK_EX : LOCK_SH) | LOCK_NB) == -1) 243 { 244 if (errno != EWOULDBLOCK) 245 { 246 mutt_perror ("flock"); 247 r = -1; 248 break; 249 } 250 251 if (fstat(fd, &sb) != 0) 252 sb.st_size = 0; 253 254 if (count == 0) 255 prev_sb = sb; 256 257 /* only unlock file if it is unchanged */ 258 if (prev_sb.st_size == sb.st_size && ++count >= (timeout?MAXLOCKATTEMPT:0)) 259 { 260 if (timeout) 261 mutt_error _("Timeout exceeded while attempting flock lock!"); 262 r = -1; 263 break; 264 } 265 266 prev_sb = sb; 267 268 mutt_message (_("Waiting for flock attempt... %d"), ++attempt); 269 sleep (1); 270 } 271#endif /* USE_FLOCK */ 272 273#ifdef USE_DOTLOCK 274 if (r == 0 && dot) 275 r = dotlock_file (path, fd, timeout); 276#endif /* USE_DOTLOCK */ 277 278 if (r != 0) 279 { 280 /* release any other locks obtained in this routine */ 281 282#ifdef USE_FCNTL 283 lck.l_type = F_UNLCK; 284 fcntl (fd, F_SETLK, &lck); 285#endif /* USE_FCNTL */ 286 287#ifdef USE_FLOCK 288 flock (fd, LOCK_UN); 289#endif /* USE_FLOCK */ 290 } 291 292 return r; 293} 294 295int mx_unlock_file (const char *path, int fd, int dot) 296{ 297#ifdef USE_FCNTL 298 struct flock unlockit = { F_UNLCK, 0, 0, 0, 0 }; 299 300 memset (&unlockit, 0, sizeof (struct flock)); 301 unlockit.l_type = F_UNLCK; 302 unlockit.l_whence = SEEK_SET; 303 fcntl (fd, F_SETLK, &unlockit); 304#endif 305 306#ifdef USE_FLOCK 307 flock (fd, LOCK_UN); 308#endif 309 310#ifdef USE_DOTLOCK 311 if (dot) 312 undotlock_file (path, fd); 313#endif 314 315 return 0; 316} 317 318static void mx_unlink_empty (const char *path) 319{ 320 int fd; 321#ifndef USE_DOTLOCK 322 struct stat sb; 323#endif 324 325 if ((fd = open (path, O_RDWR)) == -1) 326 return; 327 328 if (mx_lock_file (path, fd, 1, 0, 1) == -1) 329 { 330 close (fd); 331 return; 332 } 333 334#ifdef USE_DOTLOCK 335 invoke_dotlock (path, fd, DL_FL_UNLINK, 1); 336#else 337 if (fstat (fd, &sb) == 0 && sb.st_size == 0) 338 unlink (path); 339#endif 340 341 mx_unlock_file (path, fd, 0); 342 close (fd); 343} 344 345/* try to figure out what type of mailbox ``path'' is 346 * 347 * return values: 348 * M_* mailbox type 349 * 0 not a mailbox 350 * -1 error 351 */ 352 353#ifdef USE_IMAP 354 355int mx_is_imap(const char *p) 356{ 357 url_scheme_t scheme; 358 359 if (!p) 360 return 0; 361 362 if (*p == '{') 363 return 1; 364 365 scheme = url_check_scheme (p); 366 if (scheme == U_IMAP || scheme == U_IMAPS) 367 return 1; 368 369 return 0; 370} 371 372#endif 373 374#ifdef USE_POP 375int mx_is_pop (const char *p) 376{ 377 url_scheme_t scheme; 378 379 if (!p) 380 return 0; 381 382 scheme = url_check_scheme (p); 383 if (scheme == U_POP || scheme == U_POPS) 384 return 1; 385 386 return 0; 387} 388#endif 389 390int mx_get_magic (const char *path) 391{ 392 struct stat st; 393 int magic = 0; 394 FILE *f; 395 396#ifdef USE_IMAP 397 if (mx_is_imap(path)) 398 return MUTT_IMAP; 399#endif /* USE_IMAP */ 400 401#ifdef USE_POP 402 if (mx_is_pop (path)) 403 return MUTT_POP; 404#endif /* USE_POP */ 405 406 if (stat (path, &st) == -1) 407 { 408 dprint (1, (debugfile, "mx_get_magic(): unable to stat %s: %s (errno %d).\n", 409 path, strerror (errno), errno)); 410 return (-1); 411 } 412 413 if (S_ISDIR (st.st_mode)) 414 { 415 /* check for maildir-style mailbox */ 416 if (mx_is_maildir (path)) 417 return MUTT_MAILDIR; 418 419 /* check for mh-style mailbox */ 420 if (mx_is_mh (path)) 421 return MUTT_MH; 422 } 423 else if (st.st_size == 0) 424 { 425 /* hard to tell what zero-length files are, so assume the default magic */ 426 if (DefaultMagic == MUTT_MBOX || DefaultMagic == MUTT_MMDF) 427 return (DefaultMagic); 428 else 429 return (MUTT_MBOX); 430 } 431 else if ((f = fopen (path, "r")) != NULL) 432 { 433#ifdef HAVE_UTIMENSAT 434 struct timespec ts[2]; 435#else 436 struct utimbuf times; 437#endif /* HAVE_UTIMENSAT */ 438 int ch; 439 char tmp[10]; 440 441 /* Some mailbox creation tools erroneously append a blank line to 442 * a file before appending a mail message. This allows mutt to 443 * detect magic for and thus open those files. */ 444 while ((ch = fgetc (f)) != EOF) 445 { 446 if (ch != '\n' && ch != '\r') 447 { 448 ungetc (ch, f); 449 break; 450 } 451 } 452 453 if (fgets (tmp, sizeof (tmp), f)) 454 { 455 if (mutt_strncmp ("From ", tmp, 5) == 0) 456 magic = MUTT_MBOX; 457 else if (mutt_strcmp (MMDF_SEP, tmp) == 0) 458 magic = MUTT_MMDF; 459 } 460 safe_fclose (&f); 461 462 if (!option(OPTCHECKMBOXSIZE)) 463 { 464 /* need to restore the times here, the file was not really accessed, 465 * only the type was accessed. This is important, because detection 466 * of "new mail" depends on those times set correctly. 467 */ 468#ifdef HAVE_UTIMENSAT 469 mutt_get_stat_timespec (&ts[0], &st, MUTT_STAT_ATIME); 470 mutt_get_stat_timespec (&ts[1], &st, MUTT_STAT_MTIME); 471 utimensat (0, path, ts, 0); 472#else 473 times.actime = st.st_atime; 474 times.modtime = st.st_mtime; 475 utime (path, &times); 476#endif 477 } 478 } 479 else 480 { 481 dprint (1, (debugfile, "mx_get_magic(): unable to open file %s for reading.\n", 482 path)); 483 return (-1); 484 } 485 486#ifdef USE_COMPRESSED 487 /* If there are no other matches, see if there are any 488 * compress hooks that match */ 489 if ((magic == 0) && mutt_comp_can_read (path)) 490 return MUTT_COMPRESSED; 491#endif 492 return (magic); 493} 494 495/* 496 * set DefaultMagic to the given value 497 */ 498int mx_set_magic (const char *s) 499{ 500 if (ascii_strcasecmp (s, "mbox") == 0) 501 DefaultMagic = MUTT_MBOX; 502 else if (ascii_strcasecmp (s, "mmdf") == 0) 503 DefaultMagic = MUTT_MMDF; 504 else if (ascii_strcasecmp (s, "mh") == 0) 505 DefaultMagic = MUTT_MH; 506 else if (ascii_strcasecmp (s, "maildir") == 0) 507 DefaultMagic = MUTT_MAILDIR; 508 else 509 return (-1); 510 511 return 0; 512} 513 514/* mx_access: Wrapper for access, checks permissions on a given mailbox. 515 * We may be interested in using ACL-style flags at some point, currently 516 * we use the normal access() flags. */ 517int mx_access (const char* path, int flags) 518{ 519#ifdef USE_IMAP 520 if (mx_is_imap (path)) 521 return imap_access (path); 522#endif 523 524 return access (path, flags); 525} 526 527static int mx_open_mailbox_append (CONTEXT *ctx, int flags) 528{ 529 struct stat sb; 530 531 ctx->append = 1; 532 ctx->magic = mx_get_magic (ctx->path); 533 if (ctx->magic == 0) 534 { 535 mutt_error (_("%s is not a mailbox."), ctx->path); 536 return -1; 537 } 538 539 if (ctx->magic < 0) 540 { 541 if (stat (ctx->path, &sb) == -1) 542 { 543 if (errno == ENOENT) 544 { 545#ifdef USE_COMPRESSED 546 if (mutt_comp_can_append (ctx)) 547 ctx->magic = MUTT_COMPRESSED; 548 else 549#endif 550 ctx->magic = DefaultMagic; 551 flags |= MUTT_APPENDNEW; 552 } 553 else 554 { 555 mutt_perror (ctx->path); 556 return -1; 557 } 558 } 559 else 560 return -1; 561 } 562 563 ctx->mx_ops = mx_get_ops (ctx->magic); 564 if (!ctx->mx_ops || !ctx->mx_ops->open_append) 565 return -1; 566 567 return ctx->mx_ops->open_append (ctx, flags); 568} 569 570/* 571 * open a mailbox and parse it 572 * 573 * Args: 574 * flags MUTT_NOSORT do not sort mailbox 575 * MUTT_APPEND open mailbox for appending 576 * MUTT_READONLY open mailbox in read-only mode 577 * MUTT_QUIET only print error messages 578 * MUTT_PEEK revert atime where applicable 579 * ctx if non-null, context struct to use 580 */ 581CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx) 582{ 583 CONTEXT *ctx = pctx; 584 int rc; 585 char realpathbuf[PATH_MAX]; 586 587 if (!ctx) 588 ctx = safe_malloc (sizeof (CONTEXT)); 589 memset (ctx, 0, sizeof (CONTEXT)); 590 591 ctx->path = safe_strdup (path); 592 if (!ctx->path) 593 { 594 if (!pctx) 595 FREE (&ctx); 596 return NULL; 597 } 598 if (! realpath (ctx->path, realpathbuf) ) 599 ctx->realpath = safe_strdup (ctx->path); 600 else 601 ctx->realpath = safe_strdup (realpathbuf); 602 603 ctx->msgnotreadyet = -1; 604 ctx->collapsed = 0; 605 606 for (rc=0; rc < RIGHTSMAX; rc++) 607 mutt_bit_set(ctx->rights,rc); 608 609 if (flags & MUTT_QUIET) 610 ctx->quiet = 1; 611 if (flags & MUTT_READONLY) 612 ctx->readonly = 1; 613 if (flags & MUTT_PEEK) 614 ctx->peekonly = 1; 615 616 if (flags & (MUTT_APPEND|MUTT_NEWFOLDER)) 617 { 618 if (mx_open_mailbox_append (ctx, flags) != 0) 619 { 620 mx_fastclose_mailbox (ctx); 621 if (!pctx) 622 FREE (&ctx); 623 return NULL; 624 } 625 return ctx; 626 } 627 628 ctx->magic = mx_get_magic (path); 629 ctx->mx_ops = mx_get_ops (ctx->magic); 630 631 if (ctx->magic <= 0 || !ctx->mx_ops) 632 { 633 if (ctx->magic == -1) 634 mutt_perror(path); 635 else if (ctx->magic == 0 || !ctx->mx_ops) 636 mutt_error (_("%s is not a mailbox."), path); 637 638 mx_fastclose_mailbox (ctx); 639 if (!pctx) 640 FREE (&ctx); 641 return (NULL); 642 } 643 644 mutt_make_label_hash (ctx); 645 646 /* if the user has a `push' command in their .muttrc, or in a folder-hook, 647 * it will cause the progress messages not to be displayed because 648 * mutt_refresh() will think we are in the middle of a macro. so set a 649 * flag to indicate that we should really refresh the screen. 650 */ 651 set_option (OPTFORCEREFRESH); 652 653 if (!ctx->quiet) 654 mutt_message (_("Reading %s..."), ctx->path); 655 656 rc = ctx->mx_ops->open(ctx); 657 658 if (rc == 0) 659 { 660 if ((flags & MUTT_NOSORT) == 0) 661 { 662 /* avoid unnecessary work since the mailbox is completely unthreaded 663 to begin with */ 664 unset_option (OPTSORTSUBTHREADS); 665 unset_option (OPTNEEDRESCORE); 666 mutt_sort_headers (ctx, 1); 667 } 668 if (!ctx->quiet) 669 mutt_clear_error (); 670 } 671 else 672 { 673 mx_fastclose_mailbox (ctx); 674 if (!pctx) 675 FREE (&ctx); 676 } 677 678 unset_option (OPTFORCEREFRESH); 679 return (ctx); 680} 681 682/* free up memory associated with the mailbox context */ 683void mx_fastclose_mailbox (CONTEXT *ctx) 684{ 685 int i; 686#ifdef HAVE_UTIMENSAT 687 struct timespec ts[2]; 688#else 689 struct utimbuf ut; 690#endif /* HAVE_UTIMENSAT */ 691 692 if (!ctx) 693 return; 694 695 /* fix up the times so buffy won't get confused */ 696 if (ctx->peekonly && ctx->path && 697 (mutt_timespec_compare (&ctx->mtime, &ctx->atime) > 0)) 698 { 699#ifdef HAVE_UTIMENSAT 700 ts[0] = ctx->atime; 701 ts[1] = ctx->mtime; 702 utimensat (0, ctx->path, ts, 0); 703#else 704 ut.actime = ctx->atime.tv_sec; 705 ut.modtime = ctx->mtime.tv_sec; 706 utime (ctx->path, &ut); 707#endif /* HAVE_UTIMENSAT */ 708 } 709 710 /* never announce that a mailbox we've just left has new mail. #3290 711 * XXX: really belongs in mx_close_mailbox, but this is a nice hook point */ 712 if (!ctx->peekonly) 713 mutt_buffy_setnotified(ctx->path); 714 715 if (ctx->mx_ops) 716 ctx->mx_ops->close (ctx); 717 718#ifdef USE_COMPRESSED 719 mutt_free_compress_info (ctx); 720#endif /* USE_COMPRESSED */ 721 722 if (ctx->subj_hash) 723 hash_destroy (&ctx->subj_hash, NULL); 724 if (ctx->id_hash) 725 hash_destroy (&ctx->id_hash, NULL); 726 hash_destroy (&ctx->label_hash, NULL); 727 mutt_clear_threads (ctx); 728 for (i = 0; i < ctx->msgcount; i++) 729 mutt_free_header (&ctx->hdrs[i]); 730 FREE (&ctx->hdrs); 731 FREE (&ctx->v2r); 732 FREE (&ctx->path); 733 FREE (&ctx->realpath); 734 FREE (&ctx->pattern); 735 if (ctx->limit_pattern) 736 mutt_pattern_free (&ctx->limit_pattern); 737 safe_fclose (&ctx->fp); 738 memset (ctx, 0, sizeof (CONTEXT)); 739} 740 741/* save changes to disk */ 742static int sync_mailbox (CONTEXT *ctx, int *index_hint) 743{ 744 int rc; 745 746 if (!ctx->mx_ops || !ctx->mx_ops->sync) 747 return -1; 748 749 if (!ctx->quiet) 750 { 751 /* L10N: Displayed before/as a mailbox is being synced */ 752 mutt_message (_("Writing %s..."), ctx->path); 753 } 754 755 rc = ctx->mx_ops->sync (ctx, index_hint); 756 if (rc != 0 && !ctx->quiet) 757 { 758 /* L10N: Displayed if a mailbox sync fails */ 759 mutt_error (_("Unable to write %s!"), ctx->path); 760 } 761 762 return rc; 763} 764 765/* move deleted mails to the trash folder */ 766static int trash_append (CONTEXT *ctx) 767{ 768 CONTEXT ctx_trash; 769 int i; 770 struct stat st, stc; 771 int opt_confappend, rc; 772 773 if (!TrashPath || !ctx->deleted || 774 (ctx->magic == MUTT_MAILDIR && option (OPTMAILDIRTRASH))) 775 return 0; 776 777 for (i = 0; i < ctx->msgcount; i++) 778 if (ctx->hdrs[i]->deleted && (!ctx->hdrs[i]->purge)) 779 break; 780 if (i == ctx->msgcount) 781 return 0; /* nothing to be done */ 782 783 /* avoid the "append messages" prompt */ 784 opt_confappend = option (OPTCONFIRMAPPEND); 785 if (opt_confappend) 786 unset_option (OPTCONFIRMAPPEND); 787 rc = mutt_save_confirm (TrashPath, &st); 788 if (opt_confappend) 789 set_option (OPTCONFIRMAPPEND); 790 if (rc != 0) 791 { 792 mutt_error _("message(s) not deleted"); 793 return -1; 794 } 795 796 if (lstat (ctx->path, &stc) == 0 && stc.st_ino == st.st_ino 797 && stc.st_dev == st.st_dev && stc.st_rdev == st.st_rdev) 798 return 0; /* we are in the trash folder: simple sync */ 799 800#ifdef USE_IMAP 801 if (Context->magic == MUTT_IMAP && mx_is_imap (TrashPath)) 802 { 803 if (!imap_fast_trash (Context, TrashPath)) 804 return 0; 805 } 806#endif 807 808 if (mx_open_mailbox (TrashPath, MUTT_APPEND, &ctx_trash) != NULL) 809 { 810 /* continue from initial scan above */ 811 for (; i < ctx->msgcount ; i++) 812 if (ctx->hdrs[i]->deleted && (!ctx->hdrs[i]->purge)) 813 { 814 if (mutt_append_message (&ctx_trash, ctx, ctx->hdrs[i], 0, 0) == -1) 815 { 816 mx_close_mailbox (&ctx_trash, NULL); 817 return -1; 818 } 819 } 820 821 mx_close_mailbox (&ctx_trash, NULL); 822 } 823 else 824 { 825 mutt_error _("Can't open trash folder"); 826 return -1; 827 } 828 829 return 0; 830} 831 832/* save changes and close mailbox. 833 * 834 * returns 0 on success. 835 * -1 on error 836 * one of the check_mailbox enums if aborted for one of those reasons. 837 * 838 * Note: it's very important to ensure the mailbox is properly closed 839 * before free'ing the context. For selected mailboxes, IMAP 840 * will cache the context inside connection->idata until 841 * imap_close_mailbox() removes it. 842 * 843 * Readonly, dontwrite, and append mailboxes are guaranteed to call 844 * mx_fastclose_mailbox(), so for most of Mutt's code you won't see 845 * return value checks for temporary contexts. 846 */ 847int mx_close_mailbox (CONTEXT *ctx, int *index_hint) 848{ 849 int i, move_messages = 0, purge = 1, read_msgs = 0; 850 int rc = -1; 851 int check; 852 int isSpool = 0; 853 CONTEXT f; 854 BUFFER *mbox = NULL; 855 char buf[SHORT_STRING]; 856 857 if (!ctx) return 0; 858 859 ctx->closing = 1; 860 861 if (ctx->readonly || ctx->dontwrite || ctx->append) 862 { 863 mx_fastclose_mailbox (ctx); 864 return 0; 865 } 866 867 for (i = 0; i < ctx->msgcount; i++) 868 { 869 if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read 870 && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED))) 871 read_msgs++; 872 } 873 874 if (read_msgs && quadoption (OPT_MOVE) != MUTT_NO) 875 { 876 char *p; 877 878 mbox = mutt_buffer_pool_get (); 879 880 if ((p = mutt_find_hook (MUTT_MBOXHOOK, ctx->path))) 881 { 882 isSpool = 1; 883 mutt_buffer_strcpy (mbox, p); 884 } 885 else 886 { 887 mutt_buffer_strcpy (mbox, NONULL(Inbox)); 888 isSpool = mutt_is_spool (ctx->path) && !mutt_is_spool (mutt_b2s (mbox)); 889 } 890 891 if (isSpool && mutt_buffer_len (mbox)) 892 { 893 mutt_buffer_expand_path (mbox); 894 snprintf (buf, sizeof (buf), _("Move %d read messages to %s?"), 895 read_msgs, mutt_b2s (mbox)); 896 if ((move_messages = query_quadoption (OPT_MOVE, buf)) == -1) 897 { 898 ctx->closing = 0; 899 goto cleanup; 900 } 901 } 902 } 903 904 /* 905 * There is no point in asking whether or not to purge if we are 906 * just marking messages as "trash". 907 */ 908 if (ctx->deleted && !(ctx->magic == MUTT_MAILDIR && option (OPTMAILDIRTRASH))) 909 { 910 snprintf (buf, sizeof (buf), ctx->deleted == 1 911 ? _("Purge %d deleted message?") : _("Purge %d deleted messages?"), 912 ctx->deleted); 913 if ((purge = query_quadoption (OPT_DELETE, buf)) < 0) 914 { 915 ctx->closing = 0; 916 goto cleanup; 917 } 918 } 919 920 if (option (OPTMARKOLD)) 921 { 922 for (i = 0; i < ctx->msgcount; i++) 923 { 924 if (!ctx->hdrs[i]->deleted && !ctx->hdrs[i]->old && !ctx->hdrs[i]->read) 925 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_OLD, 1); 926 } 927 } 928 929 if (move_messages) 930 { 931 if (!ctx->quiet) 932 mutt_message (_("Moving read messages to %s..."), mutt_b2s (mbox)); 933 934#ifdef USE_IMAP 935 /* try to use server-side copy first */ 936 i = 1; 937 938 if (ctx->magic == MUTT_IMAP && mx_is_imap (mutt_b2s (mbox))) 939 { 940 /* tag messages for moving, and clear old tags, if any */ 941 for (i = 0; i < ctx->msgcount; i++) 942 if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted 943 && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED))) 944 ctx->hdrs[i]->tagged = 1; 945 else 946 ctx->hdrs[i]->tagged = 0; 947 948 i = imap_copy_messages (ctx, NULL, mutt_b2s (mbox), 1); 949 } 950 951 if (i == 0) /* success */ 952 mutt_clear_error (); 953 else if (i == -1) /* horrible error, bail */ 954 { 955 ctx->closing=0; 956 goto cleanup; 957 } 958 else /* use regular append-copy mode */ 959#endif 960 { 961 if (mx_open_mailbox (mutt_b2s (mbox), MUTT_APPEND, &f) == NULL) 962 { 963 ctx->closing = 0; 964 goto cleanup; 965 } 966 967 for (i = 0; i < ctx->msgcount; i++) 968 { 969 if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted 970 && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED))) 971 { 972 if (mutt_append_message (&f, ctx, ctx->hdrs[i], 0, CH_UPDATE_LEN) == 0) 973 { 974 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_DELETE, 1); 975 mutt_set_flag (ctx, ctx->hdrs[i], MUTT_PURGE, 1); 976 } 977 else 978 { 979 mx_close_mailbox (&f, NULL); 980 ctx->closing = 0; 981 goto cleanup; 982 } 983 } 984 } 985 986 mx_close_mailbox (&f, NULL); 987 } 988 989 } 990 else if (!ctx->changed && ctx->deleted == 0) 991 { 992 if (!ctx->quiet) 993 mutt_message _("Mailbox is unchanged."); 994 if (ctx->magic == MUTT_MBOX || ctx->magic == MUTT_MMDF) 995 mbox_reset_atime (ctx, NULL); 996 mx_fastclose_mailbox (ctx); 997 rc = 0; 998 goto cleanup; 999 } 1000 1001 /* copy mails to the trash before expunging */ 1002 if (purge && ctx->deleted && mutt_strcmp (ctx->path, TrashPath)) 1003 { 1004 if (trash_append (ctx) != 0) 1005 { 1006 ctx->closing = 0; 1007 goto cleanup; 1008 } 1009 } 1010 1011#ifdef USE_IMAP 1012 /* allow IMAP to preserve the deleted flag across sessions */ 1013 if (ctx->magic == MUTT_IMAP) 1014 { 1015 if ((check = imap_sync_mailbox (ctx, purge, index_hint)) != 0) 1016 { 1017 ctx->closing = 0; 1018 rc = check; 1019 goto cleanup; 1020 } 1021 } 1022 else 1023#endif 1024 { 1025 if (!purge) 1026 { 1027 for (i = 0; i < ctx->msgcount; i++) 1028 { 1029 ctx->hdrs[i]->deleted = 0; 1030 ctx->hdrs[i]->purge = 0; 1031 } 1032 ctx->deleted = 0; 1033 } 1034 1035 if (ctx->changed || ctx->deleted) 1036 { 1037 if ((check = sync_mailbox (ctx, index_hint)) != 0) 1038 { 1039 ctx->closing = 0; 1040 rc = check; 1041 goto cleanup; 1042 } 1043 } 1044 } 1045 1046 if (!ctx->quiet) 1047 { 1048 if (move_messages) 1049 mutt_message (_("%d kept, %d moved, %d deleted."), 1050 ctx->msgcount - ctx->deleted, read_msgs, ctx->deleted); 1051 else 1052 mutt_message (_("%d kept, %d deleted."), 1053 ctx->msgcount - ctx->deleted, ctx->deleted); 1054 } 1055 1056 if (ctx->msgcount == ctx->deleted && 1057 (ctx->magic == MUTT_MMDF || ctx->magic == MUTT_MBOX) && 1058 !mutt_is_spool(ctx->path) && !option (OPTSAVEEMPTY)) 1059 mx_unlink_empty (ctx->path); 1060 1061#ifdef USE_SIDEBAR 1062 if (purge && ctx->deleted) 1063 { 1064 int orig_msgcount = ctx->msgcount; 1065 1066 for (i = 0; i < ctx->msgcount; i++) 1067 { 1068 if (ctx->hdrs[i]->deleted && !ctx->hdrs[i]->read) 1069 ctx->unread--; 1070 if (ctx->hdrs[i]->deleted && ctx->hdrs[i]->flagged) 1071 ctx->flagged--; 1072 } 1073 ctx->msgcount -= ctx->deleted; 1074 mutt_sb_set_buffystats (ctx); 1075 ctx->msgcount = orig_msgcount; 1076 } 1077#endif 1078 1079 mx_fastclose_mailbox (ctx); 1080 1081 rc = 0; 1082 1083cleanup: 1084 mutt_buffer_pool_release (&mbox); 1085 return rc; 1086} 1087 1088 1089/* update a Context structure's internal tables. */ 1090 1091void mx_update_tables(CONTEXT *ctx, int committing) 1092{ 1093 int i, j, padding; 1094 1095 /* update memory to reflect the new state of the mailbox */ 1096 ctx->vcount = 0; 1097 ctx->vsize = 0; 1098 ctx->tagged = 0; 1099 ctx->deleted = 0; 1100 ctx->new = 0; 1101 ctx->unread = 0; 1102 ctx->changed = 0; 1103 ctx->flagged = 0; 1104 padding = mx_msg_padding_size (ctx); 1105#define this_body ctx->hdrs[j]->content 1106 for (i = 0, j = 0; i < ctx->msgcount; i++) 1107 { 1108 if ((committing && (!ctx->hdrs[i]->deleted || 1109 (ctx->magic == MUTT_MAILDIR && option (OPTMAILDIRTRASH)))) || 1110 (!committing && ctx->hdrs[i]->active)) 1111 { 1112 if (i != j) 1113 { 1114 ctx->hdrs[j] = ctx->hdrs[i]; 1115 ctx->hdrs[i] = NULL; 1116 } 1117 ctx->hdrs[j]->msgno = j; 1118 if (ctx->hdrs[j]->virtual != -1) 1119 { 1120 ctx->v2r[ctx->vcount] = j; 1121 ctx->hdrs[j]->virtual = ctx->vcount++; 1122 ctx->vsize += this_body->length + this_body->offset - 1123 this_body->hdr_offset + padding; 1124 } 1125 1126 if (committing) 1127 { 1128 ctx->hdrs[j]->changed = 0; 1129 ctx->hdrs[j]->env->changed = 0; 1130 } 1131 else if (ctx->hdrs[j]->changed) 1132 ctx->changed = 1; 1133 1134 if (!committing || (ctx->magic == MUTT_MAILDIR && option (OPTMAILDIRTRASH))) 1135 { 1136 if (ctx->hdrs[j]->deleted) 1137 ctx->deleted++; 1138 } 1139 1140 if (ctx->hdrs[j]->tagged) 1141 ctx->tagged++; 1142 if (ctx->hdrs[j]->flagged) 1143 ctx->flagged++; 1144 if (!ctx->hdrs[j]->read) 1145 { 1146 ctx->unread++; 1147 if (!ctx->hdrs[j]->old) 1148 ctx->new++; 1149 } 1150 1151 j++; 1152 } 1153 else 1154 { 1155 if (ctx->magic == MUTT_MH || ctx->magic == MUTT_MAILDIR) 1156 ctx->size -= (ctx->hdrs[i]->content->length + 1157 ctx->hdrs[i]->content->offset - 1158 ctx->hdrs[i]->content->hdr_offset); 1159 /* remove message from the hash tables */ 1160 if (ctx->subj_hash && ctx->hdrs[i]->env->real_subj) 1161 hash_delete (ctx->subj_hash, ctx->hdrs[i]->env->real_subj, ctx->hdrs[i], NULL); 1162 if (ctx->id_hash && ctx->hdrs[i]->env->message_id) 1163 hash_delete (ctx->id_hash, ctx->hdrs[i]->env->message_id, ctx->hdrs[i], NULL); 1164 mutt_label_hash_remove (ctx, ctx->hdrs[i]); 1165 /* The path mx_check_mailbox() -> imap_check_mailbox() -> 1166 * imap_expunge_mailbox() -> mx_update_tables() 1167 * can occur before a call to mx_sync_mailbox(), resulting in 1168 * last_tag being stale if it's not reset here. 1169 */ 1170 if (ctx->last_tag == ctx->hdrs[i]) 1171 ctx->last_tag = NULL; 1172 mutt_free_header (&ctx->hdrs[i]); 1173 } 1174 } 1175#undef this_body 1176 ctx->msgcount = j; 1177} 1178 1179 1180/* save changes to mailbox 1181 * 1182 * return values: 1183 * 0 success 1184 * -1 error 1185 */ 1186int mx_sync_mailbox (CONTEXT *ctx, int *index_hint) 1187{ 1188 int rc, i; 1189 int purge = 1; 1190 int msgcount, deleted; 1191 1192 if (ctx->dontwrite) 1193 { 1194 char buf[STRING], tmp[STRING]; 1195 if (km_expand_key (buf, sizeof(buf), 1196 km_find_func (MENU_MAIN, OP_TOGGLE_WRITE))) 1197 snprintf (tmp, sizeof(tmp), _(" Press '%s' to toggle write"), buf); 1198 else 1199 strfcpy (tmp, _("Use 'toggle-write' to re-enable write!"), sizeof(tmp)); 1200 1201 mutt_error (_("Mailbox is marked unwritable. %s"), tmp); 1202 return -1; 1203 } 1204 else if (ctx->readonly) 1205 { 1206 mutt_error _("Mailbox is read-only."); 1207 return -1; 1208 } 1209 1210 if (!ctx->changed && !ctx->deleted) 1211 { 1212 if (!ctx->quiet) 1213 mutt_message _("Mailbox is unchanged."); 1214 return (0); 1215 } 1216 1217 if (ctx->deleted) 1218 { 1219 char buf[SHORT_STRING]; 1220 1221 snprintf (buf, sizeof (buf), ctx->deleted == 1 1222 ? _("Purge %d deleted message?") : _("Purge %d deleted messages?"), 1223 ctx->deleted); 1224 if ((purge = query_quadoption (OPT_DELETE, buf)) < 0) 1225 return (-1); 1226 else if (purge == MUTT_NO) 1227 { 1228 if (!ctx->changed) 1229 return 0; /* nothing to do! */ 1230 /* let IMAP servers hold on to D flags */ 1231 if (ctx->magic != MUTT_IMAP) 1232 { 1233 for (i = 0 ; i < ctx->msgcount ; i++) 1234 { 1235 ctx->hdrs[i]->deleted = 0; 1236 ctx->hdrs[i]->purge = 0; 1237 } 1238 ctx->deleted = 0; 1239 } 1240 } 1241 else if (ctx->last_tag && ctx->last_tag->deleted) 1242 ctx->last_tag = NULL; /* reset last tagged msg now useless */ 1243 } 1244 1245 /* really only for IMAP - imap_sync_mailbox results in a call to 1246 * mx_update_tables, so ctx->deleted is 0 when it comes back */ 1247 msgcount = ctx->msgcount; 1248 deleted = ctx->deleted; 1249 1250 if (purge && ctx->deleted && mutt_strcmp (ctx->path, TrashPath)) 1251 { 1252 if (trash_append (ctx) != 0) 1253 return -1; 1254 } 1255 1256#ifdef USE_IMAP 1257 if (ctx->magic == MUTT_IMAP) 1258 rc = imap_sync_mailbox (ctx, purge, index_hint); 1259 else 1260#endif 1261 rc = sync_mailbox (ctx, index_hint); 1262 if (rc == 0) 1263 { 1264#ifdef USE_IMAP 1265 if (ctx->magic == MUTT_IMAP && !purge) 1266 { 1267 if (!ctx->quiet) 1268 mutt_message _("Mailbox checkpointed."); 1269 } 1270 else 1271#endif 1272 { 1273 if (!ctx->quiet) 1274 mutt_message (_("%d kept, %d deleted."), msgcount - deleted, 1275 deleted); 1276 } 1277 1278 mutt_sleep (0); 1279 1280 if (ctx->msgcount == ctx->deleted && 1281 (ctx->magic == MUTT_MBOX || ctx->magic == MUTT_MMDF) && 1282 !mutt_is_spool (ctx->path) && !option (OPTSAVEEMPTY)) 1283 { 1284 unlink (ctx->path); 1285 mx_fastclose_mailbox (ctx); 1286 return 0; 1287 } 1288 1289 /* if we haven't deleted any messages, we don't need to resort */ 1290 /* ... except for certain folder formats which need "unsorted" 1291 * sort order in order to synchronize folders. 1292 * 1293 * MH and maildir are safe. mbox-style seems to need re-sorting, 1294 * at least with the new threading code. 1295 */ 1296 if (purge || (ctx->magic != MUTT_MAILDIR && ctx->magic != MUTT_MH)) 1297 { 1298 /* IMAP does this automatically after handling EXPUNGE */ 1299 if (ctx->magic != MUTT_IMAP) 1300 { 1301 mx_update_tables (ctx, 1); 1302 mutt_sort_headers (ctx, 1); /* rethread from scratch */ 1303 } 1304 } 1305 } 1306 1307 return (rc); 1308} 1309 1310/* args: 1311 * dest destination mailbox 1312 * hdr message being copied (required for maildir support, because 1313 * the filename depends on the message flags) 1314 */ 1315MESSAGE *mx_open_new_message (CONTEXT *dest, HEADER *hdr, int flags) 1316{ 1317 ADDRESS *p = NULL; 1318 MESSAGE *msg; 1319 1320 if (!dest->mx_ops || !dest->mx_ops->open_new_msg) 1321 { 1322 dprint (1, (debugfile, "mx_open_new_message(): function unimplemented for mailbox type %d.\n", 1323 dest->magic)); 1324 return NULL; 1325 } 1326 1327 msg = safe_calloc (1, sizeof (MESSAGE)); 1328 msg->write = 1; 1329 1330 if (hdr) 1331 { 1332 msg->flags.flagged = hdr->flagged; 1333 msg->flags.replied = hdr->replied; 1334 msg->flags.read = hdr->read; 1335 msg->flags.draft = (flags & MUTT_SET_DRAFT) ? 1 : 0; 1336 msg->received = hdr->received; 1337 } 1338 1339 if (msg->received == 0) 1340 time(&msg->received); 1341 1342 if (dest->mx_ops->open_new_msg (msg, dest, hdr) == 0) 1343 { 1344 if (dest->magic == MUTT_MMDF) 1345 fputs (MMDF_SEP, msg->fp); 1346 1347 if ((dest->magic == MUTT_MBOX || dest->magic == MUTT_MMDF) && 1348 flags & MUTT_ADD_FROM) 1349 { 1350 if (hdr) 1351 { 1352 if (hdr->env->return_path) 1353 p = hdr->env->return_path; 1354 else if (hdr->env->sender) 1355 p = hdr->env->sender; 1356 else 1357 p = hdr->env->from; 1358 } 1359 1360 fprintf (msg->fp, "From %s %s", p ? p->mailbox : NONULL(Username), ctime (&msg->received)); 1361 } 1362 } 1363 else 1364 FREE (&msg); 1365 1366 return msg; 1367} 1368 1369/* check for new mail */ 1370int mx_check_mailbox (CONTEXT *ctx, int *index_hint) 1371{ 1372 if (!ctx || !ctx->mx_ops) 1373 { 1374 dprint (1, (debugfile, "mx_check_mailbox: null or invalid context.\n")); 1375 return -1; 1376 } 1377 1378 return ctx->mx_ops->check (ctx, index_hint); 1379} 1380 1381/* return a stream pointer for a message */ 1382MESSAGE *mx_open_message (CONTEXT *ctx, int msgno) 1383{ 1384 MESSAGE *msg; 1385 1386 if (!ctx->mx_ops || !ctx->mx_ops->open_msg) 1387 { 1388 dprint (1, (debugfile, "mx_open_message(): function not implemented for mailbox type %d.\n", ctx->magic)); 1389 return NULL; 1390 } 1391 1392 msg = safe_calloc (1, sizeof (MESSAGE)); 1393 if (ctx->mx_ops->open_msg (ctx, msg, msgno)) 1394 FREE (&msg); 1395 1396 return msg; 1397} 1398 1399/* commit a message to a folder */ 1400 1401int mx_commit_message (MESSAGE *msg, CONTEXT *ctx) 1402{ 1403 if (!ctx->mx_ops || !ctx->mx_ops->commit_msg) 1404 return -1; 1405 1406 if (!(msg->write && ctx->append)) 1407 { 1408 dprint (1, (debugfile, "mx_commit_message(): msg->write = %d, ctx->append = %d\n", 1409 msg->write, ctx->append)); 1410 return -1; 1411 } 1412 1413 return ctx->mx_ops->commit_msg (ctx, msg); 1414} 1415 1416/* close a pointer to a message */ 1417int mx_close_message (CONTEXT *ctx, MESSAGE **msg) 1418{ 1419 int r = 0; 1420 1421 if (ctx->mx_ops && ctx->mx_ops->close_msg) 1422 r = ctx->mx_ops->close_msg (ctx, *msg); 1423 1424 if ((*msg)->path) 1425 { 1426 dprint (1, (debugfile, "mx_close_message (): unlinking %s\n", 1427 (*msg)->path)); 1428 unlink ((*msg)->path); 1429 FREE (&(*msg)->path); 1430 } 1431 1432 FREE (msg); /* __FREE_CHECKED__ */ 1433 return (r); 1434} 1435 1436void mx_alloc_memory (CONTEXT *ctx) 1437{ 1438 int i; 1439 size_t s = MAX (sizeof (HEADER *), sizeof (int)); 1440 1441 if ((ctx->hdrmax + 25) * s < ctx->hdrmax * s) 1442 { 1443 mutt_error _("Integer overflow -- can't allocate memory."); 1444 sleep (1); 1445 mutt_exit (1); 1446 } 1447 1448 if (ctx->hdrs) 1449 { 1450 safe_realloc (&ctx->hdrs, sizeof (HEADER *) * (ctx->hdrmax += 25)); 1451 safe_realloc (&ctx->v2r, sizeof (int) * ctx->hdrmax); 1452 } 1453 else 1454 { 1455 ctx->hdrs = safe_calloc ((ctx->hdrmax += 25), sizeof (HEADER *)); 1456 ctx->v2r = safe_calloc (ctx->hdrmax, sizeof (int)); 1457 } 1458 for (i = ctx->msgcount ; i < ctx->hdrmax ; i++) 1459 { 1460 ctx->hdrs[i] = NULL; 1461 ctx->v2r[i] = -1; 1462 } 1463} 1464 1465/* this routine is called to update the counts in the context structure for 1466 * the last message header parsed. 1467 */ 1468void mx_update_context (CONTEXT *ctx, int new_messages) 1469{ 1470 HEADER *h; 1471 int msgno; 1472 1473 for (msgno = ctx->msgcount - new_messages; msgno < ctx->msgcount; msgno++) 1474 { 1475 h = ctx->hdrs[msgno]; 1476 1477 if (WithCrypto) 1478 { 1479 /* NOTE: this _must_ be done before the check for mailcap! */ 1480 h->security = crypt_query (h->content); 1481 } 1482 1483 if (!ctx->pattern) 1484 { 1485 ctx->v2r[ctx->vcount] = msgno; 1486 h->virtual = ctx->vcount++; 1487 } 1488 else 1489 h->virtual = -1; 1490 h->msgno = msgno; 1491 1492 if (h->env->supersedes) 1493 { 1494 HEADER *h2; 1495 1496 if (!ctx->id_hash) 1497 ctx->id_hash = mutt_make_id_hash (ctx); 1498 1499 h2 = hash_find (ctx->id_hash, h->env->supersedes); 1500 1501 /* FREE (&h->env->supersedes); should I ? */ 1502 if (h2) 1503 { 1504 h2->superseded = 1; 1505 if (option (OPTSCORE)) 1506 mutt_score_message (ctx, h2, 1); 1507 } 1508 } 1509 1510 /* add this message to the hash tables */ 1511 if (ctx->id_hash && h->env->message_id) 1512 hash_insert (ctx->id_hash, h->env->message_id, h); 1513 if (ctx->subj_hash && h->env->real_subj) 1514 hash_insert (ctx->subj_hash, h->env->real_subj, h); 1515 mutt_label_hash_add (ctx, h); 1516 1517 if (option (OPTSCORE)) 1518 mutt_score_message (ctx, h, 0); 1519 1520 if (h->changed) 1521 ctx->changed = 1; 1522 if (h->flagged) 1523 ctx->flagged++; 1524 if (h->deleted) 1525 ctx->deleted++; 1526 if (!h->read) 1527 { 1528 ctx->unread++; 1529 if (!h->old) 1530 ctx->new++; 1531 } 1532 } 1533} 1534 1535/* 1536 * Return: 1537 * 1 if the specified mailbox contains 0 messages. 1538 * 0 if the mailbox contains messages 1539 * -1 on error 1540 */ 1541int mx_check_empty (const char *path) 1542{ 1543 switch (mx_get_magic (path)) 1544 { 1545 case MUTT_MBOX: 1546 case MUTT_MMDF: 1547 return mbox_check_empty (path); 1548 case MUTT_MH: 1549 return mh_check_empty (path); 1550 case MUTT_MAILDIR: 1551 return maildir_check_empty (path); 1552#ifdef USE_IMAP 1553 case MUTT_IMAP: 1554 { 1555 int passive, rv; 1556 1557 passive = option (OPTIMAPPASSIVE); 1558 if (passive) 1559 unset_option (OPTIMAPPASSIVE); 1560 rv = imap_status (path, 0); 1561 if (passive) 1562 set_option (OPTIMAPPASSIVE); 1563 return (rv <= 0); 1564 } 1565#endif 1566 default: 1567 errno = EINVAL; 1568 return -1; 1569 } 1570 /* not reached */ 1571} 1572 1573/* mx_msg_padding_size: Returns the padding size between messages for the 1574 * mailbox type pointed to by ctx. 1575 * 1576 * mmdf and mbox add separators, which leads a small discrepancy when computing 1577 * vsize for a limited view. 1578 */ 1579int mx_msg_padding_size (CONTEXT *ctx) 1580{ 1581 if (!ctx->mx_ops || !ctx->mx_ops->msg_padding_size) 1582 return 0; 1583 1584 return ctx->mx_ops->msg_padding_size (ctx); 1585} 1586 1587/* Writes a single header out to the header cache. */ 1588int mx_save_to_header_cache (CONTEXT *ctx, HEADER *h) 1589{ 1590 if (!ctx->mx_ops || !ctx->mx_ops->save_to_header_cache) 1591 return 0; 1592 1593 return ctx->mx_ops->save_to_header_cache (ctx, h); 1594} 1595 1596/* vim: set sw=2: */