]> granicus.if.org Git - mutt/commitdiff
Compress patch from the neomutt repository.
authorKevin McCarthy <kevin@8t8.us>
Mon, 14 Nov 2016 04:02:33 +0000 (20:02 -0800)
committerKevin McCarthy <kevin@8t8.us>
Mon, 14 Nov 2016 04:02:33 +0000 (20:02 -0800)
With the following changes:
- po/de.po changes trimmed to just the compress additions.

- Move the sample muttrc to contrib, and add it to the Makefile.am so it is
  distributed.  Remove the sample vimrc.

- Remove extra fluff from manual.

Thanks to Roland Rosenfeld for the original patch, and to the NeoMutt
team for their work cleaning up the patch.

20 files changed:
Makefile.am
commands.c
compress.c [new file with mode: 0644]
compress.h [new file with mode: 0644]
configure.ac
contrib/Makefile.am
contrib/sample.muttrc-compress [new file with mode: 0644]
curs_main.c
doc/Muttrc.head
doc/manual.xml.head
doc/muttrc.man.head
hook.c
init.h
main.c
mutt.h
mx.c
mx.h
po/POTFILES.in
po/de.po
status.c

index 789c23ba0b8cb9c1efceb897f1f3213cd9fb925f..4bc2db477970182bbd6a1c3735a7a733f0b985aa 100644 (file)
@@ -54,7 +54,7 @@ DEFS=-DPKGDATADIR=\"$(pkgdatadir)\" -DSYSCONFDIR=\"$(sysconfdir)\" \
 
 AM_CPPFLAGS=-I. -I$(top_srcdir) $(IMAP_INCLUDES) $(GPGME_CFLAGS) -Iintl
 
-EXTRA_mutt_SOURCES = account.c bcache.c crypt-gpgme.c crypt-mod-pgp-classic.c \
+EXTRA_mutt_SOURCES = account.c bcache.c compress.c crypt-gpgme.c crypt-mod-pgp-classic.c \
        crypt-mod-pgp-gpgme.c crypt-mod-smime-classic.c \
        crypt-mod-smime-gpgme.c dotlock.c gnupgparse.c hcache.c md5.c \
        mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \
@@ -65,7 +65,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c crypt-gpgme.c crypt-mod-pgp-classic.c \
 
 EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP OPS.CRYPT OPS.SMIME TODO UPDATING \
        configure account.h \
-       attach.h buffy.h charset.h copy.h crypthash.h dotlock.h functions.h gen_defs \
+       attach.h buffy.h charset.h compress.h copy.h crypthash.h dotlock.h functions.h gen_defs \
        globals.h hash.h history.h init.h keymap.h mutt_crypt.h \
        mailbox.h mapping.h md5.h mime.h mutt.h mutt_curses.h mutt_menu.h \
        mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \
index 4243623142263f31d7069451aa0c9c4e898eae4e..25b222623ffef5687892aa567711ff3b7d58d2b2 100644 (file)
@@ -837,6 +837,16 @@ int mutt_save_message (HEADER *h, int delete,
 
   if (mx_open_mailbox (buf, MUTT_APPEND, &ctx) != NULL)
   {
+#ifdef USE_COMPRESSED
+    /* If we're saving to a compressed mailbox, the stats won't be updated
+     * until the next open.  Until then, improvise. */
+    BUFFY *cm = NULL;
+    if (ctx.compress_info)
+      cm = mutt_find_mailbox (ctx.realpath);
+    /* We probably haven't been opened yet */
+    if (cm && (cm->msg_count == 0))
+      cm = NULL;
+#endif
     if (h)
     {
       if (_mutt_save_message(h, &ctx, delete, decode, decrypt) != 0)
@@ -844,6 +854,16 @@ int mutt_save_message (HEADER *h, int delete,
         mx_close_mailbox (&ctx, NULL);
         return -1;
       }
+#ifdef USE_COMPRESSED
+      if (cm)
+      {
+        cm->msg_count++;
+        if (!h->read)
+          cm->msg_unread++;
+        if (h->flagged)
+          cm->msg_flagged++;
+      }
+#endif
     }
     else
     {
@@ -858,6 +878,17 @@ int mutt_save_message (HEADER *h, int delete,
             mx_close_mailbox (&ctx, NULL);
             return -1;
           }
+#ifdef USE_COMPRESSED
+          if (cm)
+          {
+            HEADER *h = Context->hdrs[Context->v2r[i]];
+            cm->msg_count++;
+            if (!h->read)
+              cm->msg_unread++;
+            if (h->flagged)
+              cm->msg_flagged++;
+          }
+#endif
        }
       }
     }
