mutt stable branch with some hacks
at jcs 955 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 BUFFER *tmppath; 148 FILE *tmpfp; 149 150 if (!ctx) 151 return -1; 152 153 /* Setup the right paths */ 154 FREE(&ctx->realpath); 155 ctx->realpath = ctx->path; 156 157 /* We will uncompress to /tmp */ 158 tmppath = mutt_buffer_pool_get (); 159 mutt_buffer_mktemp (tmppath); 160 ctx->path = safe_strdup (mutt_b2s (tmppath)); 161 mutt_buffer_pool_release (&tmppath); 162 163 if ((tmpfp = safe_fopen (ctx->path, "w")) == NULL) 164 return -1; 165 166 safe_fclose (&tmpfp); 167 return 0; 168} 169 170/** 171 * get_size - Get the size of a file 172 * @path: File to measure 173 * 174 * Returns: 175 * number: Size in bytes 176 * 0: On error 177 */ 178static int 179get_size (const char *path) 180{ 181 if (!path) 182 return 0; 183 184 struct stat sb; 185 if (stat (path, &sb) != 0) 186 return 0; 187 188 return sb.st_size; 189} 190 191/** 192 * store_size - Save the size of the compressed file 193 * @ctx: Mailbox 194 * 195 * Save the compressed file size in the compress_info struct. 196 */ 197static void 198store_size (const CONTEXT *ctx) 199{ 200 if (!ctx) 201 return; 202 203 COMPRESS_INFO *ci = ctx->compress_info; 204 if (!ci) 205 return; 206 207 ci->size = get_size (ctx->realpath); 208} 209 210/** 211 * find_hook - Find a hook to match a path 212 * @type: Type of hook, e.g. MUTT_CLOSEHOOK 213 * @path: Filename to test 214 * 215 * Each hook has a type and a pattern. 216 * Find a command that matches the type and path supplied. e.g. 217 * 218 * User config: 219 * open-hook '\.gz$' "gzip -cd '%f' > '%t'" 220 * 221 * Call: 222 * find_hook (MUTT_OPENHOOK, "myfile.gz"); 223 * 224 * Returns: 225 * string: Matching hook command 226 * NULL: No matches 227 */ 228static const char * 229find_hook (int type, const char *path) 230{ 231 if (!path) 232 return NULL; 233 234 const char *c = mutt_find_hook (type, path); 235 if (!c || !*c) 236 return NULL; 237 238 return c; 239} 240 241/** 242 * set_compress_info - Find the compress hooks for a mailbox 243 * @ctx: Mailbox to examine 244 * 245 * When a mailbox is opened, we check if there are any matching hooks. 246 * 247 * Returns: 248 * COMPRESS_INFO: Hook info for the mailbox's path 249 * NULL: On error 250 */ 251static COMPRESS_INFO * 252set_compress_info (CONTEXT *ctx) 253{ 254 if (!ctx || !ctx->path) 255 return NULL; 256 257 if (ctx->compress_info) 258 return ctx->compress_info; 259 260 /* Open is compulsory */ 261 const char *o = find_hook (MUTT_OPENHOOK, ctx->path); 262 if (!o) 263 return NULL; 264 265 const char *c = find_hook (MUTT_CLOSEHOOK, ctx->path); 266 const char *a = find_hook (MUTT_APPENDHOOK, ctx->path); 267 268 COMPRESS_INFO *ci = safe_calloc (1, sizeof (COMPRESS_INFO)); 269 ctx->compress_info = ci; 270 271 ci->open = safe_strdup (o); 272 ci->close = safe_strdup (c); 273 ci->append = safe_strdup (a); 274 275 return ci; 276} 277 278/** 279 * mutt_free_compress_info - Frees the compress info members and structure. 280 * @ctx: Mailbox to free compress_info for. 281 */ 282void 283mutt_free_compress_info (CONTEXT *ctx) 284{ 285 COMPRESS_INFO *ci; 286 287 if (!ctx || !ctx->compress_info) 288 return; 289 290 ci = ctx->compress_info; 291 FREE (&ci->open); 292 FREE (&ci->close); 293 FREE (&ci->append); 294 295 unlock_realpath (ctx); 296 297 FREE (&ctx->compress_info); 298} 299 300/** 301 * cb_format_str - Expand the filenames in the command string 302 * @dest: Buffer in which to save string 303 * @destlen: Buffer length 304 * @col: Starting column, UNUSED 305 * @cols: Number of screen columns, UNUSED 306 * @op: printf-like operator, e.g. 't' 307 * @src: printf-like format string 308 * @fmt: Field formatting string, UNUSED 309 * @ifstring: If condition is met, display this string, UNUSED 310 * @elsestring: Otherwise, display this string, UNUSED 311 * @data: Pointer to the mailbox CONTEXT 312 * @flags: Format flags, UNUSED 313 * 314 * cb_format_str is a callback function for mutt_FormatString. It understands 315 * two operators. '%f' : 'from' filename, '%t' : 'to' filename. 316 * 317 * Returns: src (unchanged) 318 */ 319static const char * 320cb_format_str (char *dest, size_t destlen, size_t col, int cols, char op, const char *src, 321 const char *fmt, const char *ifstring, const char *elsestring, 322 unsigned long data, format_flag flags) 323{ 324 CONTEXT *ctx = (CONTEXT *) data; 325 BUFFER *quoted = NULL; 326 327 if (!dest || (data == 0)) 328 return src; 329 330 /* NOTE the compressed file config vars expect %f and %t to be 331 * surrounded by '' (unlike other Mutt config vars, which add the 332 * outer quotes for the user). This is why we use the 333 * _mutt_buffer_quote_filename() form with add_outer of 0. */ 334 quoted = mutt_buffer_pool_get (); 335 336 switch (op) 337 { 338 case 'f': 339 /* Compressed file */ 340 _mutt_buffer_quote_filename (quoted, ctx->realpath, 0); 341 snprintf (dest, destlen, "%s", mutt_b2s (quoted)); 342 break; 343 case 't': 344 /* Plaintext, temporary file */ 345 _mutt_buffer_quote_filename (quoted, ctx->path, 0); 346 snprintf (dest, destlen, "%s", mutt_b2s (quoted)); 347 break; 348 } 349 350 mutt_buffer_pool_release (&quoted); 351 return src; 352} 353 354/** 355 * expand_command_str - Expand placeholders in command string 356 * @ctx: Mailbox for paths 357 * @buf: Buffer to store the command 358 * @buflen: Size of the buffer 359 * 360 * This function takes a hook command and expands the filename placeholders 361 * within it. The function calls mutt_FormatString() to do the replacement 362 * which calls our callback function cb_format_str(). e.g. 363 * 364 * Template command: 365 * gzip -cd '%f' > '%t' 366 * 367 * Result: 368 * gzip -dc '~/mail/abc.gz' > '/tmp/xyz' 369 */ 370static void 371expand_command_str (const CONTEXT *ctx, const char *cmd, char *buf, int buflen) 372{ 373 if (!ctx || !cmd || !buf) 374 return; 375 376 mutt_FormatString (buf, buflen, 0, buflen, cmd, cb_format_str, (unsigned long) ctx, 0); 377} 378 379/** 380 * execute_command - Run a system command 381 * @ctx: Mailbox to work with 382 * @command: Command string to execute 383 * @progress: Message to show the user 384 * 385 * Run the supplied command, taking care of all the Mutt requirements, 386 * such as locking files and blocking signals. 387 * 388 * Returns: 389 * 1: Success 390 * 0: Failure 391 */ 392static int 393execute_command (CONTEXT *ctx, const char *command, const char *progress) 394{ 395 int rc = 1; 396 char sys_cmd[HUGE_STRING]; 397 398 if (!ctx || !command || !progress) 399 return 0; 400 401 if (!ctx->quiet) 402 mutt_message (progress, ctx->realpath); 403 404 mutt_block_signals(); 405 endwin(); 406 fflush (stdout); 407 408 expand_command_str (ctx, command, sys_cmd, sizeof (sys_cmd)); 409 410 if (mutt_system (sys_cmd) != 0) 411 { 412 rc = 0; 413 mutt_any_key_to_continue (NULL); 414 mutt_error (_("Error running \"%s\"!"), sys_cmd); 415 } 416 417 mutt_unblock_signals(); 418 419 return rc; 420} 421 422/** 423 * open_mailbox - Open a compressed mailbox 424 * @ctx: Mailbox to open 425 * 426 * Set up a compressed mailbox to be read. 427 * Decompress the mailbox and set up the paths and hooks needed. 428 * Then determine the type of the mailbox so we can delegate the handling of 429 * messages. 430 */ 431static int 432open_mailbox (CONTEXT *ctx) 433{ 434 if (!ctx || (ctx->magic != MUTT_COMPRESSED)) 435 return -1; 436 437 COMPRESS_INFO *ci = set_compress_info (ctx); 438 if (!ci) 439 return -1; 440 441 /* If there's no close-hook, or the file isn't writable */ 442 if (!ci->close || (access (ctx->path, W_OK) != 0)) 443 ctx->readonly = 1; 444 445 if (setup_paths (ctx) != 0) 446 goto or_fail; 447 store_size (ctx); 448 449 if (!lock_realpath (ctx, 0)) 450 { 451 mutt_error (_("Unable to lock mailbox!")); 452 goto or_fail; 453 } 454 455 int rc = execute_command (ctx, ci->open, _("Decompressing %s")); 456 if (rc == 0) 457 goto or_fail; 458 459 unlock_realpath (ctx); 460 461 ctx->magic = mx_get_magic (ctx->path); 462 if (ctx->magic == 0) 463 { 464 mutt_error (_("Can't identify the contents of the compressed file")); 465 goto or_fail; 466 } 467 468 ci->child_ops = mx_get_ops (ctx->magic); 469 if (!ci->child_ops) 470 { 471 mutt_error (_("Can't find mailbox ops for mailbox type %d"), ctx->magic); 472 goto or_fail; 473 } 474 475 return ci->child_ops->open (ctx); 476 477or_fail: 478 /* remove the partial uncompressed file */ 479 remove (ctx->path); 480 mutt_free_compress_info (ctx); 481 return -1; 482} 483 484/** 485 * open_append_mailbox - Open a compressed mailbox for appending 486 * @ctx: Mailbox to open 487 * @flags: e.g. Does the file already exist? 488 * 489 * To append to a compressed mailbox we need an append-hook (or both open- and 490 * close-hooks). 491 * 492 * Returns: 493 * 0: Success 494 * -1: Failure 495 */ 496static int 497open_append_mailbox (CONTEXT *ctx, int flags) 498{ 499 if (!ctx) 500 return -1; 501 502 /* If this succeeds, we know there's an open-hook */ 503 COMPRESS_INFO *ci = set_compress_info (ctx); 504 if (!ci) 505 return -1; 506 507 /* To append we need an append-hook or a close-hook */ 508 if (!ci->append && !ci->close) 509 { 510 mutt_error (_("Cannot append without an append-hook or close-hook : %s"), ctx->path); 511 goto oa_fail1; 512 } 513 514 if (setup_paths (ctx) != 0) 515 goto oa_fail2; 516 517 /* Lock the realpath for the duration of the append. 518 * It will be unlocked in the close */ 519 if (!lock_realpath (ctx, 1)) 520 { 521 mutt_error (_("Unable to lock mailbox!")); 522 goto oa_fail2; 523 } 524 525 /* Open the existing mailbox, unless we are appending */ 526 if (!ci->append && (get_size (ctx->realpath) > 0)) 527 { 528 int rc = execute_command (ctx, ci->open, _("Decompressing %s")); 529 if (rc == 0) 530 { 531 mutt_error (_("Compress command failed: %s"), ci->open); 532 goto oa_fail2; 533 } 534 ctx->magic = mx_get_magic (ctx->path); 535 } 536 else 537 ctx->magic = DefaultMagic; 538 539 /* We can only deal with mbox and mmdf mailboxes */ 540 if ((ctx->magic != MUTT_MBOX) && (ctx->magic != MUTT_MMDF)) 541 { 542 mutt_error (_("Unsupported mailbox type for appending.")); 543 goto oa_fail2; 544 } 545 546 ci->child_ops = mx_get_ops (ctx->magic); 547 if (!ci->child_ops) 548 { 549 mutt_error (_("Can't find mailbox ops for mailbox type %d"), ctx->magic); 550 goto oa_fail2; 551 } 552 553 if (ci->child_ops->open_append (ctx, flags) != 0) 554 goto oa_fail2; 555 556 return 0; 557 558oa_fail2: 559 /* remove the partial uncompressed file */ 560 remove (ctx->path); 561oa_fail1: 562 /* Free the compress_info to prevent close from trying to recompress */ 563 mutt_free_compress_info (ctx); 564 565 return -1; 566} 567 568/** 569 * close_mailbox - Close a compressed mailbox 570 * @ctx: Mailbox to close 571 * 572 * If the mailbox has been changed then re-compress the tmp file. 573 * Then delete the tmp file. 574 * 575 * Returns: 576 * 0: Success 577 * -1: Failure 578 */ 579static int 580close_mailbox (CONTEXT *ctx) 581{ 582 if (!ctx) 583 return -1; 584 585 COMPRESS_INFO *ci = ctx->compress_info; 586 if (!ci) 587 return -1; 588 589 struct mx_ops *ops = ci->child_ops; 590 if (!ops) 591 return -1; 592 593 ops->close (ctx); 594 595 /* sync has already been called, so we only need to delete some files */ 596 if (!ctx->append) 597 { 598 /* If the file was removed, remove the compressed folder too */ 599 if ((access (ctx->path, F_OK) != 0) && !option (OPTSAVEEMPTY)) 600 { 601 remove (ctx->realpath); 602 } 603 else 604 { 605 remove (ctx->path); 606 } 607 608 return 0; 609 } 610 611 const char *append; 612 const char *msg; 613 614 /* The file exists and we can append */ 615 if ((access (ctx->realpath, F_OK) == 0) && ci->append) 616 { 617 append = ci->append; 618 msg = _("Compressed-appending to %s..."); 619 } 620 else 621 { 622 append = ci->close; 623 msg = _("Compressing %s..."); 624 } 625 626 int rc = execute_command (ctx, append, msg); 627 if (rc == 0) 628 { 629 mutt_any_key_to_continue (NULL); 630 mutt_error (_("Error. Preserving temporary file: %s"), ctx->path); 631 } 632 else 633 remove (ctx->path); 634 635 unlock_realpath (ctx); 636 637 return 0; 638} 639 640/** 641 * check_mailbox - Check for changes in the compressed file 642 * @ctx: Mailbox 643 * 644 * If the compressed file changes in size but the mailbox hasn't been changed 645 * in Mutt, then we can close and reopen the mailbox. 646 * 647 * If the mailbox has been changed in Mutt, warn the user. 648 * 649 * The return codes are picked to match mx_check_mailbox(). 650 * 651 * Returns: 652 * 0: Mailbox OK 653 * MUTT_REOPENED: The mailbox was closed and reopened 654 * -1: Mailbox bad 655 */ 656static int 657check_mailbox (CONTEXT *ctx, int *index_hint) 658{ 659 if (!ctx) 660 return -1; 661 662 COMPRESS_INFO *ci = ctx->compress_info; 663 if (!ci) 664 return -1; 665 666 struct mx_ops *ops = ci->child_ops; 667 if (!ops) 668 return -1; 669 670 int size = get_size (ctx->realpath); 671 if (size == ci->size) 672 return 0; 673 674 if (!lock_realpath (ctx, 0)) 675 { 676 mutt_error (_("Unable to lock mailbox!")); 677 return -1; 678 } 679 680 int rc = execute_command (ctx, ci->open, _("Decompressing %s")); 681 store_size (ctx); 682 unlock_realpath (ctx); 683 if (rc == 0) 684 return -1; 685 686 return ops->check (ctx, index_hint); 687} 688 689 690/** 691 * open_message - Delegated to mbox handler 692 */ 693static int 694open_message (CONTEXT *ctx, MESSAGE *msg, int msgno) 695{ 696 if (!ctx) 697 return -1; 698 699 COMPRESS_INFO *ci = ctx->compress_info; 700 if (!ci) 701 return -1; 702 703 struct mx_ops *ops = ci->child_ops; 704 if (!ops) 705 return -1; 706 707 /* Delegate */ 708 return ops->open_msg (ctx, msg, msgno); 709} 710 711/** 712 * close_message - Delegated to mbox handler 713 */ 714static int 715close_message (CONTEXT *ctx, MESSAGE *msg) 716{ 717 if (!ctx) 718 return -1; 719 720 COMPRESS_INFO *ci = ctx->compress_info; 721 if (!ci) 722 return -1; 723 724 struct mx_ops *ops = ci->child_ops; 725 if (!ops) 726 return -1; 727 728 /* Delegate */ 729 return ops->close_msg (ctx, msg); 730} 731 732/** 733 * commit_message - Delegated to mbox handler 734 */ 735static int 736commit_message (CONTEXT *ctx, MESSAGE *msg) 737{ 738 if (!ctx) 739 return -1; 740 741 COMPRESS_INFO *ci = ctx->compress_info; 742 if (!ci) 743 return -1; 744 745 struct mx_ops *ops = ci->child_ops; 746 if (!ops) 747 return -1; 748 749 /* Delegate */ 750 return ops->commit_msg (ctx, msg); 751} 752 753/** 754 * open_new_message - Delegated to mbox handler 755 */ 756static int 757open_new_message (MESSAGE *msg, CONTEXT *ctx, HEADER *hdr) 758{ 759 if (!ctx) 760 return -1; 761 762 COMPRESS_INFO *ci = ctx->compress_info; 763 if (!ci) 764 return -1; 765 766 struct mx_ops *ops = ci->child_ops; 767 if (!ops) 768 return -1; 769 770 /* Delegate */ 771 return ops->open_new_msg (msg, ctx, hdr); 772} 773 774 775/** 776 * mutt_comp_can_append - Can we append to this path? 777 * @path: pathname of file to be tested 778 * 779 * To append to a file we can either use an 'append-hook' or a combination of 780 * 'open-hook' and 'close-hook'. 781 * 782 * A match means it's our responsibility to append to the file. 783 * 784 * Returns: 785 * 1: Yes, we can append to the file 786 * 0: No, appending isn't possible 787 */ 788int 789mutt_comp_can_append (CONTEXT *ctx) 790{ 791 if (!ctx) 792 return 0; 793 794 /* If this succeeds, we know there's an open-hook */ 795 COMPRESS_INFO *ci = set_compress_info (ctx); 796 if (!ci) 797 return 0; 798 799 /* We have an open-hook, so to append we need an append-hook, 800 * or a close-hook. */ 801 if (ci->append || ci->close) 802 return 1; 803 804 mutt_error (_("Cannot append without an append-hook or close-hook : %s"), ctx->path); 805 return 0; 806} 807 808/** 809 * mutt_comp_can_read - Can we read from this file? 810 * @path: Pathname of file to be tested 811 * 812 * Search for an 'open-hook' with a regex that matches the path. 813 * 814 * A match means it's our responsibility to open the file. 815 * 816 * Returns: 817 * 1: Yes, we can read the file 818 * 0: No, we cannot read the file 819 */ 820int 821mutt_comp_can_read (const char *path) 822{ 823 if (!path) 824 return 0; 825 826 if (find_hook (MUTT_OPENHOOK, path)) 827 return 1; 828 else 829 return 0; 830} 831 832/** 833 * sync_mailbox - Save changes to the compressed mailbox file 834 * @ctx: Mailbox to sync 835 * 836 * Changes in Mutt only affect the tmp file. Calling sync_mailbox() 837 * will commit them to the compressed file. 838 * 839 * Returns: 840 * 0: Success 841 * -1: Failure 842 */ 843static int 844sync_mailbox (CONTEXT *ctx, int *index_hint) 845{ 846 if (!ctx) 847 return -1; 848 849 COMPRESS_INFO *ci = ctx->compress_info; 850 if (!ci) 851 return -1; 852 853 if (!ci->close) 854 { 855 mutt_error (_("Can't sync a compressed file without a close-hook")); 856 return -1; 857 } 858 859 struct mx_ops *ops = ci->child_ops; 860 if (!ops) 861 return -1; 862 863 if (!lock_realpath (ctx, 1)) 864 { 865 mutt_error (_("Unable to lock mailbox!")); 866 return -1; 867 } 868 869 int rc = check_mailbox (ctx, index_hint); 870 if (rc != 0) 871 goto sync_cleanup; 872 873 rc = ops->sync (ctx, index_hint); 874 if (rc != 0) 875 goto sync_cleanup; 876 877 rc = execute_command (ctx, ci->close, _("Compressing %s")); 878 if (rc == 0) 879 { 880 rc = -1; 881 goto sync_cleanup; 882 } 883 884 rc = 0; 885 886sync_cleanup: 887 store_size (ctx); 888 unlock_realpath (ctx); 889 return rc; 890} 891 892/** 893 * mutt_comp_valid_command - Is this command string allowed? 894 * @cmd: Command string 895 * 896 * A valid command string must have both "%f" (from file) and "%t" (to file). 897 * We don't check if we can actually run the command. 898 * 899 * Returns: 900 * 1: Valid command 901 * 0: "%f" and/or "%t" is missing 902 */ 903int 904mutt_comp_valid_command (const char *cmd) 905{ 906 if (!cmd) 907 return 0; 908 909 return (strstr (cmd, "%f") && strstr (cmd, "%t")); 910} 911 912/** 913 * compress_msg_padding_size - Returns the padding between messages. 914 */ 915static int 916compress_msg_padding_size (CONTEXT *ctx) 917{ 918 COMPRESS_INFO *ci; 919 struct mx_ops *ops; 920 921 if (!ctx) 922 return 0; 923 924 ci = ctx->compress_info; 925 if (!ci) 926 return 0; 927 928 ops = ci->child_ops; 929 if (!ops || !ops->msg_padding_size) 930 return 0; 931 932 return ops->msg_padding_size (ctx); 933} 934 935 936/** 937 * mx_comp_ops - Mailbox callback functions 938 * 939 * Compress only uses open, close and check. 940 * The message functions are delegated to mbox. 941 */ 942struct mx_ops mx_comp_ops = 943{ 944 .open = open_mailbox, 945 .open_append = open_append_mailbox, 946 .close = close_mailbox, 947 .check = check_mailbox, 948 .sync = sync_mailbox, 949 .open_msg = open_message, 950 .close_msg = close_message, 951 .commit_msg = commit_message, 952 .open_new_msg = open_new_message, 953 .msg_padding_size = compress_msg_padding_size, 954 .save_to_header_cache = NULL, /* compressed doesn't support maildir/mh */ 955};