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