mutt stable branch with some hacks
at master 961 lines 21 kB view raw
1/* Copyright (C) 1997 Alain Penders <Alain@Finale-Dev.com> 2 * Copyright (C) 2016 Richard Russon <rich@flatcap.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 */ 18 19#if HAVE_CONFIG_H 20#include "config.h" 21#endif 22 23#include <errno.h> 24#include <string.h> 25#include <sys/stat.h> 26#include <unistd.h> 27 28#include "mutt.h" 29#include "mailbox.h" 30#include "mutt_curses.h" 31#include "mx.h" 32#include "compress.h" 33 34/* Notes: 35 * Any references to compressed files also apply to encrypted files. 36 * ctx->path == plaintext file 37 * ctx->realpath == compressed file 38 */ 39 40/** 41 * struct COMPRESS_INFO - Private data for compress 42 * 43 * This object gets attached to the mailbox's CONTEXT. 44 */ 45typedef struct 46{ 47 const char *append; /* append-hook command */ 48 const char *close; /* close-hook command */ 49 const char *open; /* open-hook command */ 50 off_t size; /* size of the compressed file */ 51 struct mx_ops *child_ops; /* callbacks of de-compressed file */ 52 int locked; /* if realpath is locked */ 53 FILE *lockfp; /* fp used for locking */ 54} COMPRESS_INFO; 55 56 57/** 58 * lock_realpath - Try to lock the ctx->realpath 59 * @ctx: Mailbox to lock 60 * @excl: Lock exclusively? 61 * 62 * Try to (exclusively) lock the mailbox. If we succeed, then we mark the 63 * mailbox as locked. If we fail, but we didn't want exclusive rights, then 64 * the mailbox will be marked readonly. 65 * 66 * Returns: 67 * 1: Success (locked or readonly) 68 * 0: Error (can't lock the file) 69 */ 70static int 71lock_realpath (CONTEXT *ctx, int excl) 72{ 73 if (!ctx) 74 return 0; 75 76 COMPRESS_INFO *ci = ctx->compress_info; 77 if (!ci) 78 return 0; 79 80 if (ci->locked) 81 return 1; 82 83 if (excl) 84 ci->lockfp = fopen (ctx->realpath, "a"); 85 else 86 ci->lockfp = fopen (ctx->realpath, "r"); 87 if (!ci->lockfp) 88 { 89 mutt_perror (ctx->realpath); 90 return 0; 91 } 92 93 int r = mx_lock_file (ctx->realpath, fileno (ci->lockfp), excl, 1, 1); 94 95 if (r == 0) 96 ci->locked = 1; 97 else if (excl == 0) 98 { 99 safe_fclose (&ci->lockfp); 100 ctx->readonly = 1; 101 return 1; 102 } 103 104 return (r == 0); 105} 106 107/** 108 * unlock_realpath - Unlock the ctx->realpath 109 * @ctx: Mailbox to unlock 110 * 111 * Unlock a mailbox previously locked by lock_mailbox(). 112 */ 113static void 114unlock_realpath (CONTEXT *ctx) 115{ 116 if (!ctx) 117 return; 118 119 COMPRESS_INFO *ci = ctx->compress_info; 120 if (!ci) 121 return; 122 123 if (!ci->locked) 124 return; 125 126 mx_unlock_file (ctx->realpath, fileno (ci->lockfp), 1); 127 128 ci->locked = 0; 129 safe_fclose (&ci->lockfp); 130} 131 132/** 133 * setup_paths - Set the mailbox paths 134 * @ctx: Mailbox to modify 135 * 136 * Save the compressed filename in ctx->realpath. 137 * Create a temporary filename and put its name in ctx->path. 138 * The temporary file is created to prevent symlink attacks. 139 * 140 * Returns: 141 * 0: Success 142 * -1: Error 143 */ 144static int 145setup_paths (CONTEXT *ctx) 146{ 147 if (!ctx) 148 return -1; 149 150 char tmppath[_POSIX_PATH_MAX]; 151 FILE *tmpfp; 152 153 /* Setup the right paths */ 154 FREE(&ctx->realpath); 155 ctx->realpath = ctx->path; 156 157 /* We will uncompress to /tmp */ 158 mutt_mktemp (tmppath, sizeof (tmppath)); 159 ctx->path = safe_strdup (tmppath); 160 161 if ((tmpfp = safe_fopen (ctx->path, "w")) == NULL) 162 return -1; 163 164 safe_fclose (&tmpfp); 165 return 0; 166} 167 168/** 169 * get_size - Get the size of a file 170 * @path: File to measure 171 * 172 * Returns: 173 * number: Size in bytes 174 * 0: On error 175 */ 176static int 177get_size (const char *path) 178{ 179 if (!path) 180 return 0; 181 182 struct stat sb; 183 if (stat (path, &sb) != 0) 184 return 0; 185 186 return sb.st_size; 187} 188 189/** 190 * store_size - Save the size of the compressed file 191 * @ctx: Mailbox 192 * 193 * Save the compressed file size in the compress_info struct. 194 */ 195static void 196store_size (const CONTEXT *ctx) 197{ 198 if (!ctx) 199 return; 200 201 COMPRESS_INFO *ci = ctx->compress_info; 202 if (!ci) 203 return; 204 205 ci->size = get_size (ctx->realpath); 206} 207 208/** 209 * find_hook - Find a hook to match a path 210 * @type: Type of hook, e.g. MUTT_CLOSEHOOK 211 * @path: Filename to test 212 * 213 * Each hook has a type and a pattern. 214 * Find a command that matches the type and path supplied. e.g. 215 * 216 * User config: 217 * open-hook '\.gz$' "gzip -cd '%f' > '%t'" 218 * 219 * Call: 220 * find_hook (MUTT_OPENHOOK, "myfile.gz"); 221 * 222 * Returns: 223 * string: Matching hook command 224 * NULL: No matches 225 */ 226static const char * 227find_hook (int type, const char *path) 228{ 229 if (!path) 230 return NULL; 231 232 const char *c = mutt_find_hook (type, path); 233 if (!c || !*c) 234 return NULL; 235 236 return c; 237} 238 239/** 240 * set_compress_info - Find the compress hooks for a mailbox 241 * @ctx: Mailbox to examine 242 * 243 * When a mailbox is opened, we check if there are any matching hooks. 244 * 245 * Returns: 246 * COMPRESS_INFO: Hook info for the mailbox's path 247 * NULL: On error 248 */ 249static COMPRESS_INFO * 250set_compress_info (CONTEXT *ctx) 251{ 252 if (!ctx || !ctx->path) 253 return NULL; 254 255 if (ctx->compress_info) 256 return ctx->compress_info; 257 258 /* Open is compulsory */ 259 const char *o = find_hook (MUTT_OPENHOOK, ctx->path); 260 if (!o) 261 return NULL; 262 263 const char *c = find_hook (MUTT_CLOSEHOOK, ctx->path); 264 const char *a = find_hook (MUTT_APPENDHOOK, ctx->path); 265 266 COMPRESS_INFO *ci = safe_calloc (1, sizeof (COMPRESS_INFO)); 267 ctx->compress_info = ci; 268 269 ci->open = safe_strdup (o); 270 ci->close = safe_strdup (c); 271 ci->append = safe_strdup (a); 272 273 return ci; 274} 275 276/** 277 * mutt_free_compress_info - Frees the compress info members and structure. 278 * @ctx: Mailbox to free compress_info for. 279 */ 280void 281mutt_free_compress_info (CONTEXT *ctx) 282{ 283 COMPRESS_INFO *ci; 284 285 if (!ctx || !ctx->compress_info) 286 return; 287 288 ci = ctx->compress_info; 289 FREE (&ci->open); 290 FREE (&ci->close); 291 FREE (&ci->append); 292 293 unlock_realpath (ctx); 294 295 FREE (&ctx->compress_info); 296} 297 298/** 299 * escape_path - Escapes single quotes in a path for a command string. 300 * @src - the path to escape. 301 * 302 * Returns: a pointer to the escaped string. 303 */ 304static char * 305escape_path (char *src) 306{ 307 static char dest[HUGE_STRING]; 308 char *destp = dest; 309 int destsize = 0; 310 311 if (!src) 312 return NULL; 313 314 while (*src && (destsize < sizeof(dest) - 1)) 315 { 316 if (*src != '\'') 317 { 318 *destp++ = *src++; 319 destsize++; 320 } 321 else 322 { 323 /* convert ' into '\'' */ 324 if (destsize + 4 < sizeof(dest)) 325 { 326 *destp++ = *src++; 327 *destp++ = '\\'; 328 *destp++ = '\''; 329 *destp++ = '\''; 330 destsize += 4; 331 } 332 else 333 break; 334 } 335 } 336 *destp = '\0'; 337 338 return dest; 339} 340 341/** 342 * cb_format_str - Expand the filenames in the command string 343 * @dest: Buffer in which to save string 344 * @destlen: Buffer length 345 * @col: Starting column, UNUSED 346 * @cols: Number of screen columns, UNUSED 347 * @op: printf-like operator, e.g. 't' 348 * @src: printf-like format string 349 * @fmt: Field formatting string, UNUSED 350 * @ifstring: If condition is met, display this string, UNUSED 351 * @elsestring: Otherwise, display this string, UNUSED 352 * @data: Pointer to the mailbox CONTEXT 353 * @flags: Format flags, UNUSED 354 * 355 * cb_format_str is a callback function for mutt_FormatString. It understands 356 * two operators. '%f' : 'from' filename, '%t' : 'to' filename. 357 * 358 * Returns: src (unchanged) 359 */ 360static const char * 361cb_format_str (char *dest, size_t destlen, size_t col, int cols, char op, const char *src, 362 const char *fmt, const char *ifstring, const char *elsestring, 363 unsigned long data, format_flag flags) 364{ 365 if (!dest || (data == 0)) 366 return src; 367 368 CONTEXT *ctx = (CONTEXT *) data; 369 370 switch (op) 371 { 372 case 'f': 373 /* Compressed file */ 374 snprintf (dest, destlen, "%s", NONULL (escape_path (ctx->realpath))); 375 break; 376 case 't': 377 /* Plaintext, temporary file */ 378 snprintf (dest, destlen, "%s", NONULL (escape_path (ctx->path))); 379 break; 380 } 381 return src; 382} 383 384/** 385 * expand_command_str - Expand placeholders in command string 386 * @ctx: Mailbox for paths 387 * @buf: Buffer to store the command 388 * @buflen: Size of the buffer 389 * 390 * This function takes a hook command and expands the filename placeholders 391 * within it. The function calls mutt_FormatString() to do the replacement 392 * which calls our callback function cb_format_str(). e.g. 393 * 394 * Template command: 395 * gzip -cd '%f' > '%t' 396 * 397 * Result: 398 * gzip -dc '~/mail/abc.gz' > '/tmp/xyz' 399 */ 400static void 401expand_command_str (const CONTEXT *ctx, const char *cmd, char *buf, int buflen) 402{ 403 if (!ctx || !cmd || !buf) 404 return; 405 406 mutt_FormatString (buf, buflen, 0, buflen, cmd, cb_format_str, (unsigned long) ctx, 0); 407} 408 409/** 410 * execute_command - Run a system command 411 * @ctx: Mailbox to work with 412 * @command: Command string to execute 413 * @progress: Message to show the user 414 * 415 * Run the supplied command, taking care of all the Mutt requirements, 416 * such as locking files and blocking signals. 417 * 418 * Returns: 419 * 1: Success 420 * 0: Failure 421 */ 422static int 423execute_command (CONTEXT *ctx, const char *command, const char *progress) 424{ 425 int rc = 1; 426 char sys_cmd[HUGE_STRING]; 427 428 if (!ctx || !command || !progress) 429 return 0; 430 431 if (!ctx->quiet) 432 mutt_message (progress, ctx->realpath); 433 434 mutt_block_signals(); 435 endwin(); 436 fflush (stdout); 437 438 expand_command_str (ctx, command, sys_cmd, sizeof (sys_cmd)); 439 440 if (mutt_system (sys_cmd) != 0) 441 { 442 rc = 0; 443 mutt_any_key_to_continue (NULL); 444 mutt_error (_("Error running \"%s\"!"), sys_cmd); 445 } 446 447 mutt_unblock_signals(); 448 449 return rc; 450} 451 452/** 453 * open_mailbox - Open a compressed mailbox 454 * @ctx: Mailbox to open 455 * 456 * Set up a compressed mailbox to be read. 457 * Decompress the mailbox and set up the paths and hooks needed. 458 * Then determine the type of the mailbox so we can delegate the handling of 459 * messages. 460 */ 461static int 462open_mailbox (CONTEXT *ctx) 463{ 464 if (!ctx || (ctx->magic != MUTT_COMPRESSED)) 465 return -1; 466 467 COMPRESS_INFO *ci = set_compress_info (ctx); 468 if (!ci) 469 return -1; 470 471 /* If there's no close-hook, or the file isn't writable */ 472 if (!ci->close || (access (ctx->path, W_OK) != 0)) 473 ctx->readonly = 1; 474 475 if (setup_paths (ctx) != 0) 476 goto or_fail; 477 store_size (ctx); 478 479 if (!lock_realpath (ctx, 0)) 480 { 481 mutt_error (_("Unable to lock mailbox!")); 482 goto or_fail; 483 } 484 485 int rc = execute_command (ctx, ci->open, _("Decompressing %s")); 486 if (rc == 0) 487 goto or_fail; 488 489 unlock_realpath (ctx); 490 491 ctx->magic = mx_get_magic (ctx->path); 492 if (ctx->magic == 0) 493 { 494 mutt_error (_("Can't identify the contents of the compressed file")); 495 goto or_fail; 496 } 497 498 ci->child_ops = mx_get_ops (ctx->magic); 499 if (!ci->child_ops) 500 { 501 mutt_error (_("Can't find mailbox ops for mailbox type %d"), ctx->magic); 502 goto or_fail; 503 } 504 505 return ci->child_ops->open (ctx); 506 507or_fail: 508 /* remove the partial uncompressed file */ 509 remove (ctx->path); 510 mutt_free_compress_info (ctx); 511 return -1; 512} 513 514/** 515 * open_append_mailbox - Open a compressed mailbox for appending 516 * @ctx: Mailbox to open 517 * @flags: e.g. Does the file already exist? 518 * 519 * To append to a compressed mailbox we need an append-hook (or both open- and 520 * close-hooks). 521 * 522 * Returns: 523 * 0: Success 524 * -1: Failure 525 */ 526static int 527open_append_mailbox (CONTEXT *ctx, int flags) 528{ 529 if (!ctx) 530 return -1; 531 532 /* If this succeeds, we know there's an open-hook */ 533 COMPRESS_INFO *ci = set_compress_info (ctx); 534 if (!ci) 535 return -1; 536 537 /* To append we need an append-hook or a close-hook */ 538 if (!ci->append && !ci->close) 539 { 540 mutt_error (_("Cannot append without an append-hook or close-hook : %s"), ctx->path); 541 goto oa_fail1; 542 } 543 544 if (setup_paths (ctx) != 0) 545 goto oa_fail2; 546 547 /* Lock the realpath for the duration of the append. 548 * It will be unlocked in the close */ 549 if (!lock_realpath (ctx, 1)) 550 { 551 mutt_error (_("Unable to lock mailbox!")); 552 goto oa_fail2; 553 } 554 555 /* Open the existing mailbox, unless we are appending */ 556 if (!ci->append && (get_size (ctx->realpath) > 0)) 557 { 558 int rc = execute_command (ctx, ci->open, _("Decompressing %s")); 559 if (rc == 0) 560 { 561 mutt_error (_("Compress command failed: %s"), ci->open); 562 goto oa_fail2; 563 } 564 ctx->magic = mx_get_magic (ctx->path); 565 } 566 else 567 ctx->magic = DefaultMagic; 568 569 /* We can only deal with mbox and mmdf mailboxes */ 570 if ((ctx->magic != MUTT_MBOX) && (ctx->magic != MUTT_MMDF)) 571 { 572 mutt_error (_("Unsupported mailbox type for appending.")); 573 goto oa_fail2; 574 } 575 576 ci->child_ops = mx_get_ops (ctx->magic); 577 if (!ci->child_ops) 578 { 579 mutt_error (_("Can't find mailbox ops for mailbox type %d"), ctx->magic); 580 goto oa_fail2; 581 } 582 583 if (ci->child_ops->open_append (ctx, flags) != 0) 584 goto oa_fail2; 585 586 return 0; 587 588oa_fail2: 589 /* remove the partial uncompressed file */ 590 remove (ctx->path); 591oa_fail1: 592 /* Free the compress_info to prevent close from trying to recompress */ 593 mutt_free_compress_info (ctx); 594 595 return -1; 596} 597 598/** 599 * close_mailbox - Close a compressed mailbox 600 * @ctx: Mailbox to close 601 * 602 * If the mailbox has been changed then re-compress the tmp file. 603 * Then delete the tmp file. 604 * 605 * Returns: 606 * 0: Success 607 * -1: Failure 608 */ 609static int 610close_mailbox (CONTEXT *ctx) 611{ 612 if (!ctx) 613 return -1; 614 615 COMPRESS_INFO *ci = ctx->compress_info; 616 if (!ci) 617 return -1; 618 619 struct mx_ops *ops = ci->child_ops; 620 if (!ops) 621 return -1; 622 623 ops->close (ctx); 624 625 /* sync has already been called, so we only need to delete some files */ 626 if (!ctx->append) 627 { 628 /* If the file was removed, remove the compressed folder too */ 629 if ((access (ctx->path, F_OK) != 0) && !option (OPTSAVEEMPTY)) 630 { 631 remove (ctx->realpath); 632 } 633 else 634 { 635 remove (ctx->path); 636 } 637 638 return 0; 639 } 640 641 const char *append; 642 const char *msg; 643 644 /* The file exists and we can append */ 645 if ((access (ctx->realpath, F_OK) == 0) && ci->append) 646 { 647 append = ci->append; 648 msg = _("Compressed-appending to %s..."); 649 } 650 else 651 { 652 append = ci->close; 653 msg = _("Compressing %s..."); 654 } 655 656 int rc = execute_command (ctx, append, msg); 657 if (rc == 0) 658 { 659 mutt_any_key_to_continue (NULL); 660 mutt_error (_("Error. Preserving temporary file: %s"), ctx->path); 661 } 662 else 663 remove (ctx->path); 664 665 unlock_realpath (ctx); 666 667 return 0; 668} 669 670/** 671 * check_mailbox - Check for changes in the compressed file 672 * @ctx: Mailbox 673 * 674 * If the compressed file changes in size but the mailbox hasn't been changed 675 * in Mutt, then we can close and reopen the mailbox. 676 * 677 * If the mailbox has been changed in Mutt, warn the user. 678 * 679 * The return codes are picked to match mx_check_mailbox(). 680 * 681 * Returns: 682 * 0: Mailbox OK 683 * MUTT_REOPENED: The mailbox was closed and reopened 684 * -1: Mailbox bad 685 */ 686static int 687check_mailbox (CONTEXT *ctx, int *index_hint) 688{ 689 if (!ctx) 690 return -1; 691 692 COMPRESS_INFO *ci = ctx->compress_info; 693 if (!ci) 694 return -1; 695 696 struct mx_ops *ops = ci->child_ops; 697 if (!ops) 698 return -1; 699 700 int size = get_size (ctx->realpath); 701 if (size == ci->size) 702 return 0; 703 704 if (!lock_realpath (ctx, 0)) 705 { 706 mutt_error (_("Unable to lock mailbox!")); 707 return -1; 708 } 709 710 int rc = execute_command (ctx, ci->open, _("Decompressing %s")); 711 store_size (ctx); 712 unlock_realpath (ctx); 713 if (rc == 0) 714 return -1; 715 716 return ops->check (ctx, index_hint); 717} 718 719 720/** 721 * open_message - Delegated to mbox handler 722 */ 723static int 724open_message (CONTEXT *ctx, MESSAGE *msg, int msgno) 725{ 726 if (!ctx) 727 return -1; 728 729 COMPRESS_INFO *ci = ctx->compress_info; 730 if (!ci) 731 return -1; 732 733 struct mx_ops *ops = ci->child_ops; 734 if (!ops) 735 return -1; 736 737 /* Delegate */ 738 return ops->open_msg (ctx, msg, msgno); 739} 740 741/** 742 * close_message - Delegated to mbox handler 743 */ 744static int 745close_message (CONTEXT *ctx, MESSAGE *msg) 746{ 747 if (!ctx) 748 return -1; 749 750 COMPRESS_INFO *ci = ctx->compress_info; 751 if (!ci) 752 return -1; 753 754 struct mx_ops *ops = ci->child_ops; 755 if (!ops) 756 return -1; 757 758 /* Delegate */ 759 return ops->close_msg (ctx, msg); 760} 761 762/** 763 * commit_message - Delegated to mbox handler 764 */ 765static int 766commit_message (CONTEXT *ctx, MESSAGE *msg) 767{ 768 if (!ctx) 769 return -1; 770 771 COMPRESS_INFO *ci = ctx->compress_info; 772 if (!ci) 773 return -1; 774 775 struct mx_ops *ops = ci->child_ops; 776 if (!ops) 777 return -1; 778 779 /* Delegate */ 780 return ops->commit_msg (ctx, msg); 781} 782 783/** 784 * open_new_message - Delegated to mbox handler 785 */ 786static int 787open_new_message (MESSAGE *msg, CONTEXT *ctx, HEADER *hdr) 788{ 789 if (!ctx) 790 return -1; 791 792 COMPRESS_INFO *ci = ctx->compress_info; 793 if (!ci) 794 return -1; 795 796 struct mx_ops *ops = ci->child_ops; 797 if (!ops) 798 return -1; 799 800 /* Delegate */ 801 return ops->open_new_msg (msg, ctx, hdr); 802} 803 804 805/** 806 * mutt_comp_can_append - Can we append to this path? 807 * @path: pathname of file to be tested 808 * 809 * To append to a file we can either use an 'append-hook' or a combination of 810 * 'open-hook' and 'close-hook'. 811 * 812 * A match means it's our responsibility to append to the file. 813 * 814 * Returns: 815 * 1: Yes, we can append to the file 816 * 0: No, appending isn't possible 817 */ 818int 819mutt_comp_can_append (CONTEXT *ctx) 820{ 821 if (!ctx) 822 return 0; 823 824 /* If this succeeds, we know there's an open-hook */ 825 COMPRESS_INFO *ci = set_compress_info (ctx); 826 if (!ci) 827 return 0; 828 829 /* We have an open-hook, so to append we need an append-hook, 830 * or a close-hook. */ 831 if (ci->append || ci->close) 832 return 1; 833 834 mutt_error (_("Cannot append without an append-hook or close-hook : %s"), ctx->path); 835 return 0; 836} 837 838/** 839 * mutt_comp_can_read - Can we read from this file? 840 * @path: Pathname of file to be tested 841 * 842 * Search for an 'open-hook' with a regex that matches the path. 843 * 844 * A match means it's our responsibility to open the file. 845 * 846 * Returns: 847 * 1: Yes, we can read the file 848 * 0: No, we cannot read the file 849 */ 850int 851mutt_comp_can_read (const char *path) 852{ 853 if (!path) 854 return 0; 855 856 if (find_hook (MUTT_OPENHOOK, path)) 857 return 1; 858 else 859 return 0; 860} 861 862/** 863 * sync_mailbox - Save changes to the compressed mailbox file 864 * @ctx: Mailbox to sync 865 * 866 * Changes in Mutt only affect the tmp file. Calling sync_mailbox() 867 * will commit them to the compressed file. 868 * 869 * Returns: 870 * 0: Success 871 * -1: Failure 872 */ 873static int 874sync_mailbox (CONTEXT *ctx, int *index_hint) 875{ 876 if (!ctx) 877 return -1; 878 879 COMPRESS_INFO *ci = ctx->compress_info; 880 if (!ci) 881 return -1; 882 883 if (!ci->close) 884 { 885 mutt_error (_("Can't sync a compressed file without a close-hook")); 886 return -1; 887 } 888 889 struct mx_ops *ops = ci->child_ops; 890 if (!ops) 891 return -1; 892 893 if (!lock_realpath (ctx, 1)) 894 { 895 mutt_error (_("Unable to lock mailbox!")); 896 return -1; 897 } 898 899 int rc = check_mailbox (ctx, index_hint); 900 if (rc != 0) 901 goto sync_cleanup; 902 903 rc = ops->sync (ctx, index_hint); 904 if (rc != 0) 905 goto sync_cleanup; 906 907 rc = execute_command (ctx, ci->close, _("Compressing %s")); 908 if (rc == 0) 909 { 910 rc = -1; 911 goto sync_cleanup; 912 } 913 914 rc = 0; 915 916sync_cleanup: 917 store_size (ctx); 918 unlock_realpath (ctx); 919 return rc; 920} 921 922/** 923 * mutt_comp_valid_command - Is this command string allowed? 924 * @cmd: Command string 925 * 926 * A valid command string must have both "%f" (from file) and "%t" (to file). 927 * We don't check if we can actually run the command. 928 * 929 * Returns: 930 * 1: Valid command 931 * 0: "%f" and/or "%t" is missing 932 */ 933int 934mutt_comp_valid_command (const char *cmd) 935{ 936 if (!cmd) 937 return 0; 938 939 return (strstr (cmd, "%f") && strstr (cmd, "%t")); 940} 941 942 943/** 944 * mx_comp_ops - Mailbox callback functions 945 * 946 * Compress only uses open, close and check. 947 * The message functions are delegated to mbox. 948 */ 949struct mx_ops mx_comp_ops = 950{ 951 .open = open_mailbox, 952 .open_append = open_append_mailbox, 953 .close = close_mailbox, 954 .check = check_mailbox, 955 .sync = sync_mailbox, 956 .open_msg = open_message, 957 .close_msg = close_message, 958 .commit_msg = commit_message, 959 .open_new_msg = open_new_message 960}; 961