diff --git a/compress.c b/compress.c
new file mode 100644 (file)
index 0000000..aadf094
--- /dev/null
@@ -0,0 +1,918 @@
+/* Copyright (C) 1997 Alain Penders <Alain@Finale-Dev.com>
+ * Copyright (C) 2016 Richard Russon <rich@flatcap.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "mutt.h"
+#include "mailbox.h"
+#include "mutt_curses.h"
+#include "mx.h"
+
+/* Notes:
+ * Any references to compressed files also apply to encrypted files.
+ * ctx->path     == plaintext file
+ * ctx->realpath == compressed file
+ */
+
+/**
+ * struct COMPRESS_INFO - Private data for compress
+ *
+ * This object gets attached to the mailbox's CONTEXT.
+ */
+typedef struct
+{
+  const char *append;             /* append-hook command */
+  const char *close;              /* close-hook  command */
+  const char *open;               /* open-hook   command */
+  off_t size;                     /* size of the compressed file */
+  struct mx_ops *child_ops;       /* callbacks of de-compressed file */
+} COMPRESS_INFO;
+
+
+/**
+ * lock_mailbox - Try to lock a mailbox (exclusively)
+ * @ctx:  Mailbox to lock
+ * @fp:   File pointer to the mailbox file
+ * @excl: Lock exclusively?
+ *
+ * Try to (exclusively) lock the mailbox.  If we succeed, then we mark the
+ * mailbox as locked.  If we fail, but we didn't want exclusive rights, then
+ * the mailbox will be marked readonly.
+ *
+ * Returns:
+ *      1: Success (locked or readonly)
+ *      0: Error (can't lock the file)
+ */
+static int
+lock_mailbox (CONTEXT *ctx, FILE *fp, int excl)
+{
+  if (!ctx || !fp)
+    return 0;
+
+  int r = mx_lock_file (ctx->realpath, fileno (fp), excl, 1, 1);
+
+  if (r == 0)
+  {
+    ctx->locked = 1;
+  }
+  else if (excl == 0)
+  {
+    ctx->readonly = 1;
+    return 1;
+  }
+
+  return (r == 0);
+}
+
+/**
+ * unlock_mailbox - Unlock a mailbox
+ * @ctx: Mailbox to unlock
+ * @fp:  File pointer to mailbox file
+ *
+ * Unlock a mailbox previously locked by lock_mailbox().
+ */
+static void
+unlock_mailbox (CONTEXT *ctx, FILE *fp)
+{
+  if (!ctx || !fp)
+    return;
+
+  if (!ctx->locked)
+    return;
+
+  fflush (fp);
+
+  mx_unlock_file (ctx->realpath, fileno (fp), 1);
+  ctx->locked = 0;
+}
+
+/**
+ * setup_paths - Set the mailbox paths
+ * @ctx: Mailbox to modify
+ *
+ * Save the compressed filename in ctx->realpath.
+ * Create a temporary filename and put its name in ctx->path.
+ *
+ * Note: The temporary file is NOT created.
+ * Note: ctx->path will be freed by restore_path()
+ */
+static void
+setup_paths (CONTEXT *ctx)
+{
+  if (!ctx)
+    return;
+
+  char tmppath[_POSIX_PATH_MAX];
+
+  /* Setup the right paths */
+  ctx->realpath = ctx->path;
+
+  /* We will uncompress to /tmp */
+  mutt_mktemp (tmppath, sizeof (tmppath));
+  ctx->path = safe_strdup (tmppath);
+}
+
+/**
+ * restore_path - Put back the original mailbox name
+ * @ctx: Mailbox to modify
+ *
+ * When we use a compressed mailbox, we change the CONTEXT to refer to the
+ * uncompressed file.  We store the original name in ctx->realpath.
+ *      ctx->path     = "/tmp/mailbox"
+ *      ctx->realpath = "mailbox.gz"
+ *
+ * When we have finished with a compressed mailbox, we put back the original
+ * name.
+ *      ctx->path     = "mailbox.gz"
+ *      ctx->realpath = NULL
+ */
+static void
+restore_path (CONTEXT *ctx)
+{
+  if (!ctx)
+    return;
+
+  FREE(&ctx->path);
+  ctx->path = ctx->realpath;
+  ctx->realpath = NULL;
+}
+
+/**
+ * get_size - Get the size of a file
+ * @path: File to measure
+ *
+ * Returns:
+ *      number: Size in bytes
+ *      0:      On error
+ */
+static int
+get_size (const char *path)
+{
+  if (!path)
+    return 0;
+
+  struct stat sb;
+  if (stat (path, &sb) != 0)
+    return 0;
+
+  return sb.st_size;
+}
+
+/**
+ * store_size - Save the size of the compressed file
+ * @ctx: Mailbox
+ *
+ * Save the compressed file size in the compress_info struct.
+ */
+static void
+store_size (const CONTEXT *ctx)
+{
+  if (!ctx)
+    return;
+
+  COMPRESS_INFO *ci = ctx->compress_info;
+  if (!ci)
+    return;
+
+  ci->size = get_size (ctx->realpath);
+}
+
+/**
+ * find_hook - Find a hook to match a path
+ * @type: Type of hook, e.g. MUTT_CLOSEHOOK
+ * @path: Filename to test
+ *
+ * Each hook has a type and a pattern.
+ * Find a command that matches the type and path supplied. e.g.
+ *
+ * User config:
+ *      open-hook '\.gz$' "gzip -cd '%f' > '%t'"
+ *
+ * Call:
+ *      find_hook (MUTT_OPENHOOK, "myfile.gz");
+ *
+ * Returns:
+ *      string: Matching hook command
+ *      NULL:   No matches
+ */
+static const char *
+find_hook (int type, const char *path)
+{
+  if (!path)
+    return NULL;
+
+  const char *c = mutt_find_hook (type, path);
+  if (!c || !*c)
+    return NULL;
+
+  return c;
+}
+
+/**
+ * set_compress_info - Find the compress hooks for a mailbox
+ * @ctx: Mailbox to examine
+ *
+ * When a mailbox is opened, we check if there are any matching hooks.
+ *
+ * Note: Caller must free the COMPRESS_INFO when done.
+ *
+ * Returns:
+ *      COMPRESS_INFO: Hook info for the mailbox's path
+ *      NULL:          On error
+ */
+static COMPRESS_INFO *
+set_compress_info (CONTEXT *ctx)
+{
+  if (!ctx || !ctx->path)
+    return NULL;
+
+  if (ctx->compress_info)
+    return ctx->compress_info;
+
+  /* Open is compulsory */
+  const char *o = find_hook (MUTT_OPENHOOK,   ctx->path);
+  if (!o)
+    return NULL;
+
+  const char *c = find_hook (MUTT_CLOSEHOOK,  ctx->path);
+  const char *a = find_hook (MUTT_APPENDHOOK, ctx->path);
+
+  COMPRESS_INFO *ci = safe_calloc (1, sizeof (COMPRESS_INFO));
+  ctx->compress_info = ci;
+
+  ci->open   = o;
+  ci->close  = c;
+  ci->append = a;
+
+  return ci;
+}
+
+/**
+ * cb_format_str - Expand the filenames in the command string
+ * @dest:        Buffer in which to save string
+ * @destlen:     Buffer length
+ * @col:         Starting column, UNUSED
+ * @cols:        Number of screen columns, UNUSED
+ * @op:          printf-like operator, e.g. 't'
+ * @src:         printf-like format string
+ * @fmt:         Field formatting string, UNUSED
+ * @ifstring:    If condition is met, display this string, UNUSED
+ * @elsestring:  Otherwise, display this string, UNUSED
+ * @data:        Pointer to the mailbox CONTEXT
+ * @flags:       Format flags, UNUSED
+ *
+ * cb_format_str is a callback function for mutt_FormatString.  It understands
+ * two operators. '%f' : 'from' filename, '%t' : 'to' filename.
+ *
+ * Returns: src (unchanged)
+ */
+static const char *
+cb_format_str (char *dest, size_t destlen, size_t col, int cols, char op, const char *src,
+  const char *fmt, const char *ifstring, const char *elsestring,
+  unsigned long data, format_flag flags)
+{
+  if (!dest || (data == 0))
+    return src;
+
+  CONTEXT *ctx = (CONTEXT *) data;
+
+  switch (op)
+  {
+    case 'f':
+      /* Compressed file */
+      snprintf (dest, destlen, "%s", ctx->realpath);
+      break;
+    case 't':
+      /* Plaintext, temporary file */
+      snprintf (dest, destlen, "%s", ctx->path);
+      break;
+  }
+  return src;
+}
+
+/**
+ * expand_command_str - Expand placeholders in command string
+ * @ctx:    Mailbox for paths
+ * @buf:    Buffer to store the command
+ * @buflen: Size of the buffer
+ *
+ * This function takes a hook command and expands the filename placeholders
+ * within it.  The function calls mutt_FormatString() to do the replacement
+ * which calls our callback function cb_format_str(). e.g.
+ *
+ * Template command:
+ *      gzip -cd '%f' > '%t'
+ *
+ * Result:
+ *      gzip -dc '~/mail/abc.gz' > '/tmp/xyz'
+ */
+static void
+expand_command_str (const CONTEXT *ctx, const char *cmd, char *buf, int buflen)
+{
+  if (!ctx || !cmd || !buf)
+    return;
+
+  mutt_FormatString (buf, buflen, 0, buflen, cmd, cb_format_str, (unsigned long) ctx, 0);
+}
+
+/**
+ * execute_command - Run a system command
+ * @ctx:         Mailbox to work with
+ * @command:     Command string to execute
+ * @create_file: Should the tmp file be created?
+ * @progress:    Message to show the user
+ *
+ * Run the supplied command, taking care of all the Mutt requirements,
+ * such as locking files and blocking signals.
+ *
+ * Returns:
+ *      1: Success
+ *      0: Failure
+ */
+static int
+execute_command (CONTEXT *ctx, const char *command, int create_file, const char *progress)
+{
+  if (!ctx || !command || !progress)
+    return 0;
+
+  if (!ctx->quiet)
+    mutt_message (progress, ctx->realpath);
+
+  FILE *fp;
+  if (create_file)
+    fp = fopen (ctx->realpath, "a");
+  else
+    fp = fopen (ctx->realpath, "r");
+
+  if (!fp)
+  {
+    mutt_perror (ctx->realpath);
+    return 0;
+  }
+
+  mutt_block_signals();
+  /* If we're creating the file, lock it exclusively */
+  if (!lock_mailbox (ctx, fp, create_file))
+  {
+    safe_fclose (&fp);
+    mutt_unblock_signals();
+    mutt_error (_("Unable to lock mailbox!"));
+    return 0;
+  }
+
+  endwin();
+  fflush (stdout);
+
+  char sys_cmd[HUGE_STRING];
+
+  expand_command_str (ctx, command, sys_cmd, sizeof (sys_cmd));
+
+  int rc = mutt_system (sys_cmd);
+  if (rc != 0)
+  {
+    mutt_any_key_to_continue (NULL);
+    mutt_error (_("Error executing: %s\n"), sys_cmd);
+  }
+
+  unlock_mailbox (ctx, fp);
+  mutt_unblock_signals();
+  safe_fclose (&fp);
+
+  return 1;
+}
+
+/**
+ * open_read - Open a compressed mailbox for reading
+ * @ctx: Mailbox to open
+ *
+ * Decompress the mailbox and set up the paths and hooks needed.
+ *
+ * Note: The message handling will be delegated to the mbox code.
+ *
+ * Returns:
+ *      1: Success
+ *      0: Failure
+ */
+static int
+open_read (CONTEXT *ctx)
+{
+  if (!ctx)
+    return 0;
+
+  COMPRESS_INFO *ci = set_compress_info (ctx);
+  if (!ci)
+  {
+    ctx->magic = 0;
+    return 0;
+  }
+
+  /* If there's no close-hook, or the file isn't writable */
+  if (!ci->close || (access (ctx->path, W_OK) != 0))
+    ctx->readonly = 1;
+
+  setup_paths (ctx);
+  store_size (ctx);
+
+  int rc = execute_command (ctx, ci->open, 0, _("Decompressing %s"));
+  if (rc == 0)
+  {
+    goto or_fail;
+  }
+
+  ctx->magic = mx_get_magic (ctx->path);
+  if (ctx->magic == 0)
+  {
+    mutt_error (_("Can't identify the contents of the compressed file"));
+    goto or_fail;
+  }
+
+  ci->child_ops = mx_get_ops (ctx->magic);
+  if (!ci->child_ops)
+  {
+    mutt_error (_("Can't find mailbox ops for mailbox type %d"), ctx->magic);
+    goto or_fail;
+  }
+
+  return 1;
+
+or_fail:
+  /* remove the partial uncompressed file */
+  remove (ctx->path);
+  restore_path (ctx);
+  return 0;
+}
+
+
+struct mx_ops mx_comp_ops;
+
+/**
+ * open_mailbox - Open a compressed mailbox
+ * @ctx: Mailbox to open
+ *
+ * Set up a compressed mailbox to be read.
+ * First call open_read() to decompress the file.
+ * Then determine the type of the mailbox so we can delegate the handling of
+ * messages.
+ */
+static int
+open_mailbox (CONTEXT *ctx)
+{
+  if (!ctx || (ctx->magic != MUTT_COMPRESSED))
+    return 1;
+
+  if (!open_read (ctx))
+    return 1;
+
+  COMPRESS_INFO *ci = ctx->compress_info;
+  if (!ci)
+    return 1;
+
+  struct mx_ops *ops = ci->child_ops;
+  if (!ops)
+    return 1;
+
+  /* Delegate */
+  return ops->open (ctx);
+}
+
+/**
+ * open_append_mailbox - Open a compressed mailbox for appending
+ * @ctx:   Mailbox to open
+ * @flags: e.g. Does the file already exist?
+ *
+ * To append to a compressed mailbox we need an append-hook (or both open- and
+ * close-hooks).
+ *
+ * Returns:
+ *       0: Success
+ *      -1: Failure
+ */
+static int
+open_append_mailbox (CONTEXT *ctx, int flags)
+{
+  if (!ctx)
+    return -1;
+
+  /* If this succeeds, we know there's an open-hook */
+  COMPRESS_INFO *ci = set_compress_info (ctx);
+  if (!ci)
+    return -1;
+
+  /* To append we need an append-hook or a close-hook */
+  if (!ci->append && !ci->close)
+  {
+    FREE(&ctx->compress_info);
+    mutt_error (_("Cannot append without an append-hook or close-hook : %s"), ctx->path);
+    return -1;
+  }
+
+  ctx->magic = DefaultMagic;
+  /* We can only deal with mbox and mmdf mailboxes */
+  if ((ctx->magic != MUTT_MBOX) && (ctx->magic != MUTT_MMDF))
+    return -1;
+
+  setup_paths (ctx);
+
+  ctx->mx_ops = &mx_comp_ops;
+  ci->child_ops = mx_get_ops (ctx->magic);
+  if (!ci->child_ops)
+  {
+    mutt_error (_("Can't find mailbox ops for mailbox type %d"), ctx->magic);
+    return -1;
+  }
+
+  if (ci->append)
+  {
+    /* Create an empty temporary file */
+    ctx->fp = safe_fopen (ctx->path, "w");
+    if (!ctx->fp)
+    {
+      mutt_perror (ctx->path);
+      goto oa_fail;
+    }
+  }
+  else
+  {
+    /* Open the existing mailbox */
+    int rc = execute_command (ctx, ci->open, 0, _("Decompressing %s"));
+    if (rc == 0)
+    {
+      mutt_error (_("Compress command failed: %s"), ci->open);
+      goto oa_fail;
+    }
+    ctx->fp = safe_fopen (ctx->path, "a");
+    if (!ctx->fp)
+    {
+      mutt_perror (ctx->path);
+      goto oa_fail;
+    }
+  }
+
+  return 0;
+
+oa_fail:
+  /* remove the partial uncompressed file */
+  remove (ctx->path);
+  restore_path (ctx);
+  return -1;
+}
+
+/**
+ * close_mailbox - Close a compressed mailbox
+ * @ctx: Mailbox to close
+ *
+ * If the mailbox has been changed then re-compress the tmp file.
+ * Then delete the tmp file.
+ *
+ * Returns:
+ *       0: Success
+ *      -1: Failure
+ */
+static int
+close_mailbox (CONTEXT *ctx)
+{
+  if (!ctx)
+    return -1;
+
+  COMPRESS_INFO *ci = ctx->compress_info;
+  if (!ci)
+    return -1;
+
+  safe_fclose (&ctx->fp);
+
+  /* sync has already been called, so we only need to delete some files */
+  if (!ctx->append)
+  {
+    /* If the file was removed, remove the compressed folder too */
+    if ((access (ctx->path, F_OK) != 0) && !option (OPTSAVEEMPTY))
+    {
+      remove (ctx->realpath);
+    }
+    else
+    {
+      remove (ctx->path);
+    }
+
+    restore_path (ctx);
+    FREE(&ctx->compress_info);
+    return 0;
+  }
+
+  const char *append;
+  const char *msg;
+
+  /* The file exists and we can append */
+  if ((access (ctx->realpath, F_OK) == 0) && ci->append)
+  {
+    append = ci->append;
+    msg = _("Compressed-appending to %s...");
+  }
+  else
+  {
+    append = ci->close;
+    msg = _("Compressing %s...");
+  }
+
+  int rc = execute_command (ctx, append, 1, msg);
+  if (rc == 0)
+  {
+    mutt_any_key_to_continue (NULL);
+    mutt_error (_(" %s: Error compressing mailbox!  Uncompressed one kept!\n"), ctx->path);
+  }
+
+  remove (ctx->path);
+  restore_path (ctx);
+  FREE(&ctx->compress_info);
+
+  return 0;
+}
+
+/**
+ * check_mailbox - Check for changes in the compressed file
+ * @ctx: Mailbox
+ *
+ * If the compressed file changes in size but the mailbox hasn't been changed
+ * in Mutt, then we can close and reopen the mailbox.
+ *
+ * If the mailbox has been changed in Mutt, warn the user.
+ *
+ * The return codes are picked to match mx_check_mailbox().
+ *
+ * Returns:
+ *      0:              Mailbox OK
+ *      MUTT_REOPENED:  The mailbox was closed and reopened
+ *      -1:             Mailbox bad
+ */
+static int
+check_mailbox (CONTEXT *ctx, int *index_hint)
+{
+  if (!ctx)
+    return -1;
+
+  COMPRESS_INFO *ci = ctx->compress_info;
+  if (!ci)
+    return -1;
+
+  int size = get_size (ctx->realpath);
+  if (size == ci->size)
+    return 0;
+
+  if (ctx->changed)
+  {
+    FREE(&ctx->compress_info);
+    restore_path (ctx);
+    mutt_error (_("Mailbox was corrupted!"));
+    return -1;
+  }
+
+  close_mailbox (ctx);
+
+  const char *path = ctx->path;
+  ctx->path = NULL;
+
+  mx_open_mailbox (path, 0, ctx);
+  FREE(&path);
+
+  return MUTT_REOPENED;
+}
+
+
+/**
+ * open_message - Delegated to mbox handler
+ */
+static int
+open_message (CONTEXT *ctx,  MESSAGE *msg, int msgno)
+{
+  if (!ctx)
+    return -1;
+
+  COMPRESS_INFO *ci = ctx->compress_info;
+  if (!ci)
+    return -1;
+
+  struct mx_ops *ops = ci->child_ops;
+  if (!ops)
+    return -1;
+
+  /* Delegate */
+  return ops->open_msg (ctx, msg, msgno);
+}
+
+/**
+ * close_message - Delegated to mbox handler
+ */
+static int
+close_message (CONTEXT *ctx, MESSAGE *msg)
+{
+  if (!ctx)
+    return -1;
+
+  COMPRESS_INFO *ci = ctx->compress_info;
+  if (!ci)
+    return -1;
+
+  struct mx_ops *ops = ci->child_ops;
+  if (!ops)
+    return -1;
+
+  /* Delegate */
+  return ops->close_msg (ctx, msg);
+}
+
+/**
+ * commit_message - Delegated to mbox handler
+ */
+static int
+commit_message (CONTEXT *ctx, MESSAGE *msg)
+{
+  if (!ctx)
+    return -1;
+
+  COMPRESS_INFO *ci = ctx->compress_info;
+  if (!ci)
+    return -1;
+
+  struct mx_ops *ops = ci->child_ops;
+  if (!ops)
+    return -1;
+
+  /* Delegate */
+  return ops->commit_msg (ctx, msg);
+}
+
+/**
+ * open_new_message - Delegated to mbox handler
+ */
+static int
+open_new_message (MESSAGE *msg, CONTEXT *ctx, HEADER *hdr)
+{
+  if (!ctx)
+    return -1;
+
+  COMPRESS_INFO *ci = ctx->compress_info;
+  if (!ci)
+    return -1;
+
+  struct mx_ops *ops = ci->child_ops;
+  if (!ops)
+    return -1;
+
+  /* Delegate */
+  return ops->open_new_msg (msg, ctx, hdr);
+}
+
+
+/**
+ * comp_can_append - Can we append to this path?
+ * @path: pathname of file to be tested
+ *
+ * To append to a file we can either use an 'append-hook' or a combination of
+ * 'open-hook' and 'close-hook'.
+ *
+ * A match means it's our responsibility to append to the file.
+ *
+ * Returns:
+ *      1: Yes, we can append to the file
+ *      0: No, appending isn't possible
+ */
+int
+comp_can_append (CONTEXT *ctx)
+{
+  if (!ctx)
+    return 0;
+
+  /* If this succeeds, we know there's an open-hook */
+  COMPRESS_INFO *ci = set_compress_info (ctx);
+  if (!ci)
+    return 0;
+
+  /* We have an open-hook, so to append we need an append-hook,
+   * or a close-hook. */
+  if (ci->append || ci->close)
+    return 1;
+
+  mutt_error (_("Cannot append without an append-hook or close-hook : %s"), ctx->path);
+  return 0;
+}
+
+/**
+ * comp_can_read - Can we read from this file?
+ * @path: Pathname of file to be tested
+ *
+ * Search for an 'open-hook' with a regex that matches the path.
+ *
+ * A match means it's our responsibility to open the file.
+ *
+ * Returns:
+ *      1: Yes, we can read the file
+ *      0: No, we cannot read the file
+ */
+int
+comp_can_read (const char *path)
+{
+  if (!path)
+    return 0;
+
+  if (find_hook (MUTT_OPENHOOK, path))
+    return 1;
+  else
+    return 0;
+}
+
+/**
+ * comp_sync - Save changes to the compressed mailbox file
+ * @ctx: Mailbox to sync
+ *
+ * Changes in Mutt only affect the tmp file.  Calling comp_sync() will commit
+ * them to the compressed file.
+ *
+ * Returns:
+ *       0: Success
+ *      -1: Failure
+ */
+int
+comp_sync (CONTEXT *ctx)
+{
+  if (!ctx)
+    return -1;
+
+  COMPRESS_INFO *ci = ctx->compress_info;
+  if (!ci)
+    return -1;
+
+  if (!ci->close)
+  {
+    mutt_error (_("Can't sync a compressed file without a close-hook"));
+    return -1;
+  }
+
+  int rc = execute_command (ctx, ci->close, 1, _("Compressing %s"));
+  if (rc == 0)
+    return -1;
+
+  store_size (ctx);
+
+  return 0;
+}
+
+/**
+ * comp_valid_command - Is this command string allowed?
+ * @cmd:  Command string
+ *
+ * A valid command string must have both "%f" (from file) and "%t" (to file).
+ * We don't check if we can actually run the command.
+ *
+ * Returns:
+ *      1: Valid command
+ *      0: "%f" and/or "%t" is missing
+ */
+int
+comp_valid_command (const char *cmd)
+{
+  if (!cmd)
+    return 0;
+
+  return (strstr (cmd, "%f") && strstr (cmd, "%t"));
+}
+
+
+/**
+ * mx_comp_ops - Mailbox callback functions
+ *
+ * Compress only uses open, close and check.
+ * The message functions are delegated to mbox.
+ */
+struct mx_ops mx_comp_ops =
+{
+  .open         = open_mailbox,
+  .open_append  = open_append_mailbox,
+  .close        = close_mailbox,
+  .check        = check_mailbox,
+  .open_msg     = open_message,
+  .close_msg    = close_message,
+  .commit_msg   = commit_message,
+  .open_new_msg = open_new_message
+};
+
diff --git a/compress.h b/compress.h
new file mode 100644 (file)
index 0000000..26beae6
--- /dev/null
@@ -0,0 +1,29 @@
+/* Copyright (C) 1997 Alain Penders <Alain@Finale-Dev.com>
+ * Copyright (C) 2016 Richard Russon <rich@flatcap.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef _COMPRESS_H_
+#define _COMPRESS_H_
+
+int comp_can_append    (CONTEXT *ctx);
+int comp_can_read      (const char *path);
+int comp_sync          (CONTEXT *ctx);
+int comp_valid_command (const char *cmd);
+
+extern struct mx_ops mx_comp_ops;
+
+#endif /* _COMPRESS_H_ */
index 6d1c418a3a386e857c01a13e46fd5450c99a2d98..57e408e4bed2c0e441343c900ea16f23db064738 100644 (file)
@@ -183,6 +183,15 @@ AC_ARG_ENABLE(sidebar, AC_HELP_STRING([--enable-sidebar], [Enable Sidebar suppor
         fi
 ])
 
+AC_ARG_ENABLE(compressed, AC_HELP_STRING([--enable-compressed], [Enable compressed folders support]),
+              enable_compressed=$enableval, enable_compressed=no
+)
+AS_IF([test x$enable_compressed = "xyes"], [
+          AC_DEFINE(USE_COMPRESSED, 1, [Define to enable compressed folders support.])
+          MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS compress.o"
+])
+AM_CONDITIONAL(BUILD_COMPRESS, test x$enable_compressed = xyes)
+
 AC_ARG_WITH(mixmaster, AS_HELP_STRING([--with-mixmaster@<:@=PATH@:>@],[Include Mixmaster support]),
   [if test "$withval" != no
    then
index 3ca99a023e7e82fb6f94692abe75566222cf3ece..89aa1586de29635ab4cbc885a37515b710454ac5 100644 (file)
@@ -4,7 +4,7 @@ subdir = contrib
 
 SAMPLES = Mush.rc Pine.rc gpg.rc pgp2.rc pgp5.rc pgp6.rc Tin.rc \
        sample.mailcap sample.muttrc sample.muttrc-sidebar sample.muttrc-tlr \
-       sample.vimrc-sidebar colors.default colors.linux smime.rc \
+       sample.muttrc-compress sample.vimrc-sidebar colors.default colors.linux smime.rc \
        ca-bundle.crt smime_keys_test.pl mutt_xtitle
 
 EXTRA_DIST = language.txt language50.txt  \
diff --git a/contrib/sample.muttrc-compress b/contrib/sample.muttrc-compress
new file mode 100644 (file)
index 0000000..d793a9e
--- /dev/null
@@ -0,0 +1,38 @@
+# Example Mutt config file for the compress feature.
+
+# This feature adds three hooks to Mutt which allow it to
+# work with compressed, or encrypted, mailboxes.
+
+# The hooks are of the form:
+#       open-hook   regexp "shell-command"
+#       close-hook  regexp "shell-command"
+#       append-hook regexp "shell-command"
+
+# The 'append-hook' is optional.
+
+# Handler for gzip compressed mailboxes
+open-hook   '\.gz$'  "gzip -cd  '%f' >  '%t'"
+close-hook  '\.gz$'  "gzip -c   '%t' >  '%f'"
+append-hook '\.gz$'  "gzip -c   '%t' >> '%f'"
+
+# Handler for bzip2 compressed mailboxes
+open-hook   '\.bz2$' "bzip2 -cd '%f' >  '%t'"
+close-hook  '\.bz2$' "bzip2 -c  '%t' >  '%f'"
+append-hook '\.bz2$' "bzip2 -c  '%t' >> '%f'"
+
+# Handler for xz compressed mailboxes
+open-hook   '\.xz$'  "xz    -cd '%f' >  '%t'"
+close-hook  '\.xz$'  "xz    -c  '%t' >  '%f'"
+append-hook '\.xz$'  "xz    -c  '%t' >> '%f'"
+
+# Handler for pgp encrypted mailboxes
+# PGP does not support appending to an encrypted file
+open-hook   '\.pgp$' "pgp -f < '%f' > '%t'"
+close-hook  '\.pgp$' "pgp -fe YourPgpUserIdOrKeyId < '%t' > '%f'"
+
+# Handler for gpg encrypted mailboxes
+# gpg does not support appending to an encrypted file
+open-hook   '\.gpg$' "gpg --decrypt < '%f' > '%t'"
+close-hook  '\.gpg$' "gpg --encrypt --recipient YourGpgUserIdOrKeyId < '%t' > '%f'"
+
+# vim: syntax=muttrc
index 3793eccc2fd10c9faac02eec97a74c2eb1bb0a76..03e2f6a2f7172c97f0eeef8bb730581601a85f9e 100644 (file)
@@ -1241,6 +1241,11 @@ int mutt_index_menu (void)
         {
          int check;
 
+#ifdef USE_COMPRESSED
+         if (Context->compress_info && Context->realpath)
+           mutt_str_replace (&LastFolder, Context->realpath);
+         else
+#endif
          mutt_str_replace (&LastFolder, Context->path);
          oldcount = Context ? Context->msgcount : 0;
 
index 1f7012e77c01a32422be853d2f62b61f3ea97f5e..78916d95446ae3107aa0b4650d02754dc65754b6 100644 (file)
@@ -29,6 +29,11 @@ macro generic,pager <F1> "<shell-escape> less @docdir@/manual.txt<Enter>" "show
 macro index,pager y "<change-folder>?<toggle-mailboxes>" "show incoming mailboxes list"
 bind browser y exit
 
+# Handler for gzip compressed mailboxes
+# open-hook   '\.gz$'  "gzip -cd  '%f' >  '%t'"
+# close-hook  '\.gz$'  "gzip -c   '%t' >  '%f'"
+# append-hook '\.gz$'  "gzip -c   '%t' >> '%f'"
+
 # If Mutt is unable to determine your site's domain name correctly, you can
 # set the default here.
 #
index db88f87ef8fa4431803b4ca79065ec9e9d0de2d1..af8ba01486a25cf9e16b4287153f8dbbff2b961e 100644 (file)
@@ -8427,6 +8427,282 @@ please have a look at the mixmaster documentation.
   </sect2>
 </sect1>
 
+<sect1 id="compress">
+  <title>Compressed Folders Feature</title>
+  <subtitle>Read from/write to compressed mailboxes</subtitle>
+
+  <sect2 id="compress-intro">
+    <title>Introduction</title>
+
+    <para>
+      The Compressed Folder patch allows Mutt to read mailbox files that are
+      compressed.  But it isn't limited to compressed files.  It works well
+      with encrypted files, too.  In fact, if you can create a program/script
+      to convert to and from your format, then Mutt can read it.
+    </para>
+
+    <para>
+      The patch adds three hooks to Mutt: <literal>open-hook</literal>,
+      <literal>close-hook</literal> and <literal>append-hook</literal>.  They
+      define commands to: uncompress a file; compress a file; append
+      messages to an already compressed file.
+    </para>
+
+    <para>
+      There are some examples of both compressed and encrypted files,
+      later.  For now, the documentation will just concentrate on
+      compressed files.
+    </para>
+
+  </sect2>
+
+  <sect2 id="compress-commands">
+    <title>Commands</title>
+    <cmdsynopsis>
+      <command>open-hook</command>
+      <arg choice="plain">
+        <replaceable class="parameter">pattern</replaceable>
+      </arg>
+      <arg choice="plain">
+        <replaceable class="parameter">shell-command</replaceable>
+      </arg>
+      <command>close-hook</command>
+      <arg choice="plain">
+        <replaceable class="parameter">pattern</replaceable>
+      </arg>
+      <arg choice="plain">
+        <replaceable class="parameter">shell-command</replaceable>
+      </arg>
+      <command>append-hook</command>
+      <arg choice="plain">
+        <replaceable class="parameter">pattern</replaceable>
+      </arg>
+      <arg choice="plain">
+        <replaceable class="parameter">shell-command</replaceable>
+      </arg>
+    </cmdsynopsis>
+
+    <para>
+      The shell-command must contain two placeholders for filenames:
+      <literal>%f</literal> and <literal>%t</literal>.  These represent
+      <quote>from</quote> and <quote>to</quote> filenames.  It's a good idea to
+      put quotes around these placeholders.
+    </para>
+
+    <para>
+      If you need the exact string <quote>%f</quote> or <quote>%t</quote> in your
+      command, simply double up the <quote>%</quote> character, e.g.
+      <quote>%%f</quote> or <quote>%%t</quote>.
+    </para>
+
+    <table id="table-compress-optional">
+      <title>Not all Hooks are Required</title>
+      <tgroup cols="5">
+        <thead>
+          <row>
+            <entry>Open</entry>
+            <entry>Close</entry>
+            <entry>Append</entry>
+            <entry>Effect</entry>
+            <entry>Useful if</entry>
+          </row>
+        </thead>
+        <tbody>
+          <row>
+            <entry>Open</entry>
+            <entry>-</entry>
+            <entry>-</entry>
+            <entry>Folder is readonly</entry>
+            <entry>The folder is just a backup</entry>
+          </row>
+          <row>
+            <entry>Open</entry>
+            <entry>Close</entry>
+            <entry>-</entry>
+            <entry>Folder is read/write, but the entire folder must be
+              written if anything is changed</entry>
+            <entry>Your compression format doesn't support appending</entry>
+          </row>
+          <row>
+            <entry>Open</entry>
+            <entry>Close</entry>
+            <entry>Append</entry>
+            <entry>Folder is read/write and emails can be efficiently added
+              to the end</entry>
+            <entry>Your compression format supports appending</entry>
+          </row>
+          <row>
+            <entry>Open</entry>
+            <entry>-</entry>
+            <entry>Append</entry>
+            <entry>Folder is readonly, but can be appended to</entry>
+            <entry>You want to store emails, but never change them</entry>
+          </row>
+        </tbody>
+      </tgroup>
+    </table>
+
+    <note>
+      <para>The command:</para>
+      <itemizedlist>
+        <listitem><para>should return a non-zero exit status on failure</para></listitem>
+        <listitem><para>should not delete any files</para></listitem>
+      </itemizedlist>
+    </note>
+
+    <sect3 id="open-hook">
+      <title>Read from compressed mailbox</title>
+
+      <screen>open-hook regexp shell-command</screen>
+
+      <para>
+        If Mutt is unable to open a file, it then looks for
+        <literal>open-hook</literal> that matches the filename.
+      </para>
+
+      <para>
+        If your compression program doesn't have a well-defined extension,
+        then you can use <literal>.</literal> as the regexp.
+      </para>
+
+      <sect4 id="compress-open-hook-example">
+        <title>Example of open-hook</title>
+
+        <screen>open-hook '\.gz$' &quot;gzip -cd '%f' &gt; '%t'&quot;</screen>
+
+        <itemizedlist>
+          <listitem><para>Mutt finds a file, <quote>example.gz</quote>,
+              that it can't read</para></listitem>
+          <listitem><para>Mutt has an <literal>open-hook</literal>
+              whose regexp matches the filename:
+              <literal>\.gz$</literal></para></listitem>
+          <listitem><para>Mutt uses the command <literal>gzip -cd</literal>
+              to create a temporary file that it <emphasis>can</emphasis>
+              read</para></listitem>
+        </itemizedlist>
+      </sect4>
+    </sect3>
+
+    <sect3 id="close-hook">
+      <title>Write to a compressed mailbox</title>
+
+      <screen>close-hook regexp shell-command</screen>
+
+      <para>
+        When Mutt has finished with a compressed mail folder, it will look
+        for a matching <literal>close-hook</literal> to recompress the file.
+        This hook is <link linkend="table-compress-optional">optional</link>.
+      </para>
+
+      <note>
+        <para>
+          If the folder has not been modified, the
+          <literal>close-hook</literal> will not be called.
+        </para>
+      </note>
+
+      <sect4 id="compress-close-hook-example">
+        <title>Example of close-hook</title>
+
+        <screen>close-hook '\.gz$' &quot;gzip -c '%t' &gt; '%f'&quot;</screen>
+
+        <itemizedlist>
+          <listitem><para>Mutt has finished with a folder, <quote>example.gz</quote>,
+              that it opened with <literal>open-hook</literal></para></listitem>
+          <listitem><para>The folder has been modified</para></listitem>
+          <listitem><para>Mutt has a <literal>close-hook</literal> whose regexp
+              matches the filename: <literal>\.gz$</literal></para></listitem>
+          <listitem><para>Mutt uses the command <literal>gzip -c</literal>
+              to create a new compressed file</para></listitem>
+        </itemizedlist>
+      </sect4>
+    </sect3>
+
+    <sect3 id="append-hook">
+      <title>Append to a compressed mailbox</title>
+
+      <screen>append-hook regexp shell-command</screen>
+
+      <para>
+        When Mutt wants to append an email to a compressed mail folder, it
+        will look for a matching <literal>append-hook</literal>.
+        This hook is <link linkend="table-compress-optional">optional</link>.
+      </para>
+
+      <para>
+        Using the <literal>append-hook</literal> will save time, but
+        Mutt won't be able to determine the type of the mail folder
+        inside the compressed file.
+      </para>
+
+      <para>
+        Mutt will <emphasis>assume</emphasis> the type to be that of
+        the <literal>$mbox_type</literal> variable.  Mutt also uses
+        this type for temporary files.
+      </para>
+
+      <para>
+        Mutt will only use the <literal>append-hook</literal> for existing files.
+        The <literal>close-hook</literal> will be used for empty, or missing files.
+      </para>
+
+      <note>
+        <para>
+          If your command writes to stdout, it is vital that you use
+          <literal>&gt;&gt;</literal> in the <quote>append-hook</quote>.
+          If not, data will be lost.
+        </para>
+      </note>
+
+      <sect4 id="compress-append-hook-example">
+        <title>Example of append-hook</title>
+
+        <screen>append-hook '\.gz$' &quot;gzip -c '%t' &gt;&gt; '%f'&quot;</screen>
+
+        <itemizedlist>
+          <listitem><para>Mutt wants to append an email to a folder, <quote>example.gz</quote>,
+              that it opened with <literal>open-hook</literal></para></listitem>
+          <listitem><para>Mutt has an <literal>append-hook</literal> whose regexp matches
+              the filename: <literal>\.gz$</literal></para></listitem>
+          <listitem><para>Mutt knows the mailbox type from the <literal>$mbox</literal>
+              variable</para></listitem>
+          <listitem><para>Mutt uses the command <literal>gzip -c</literal>
+              to append to an existing compressed file</para></listitem>
+        </itemizedlist>
+      </sect4>
+
+    </sect3>
+
+    <sect3 id="compress-empty">
+      <title>Empty Files</title>
+
+      <para>
+        Mutt assumes that an empty file is not compressed.  In this
+        situation, unset <link linkend="save-empty">$save_empty</link>, so
+        that the compressed file will be removed if you delete all of the
+        messages.
+      </para>
+    </sect3>
+
+    <sect3 id="compress-security">
+      <title>Security</title>
+
+      <para>
+        Encrypted files are decrypted into temporary files which are
+        stored in the <link linkend="tmpdir">$tmpdir</link> directory.
+        This could be a security risk.
+      </para>
+    </sect3>
+  </sect2>
+
+  <sect2 id="compress-known-bugs">
+    <title>Known Bugs</title>
+
+    <itemizedlist>
+      <listitem><para>The Compressed Folder hooks cannot deal with filenames that contains quotes/apostrophes.</para></listitem>
+    </itemizedlist>
+  </sect2>
+</sect1>
 </chapter>
 
 <chapter id="security">
index b237c5a22469f6116dc8492094eb019361592371..6ab69a704f1afcf55cdda119cf80427a89458bc7 100644 (file)
@@ -358,7 +358,24 @@ You may use multiple
 \fBcrypt-hook\fPs with the same \fIregexp\fP; multiple matching
 \fBcrypt-hook\fPs result in the use of multiple \fIkey-id\fPs for
 a recipient.
-
+.PP
+.nf
+\fBopen-hook\fP \fIregexp\fP "\fIcommand\fP"
+\fBclose-hook\fP \fIregexp\fP "\fIcommand\fP"
+\fBappend-hook\fP \fIregexp\fP "\fIcommand\fP"
+.fi
+.IP
+These commands provide a way to handle compressed folders. The given
+\fBregexp\fP specifies which folders are taken as compressed (e.g.
+"\fI\\\\.gz$\fP"). The commands tell Mutt how to uncompress a folder
+(\fBopen-hook\fP), compress a folder (\fBclose-hook\fP) or append a
+compressed mail to a compressed folder (\fBappend-hook\fP). The
+\fIcommand\fP string is the
+.BR printf (3)
+like format string, and it should accept two parameters: \fB%f\fP,
+which is replaced with the (compressed) folder name, and \fB%t\fP
+which is replaced with the name of the temporary folder to which to
+write.
 .TP
 \fBpush\fP \fIstring\fP
 This command adds the named \fIstring\fP to the keyboard buffer.
diff --git a/hook.c b/hook.c
index 1b906c33ece2820c8c352e521733652637f523fd..d0a111b27f3431229adce65fce8bb978a0eea654 100644 (file)
--- a/hook.c
+++ b/hook.c
 #include "mailbox.h"
 #include "mutt_crypt.h"
 
+#ifdef USE_COMPRESSED
+#include "compress.h"
+#endif
+
 #include <limits.h>
 #include <string.h>
 #include <stdlib.h>
@@ -109,6 +113,14 @@ int mutt_parse_hook (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
     memset (&pattern, 0, sizeof (pattern));
     pattern.data = safe_strdup (path);
   }
+#ifdef USE_COMPRESSED
+  else if (data & (MUTT_APPENDHOOK | MUTT_OPENHOOK | MUTT_CLOSEHOOK)) {
+    if (comp_valid_command (command.data) == 0) {
+      strfcpy (err->data, _("badly formatted command string"), err->dsize);
+      return -1;
+    }
+  }
+#endif
   else if (DefaultHook && !(data & (MUTT_CHARSETHOOK | MUTT_ICONVHOOK | MUTT_ACCOUNTHOOK))
            && (!WithCrypto || !(data & MUTT_CRYPTHOOK))
       )
diff --git a/init.h b/init.h
index 48cf915aeaedc58378c84120a9f5829a672f621f..a0a399c11530069bf46cdbddb59ff3d822af1366 100644 (file)
--- a/init.h
+++ b/init.h
@@ -3907,6 +3907,11 @@ const struct command_t Commands[] = {
   { "fcc-hook",                mutt_parse_hook,        MUTT_FCCHOOK },
   { "fcc-save-hook",   mutt_parse_hook,        MUTT_FCCHOOK | MUTT_SAVEHOOK },
   { "folder-hook",     mutt_parse_hook,        MUTT_FOLDERHOOK },
+#ifdef USE_COMPRESSED
+  { "open-hook",       mutt_parse_hook,        MUTT_OPENHOOK },
+  { "close-hook",      mutt_parse_hook,        MUTT_CLOSEHOOK },
+  { "append-hook",     mutt_parse_hook,        MUTT_APPENDHOOK },
+#endif
   { "group",           parse_group,            MUTT_GROUP },
   { "ungroup",         parse_group,            MUTT_UNGROUP },
   { "hdr_order",       parse_list,             UL &HeaderOrderList },
diff --git a/main.c b/main.c
index 68a3601834af737e4f08378a28c816a6e27efab4..25b1d04b5e4b225c4fdd904884763aed6fc597fc 100644 (file)
--- a/main.c
+++ b/main.c
@@ -494,6 +494,12 @@ static void show_version (void)
        "-USE_SIDEBAR  "
 #endif
 
+#ifdef USE_COMPRESSED
+       "+USE_COMPRESSED  "
+#else
+       "-USE_COMPRESSED  "
+#endif
+
        );
 
 #ifdef ISPELL
diff --git a/mutt.h b/mutt.h
index 95c5f0e42db92fcc8aa48a04780c490cc93e73c7..c63a1eeb13f7ab79c750150cce4d3ea54df0f3c0 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -146,6 +146,11 @@ typedef enum
 #define MUTT_ACCOUNTHOOK (1<<9)
 #define MUTT_REPLYHOOK   (1<<10)
 #define MUTT_SEND2HOOK   (1<<11)
+#ifdef USE_COMPRESSED
+#define MUTT_OPENHOOK    (1<<12)
+#define MUTT_APPENDHOOK  (1<<13)
+#define MUTT_CLOSEHOOK   (1<<14)
+#endif
 
 /* tree characters for linearize_tree and print_enriched_string */
 #define MUTT_TREE_LLCORNER      1
@@ -951,6 +956,10 @@ typedef struct _context
   unsigned int closing : 1;    /* mailbox is being closed */
   unsigned int peekonly : 1;   /* just taking a glance, revert atime */
 
+#ifdef USE_COMPRESSED
+  void *compress_info;         /* compressed mbox module private data */
+#endif /* USE_COMPRESSED */
+
   /* driver hooks */
   void *data;                  /* driver specific data */
   struct mx_ops *mx_ops;
diff --git a/mx.c b/mx.c
index 680d38f40abc2ac2181a3faac6cd9999a9230197..fdb2a77c6fabf73f725a2400683e279b60d214e9 100644 (file)
--- a/mx.c
+++ b/mx.c
 #include "sidebar.h"
 #endif
 
+#ifdef USE_COMPRESSED
+#include "compress.h"
+#endif
+
 #ifdef USE_IMAP
 #include "imap.h"
 #endif
@@ -60,7 +64,7 @@
 #include <ctype.h>
 #include <utime.h>
 
-static struct mx_ops* mx_get_ops (int magic)
+struct mx_ops* mx_get_ops (int magic)
 {
   switch (magic)
   {
@@ -79,6 +83,10 @@ static struct mx_ops* mx_get_ops (int magic)
 #ifdef USE_POP
     case MUTT_POP:
       return &mx_pop_ops;
+#endif
+#ifdef USE_COMPRESSED
+    case MUTT_COMPRESSED:
+      return &mx_comp_ops;
 #endif
     default:
       return NULL;
@@ -441,6 +449,12 @@ int mx_get_magic (const char *path)
     return (-1);
   }
 
+#ifdef USE_COMPRESSED
+  /* If there are no other matches, see if there are any
+   * compress hooks that match */
+  if ((magic == 0) && comp_can_read (path))
+    return MUTT_COMPRESSED;
+#endif
   return (magic);
 }
 
@@ -507,6 +521,11 @@ static int mx_open_mailbox_append (CONTEXT *ctx, int flags)
       return -1;
   }
 
+#ifdef USE_COMPRESSED
+  if (comp_can_append (ctx))
+    ctx->mx_ops = &mx_comp_ops;
+  else
+#endif
   ctx->mx_ops = mx_get_ops (ctx->magic);
   if (!ctx->mx_ops || !ctx->mx_ops->open_append)
     return -1;
@@ -700,6 +719,14 @@ static int sync_mailbox (CONTEXT *ctx, int *index_hint)
   
   if (tmp && tmp->new == 0)
     mutt_update_mailbox (tmp);
+
+#ifdef USE_COMPRESSED
+  /* If everything went well, the mbox handler saved the changes to our
+   * temporary file.  Next, comp_sync() will compress the temporary file. */
+  if ((rc == 0) && ctx->compress_info)
+    return comp_sync (ctx);
+#endif
+
   return rc;
 }
 
diff --git a/mx.h b/mx.h
index ba9a78f9ae3d3486cfa6c8da051d59f1c3d48e3a..6b3c535ea186e26bd02df2e7303c95669397a708 100644 (file)
--- a/mx.h
+++ b/mx.h
@@ -37,6 +37,9 @@ enum
   MUTT_MAILDIR,
   MUTT_IMAP,
   MUTT_POP
+#ifdef USE_COMPRESSED
+  , MUTT_COMPRESSED
+#endif
 };
 
 WHERE short DefaultMagic INITVAL (MUTT_MBOX);
@@ -70,6 +73,7 @@ void mx_update_tables (CONTEXT *, int);
 int mx_lock_file (const char *, int, int, int, int);
 int mx_unlock_file (const char *path, int fd, int dot);
 
+struct mx_ops* mx_get_ops (int magic);
 extern struct mx_ops mx_maildir_ops;
 extern struct mx_ops mx_mbox_ops;
 extern struct mx_ops mx_mh_ops;
index 2d01add62fe1a3ee7a7639603f67e2d83fcf2765..3654ad149bc879d133bc761a232072cf261ba794 100644 (file)
@@ -8,6 +8,7 @@ charset.c
 color.c
 commands.c
 compose.c
+compress.c
 crypt-gpgme.c
 crypt.c
 cryptglue.c
index 19ddc349fab463d89546a5271f1c87951b590ac3..bb213183cbb8fee8dd52ca0165c76a2cbc928674 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -5418,6 +5418,31 @@ msgstr "Extrahiere unterst
 msgid "show S/MIME options"
 msgstr "Zeige S/MIME Optionen"
 
+#: compress.c:228 compress.c:253
+#, c-format
+msgid "Decompressing %s"
+msgstr "Entpacke %s"
+
+#: compress.c:264
+#, c-format
+msgid "Error executing: %s\n"
+msgstr "Fehler beim Ausführen von %s\n"
+
+#: compress.c:350 compress.c:377 compress.c:423 compress.c:454
+#, c-format
+msgid "Compressing %s..."
+msgstr "Komprimiere %s..."
+
+#: compress.c:425 compress.c:456
+#, c-format
+msgid "Compressed-appending to %s..."
+msgstr "Hänge komprimiert an %s... an"
+
+#: compress.c:461
+#, c-format
+msgid " %s: Error compressing mailbox!  Uncompressed one kept!\n"
+msgstr " %s: Fehler beim packen der Mailbox! Entpackte Mailbox gespeichert!\n"
+
 #~ msgid "delete message(s)"
 #~ msgstr "Nachricht(en) löschen"
 
index 6ac5b56bdab1dee6e5461f6b3c27578e16b011e9..86113a8e1bd6d62a812557ac5cf2d7a31c1411bd 100644 (file)
--- a/status.c
+++ b/status.c
@@ -96,6 +96,12 @@ status_format_str (char *buf, size_t buflen, size_t col, int cols, char op, cons
 
     case 'f':
       snprintf (fmt, sizeof(fmt), "%%%ss", prefix);
+#ifdef USE_COMPRESSED
+      if (Context && Context->compress_info && Context->realpath) {
+        strfcpy (tmp, Context->realpath, sizeof (tmp));
+        mutt_pretty_mailbox (tmp, sizeof (tmp));
+      } else
+#endif
       if (Context && Context->path)
       {
        strfcpy (tmp, Context->path, sizeof (tmp));