mutt stable branch with some hacks
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 ("ed);
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};