2 * Copyright notice from original mutt:
3 * Copyright (C) 1997 Alain Penders <Alain@Finale-Dev.com>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 #include <lib-lib/lib-lib.h>
12 #include <lib-sys/mutt_signal.h>
13 #include <lib-sys/unix.h>
15 #include <lib-ui/curses.h>
23 struct compress_info {
24 const char *close; /* close-hook command */
25 const char *open; /* open-hook command */
26 const char *append; /* append-hook command */
27 off_t size; /* size of real folder */
30 char echo_cmd[HUGE_STRING];
33 * ctx - context to lock
34 * excl - exclusive lock?
35 * retry - should retry if unable to lock?
37 static int mbox_lock_compressed (CONTEXT * ctx, FILE * fp, int excl, int retry)
41 if ((r = mx_lock_file (ctx->realpath, fileno (fp), excl, 1, retry)) == 0)
43 else if (retry && !excl) {
51 static void mbox_unlock_compressed (CONTEXT * ctx, FILE * fp)
56 mx_unlock_file (ctx->realpath, fileno (fp), 1);
61 static int is_new (const char *path)
63 return (access (path, W_OK) != 0 && errno == ENOENT) ? 1 : 0;
66 static const char *find_compress_hook (int type, const char *path)
68 const char *c = mutt_find_hook (type, path);
70 return (!c || !*c) ? NULL : c;
73 int mutt_can_read_compressed (const char *path)
75 return find_compress_hook (M_OPENHOOK, path) ? 1 : 0;
78 /* if the file is new, we really do not append, but create, and so use
79 * close-hook, and not append-hook
81 static const char *get_append_command (const char *path, const CONTEXT * ctx)
83 return is_new(path) ? ctx->cinfo->close : ctx->cinfo->append;
86 int mutt_can_append_compressed (const char *path)
91 return (find_compress_hook (M_CLOSEHOOK, path) ? 1 : 0);
93 magic = mx_get_magic (path);
95 if (magic != 0 && magic != M_COMPRESSED)
98 return (find_compress_hook (M_APPENDHOOK, path)
99 || (find_compress_hook (M_OPENHOOK, path)
100 && find_compress_hook (M_CLOSEHOOK, path))) ? 1 : 0;
103 /* open a compressed mailbox */
104 static compress_info *set_compress_info (CONTEXT * ctx)
106 compress_info *ci = p_new(compress_info, 1);
108 /* Now lets uncompress this thing */
109 ci->append = find_compress_hook (M_APPENDHOOK, ctx->path);
110 ci->open = find_compress_hook (M_OPENHOOK, ctx->path);
111 ci->close = find_compress_hook (M_CLOSEHOOK, ctx->path);
113 return (ctx->cinfo = ci);
116 static void set_path (CONTEXT * ctx)
118 char tmppath[_POSIX_PATH_MAX];
120 /* Setup the right paths */
121 ctx->realpath = ctx->path;
123 /* Uncompress to /tmp */
124 mutt_mktemp (tmppath);
125 ctx->path = p_dupstr(tmppath, m_strlen(tmppath));
128 static int get_size (const char *path)
132 if (stat (path, &sb) != 0)
137 static const char *compresshook_format_str (char *dest, ssize_t destlen,
138 char op, const char *src,
140 const char *ifstring __attribute__ ((unused)),
141 const char *elsestring __attribute__ ((unused)),
143 format_flag flags __attribute__ ((unused)))
145 char tmp[SHORT_STRING];
147 CONTEXT *ctx = (CONTEXT *) data;
151 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
152 snprintf (dest, destlen, tmp, ctx->realpath);
155 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
156 snprintf (dest, destlen, tmp, ctx->path);
162 /* check that the command has both %f and %t
163 * 0 means OK, -1 means error
165 int mutt_test_compress_command (const char *cmd)
167 return (strstr (cmd, "%f") && strstr (cmd, "%t")) ? 0 : -1;
170 static char *get_compression_cmd (const char *cmd, const CONTEXT * ctx)
172 char expanded[_POSIX_PATH_MAX];
174 mutt_FormatString (expanded, sizeof (expanded), cmd,
175 compresshook_format_str, (unsigned long) ctx, 0);
176 return m_strdup(expanded);
179 int mutt_check_mailbox_compressed (CONTEXT * ctx)
181 if (ctx->cinfo->size != get_size (ctx->realpath)) {
182 p_delete(&ctx->cinfo);
183 p_delete(&ctx->realpath);
184 mutt_error _("Mailbox was corrupted!");
191 int mutt_open_read_compressed (CONTEXT * ctx)
197 compress_info *ci = set_compress_info (ctx);
201 p_delete(&ctx->cinfo);
204 if (!ci->close || access (ctx->path, W_OK) != 0)
208 ctx->cinfo->size = get_size(ctx->realpath);
211 mutt_message (_("Decompressing %s..."), ctx->realpath);
213 cmd = get_compression_cmd (ci->open, ctx);
217 if ((fp = fopen (ctx->realpath, "r")) == NULL) {
218 mutt_perror (ctx->realpath);
222 mutt_block_signals ();
223 if (mbox_lock_compressed (ctx, fp, 0, 1) == -1) {
225 mutt_unblock_signals ();
226 mutt_error _("Unable to lock mailbox!");
234 sprintf (echo_cmd, _("echo Decompressing %s..."), ctx->realpath);
235 mutt_system (echo_cmd);
236 rc = mutt_system (cmd);
237 mbox_unlock_compressed (ctx, fp);
238 mutt_unblock_signals ();
242 mutt_any_key_to_continue (NULL);
244 p_delete(&ctx->cinfo);
245 mutt_error(_("Error executing: %s : unable to open the mailbox!\n"), cmd);
251 if (mutt_check_mailbox_compressed (ctx))
254 ctx->magic = mx_get_magic (ctx->path);
259 static void restore_path (CONTEXT * ctx)
261 p_delete(&ctx->path);
262 ctx->path = ctx->realpath;
265 /* remove the temporary mailbox */
266 static void remove_file (CONTEXT * ctx)
268 if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
272 int mutt_open_append_compressed (CONTEXT * ctx)
275 compress_info *ci = set_compress_info (ctx);
277 if (!get_append_command (ctx->path, ctx)) {
278 if (ci->open && ci->close)
279 return (mutt_open_read_compressed (ctx));
282 p_delete(&ctx->cinfo);
288 ctx->magic = DefaultMagic;
290 if (!is_new (ctx->realpath))
291 if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
292 if ((fh = safe_fopen (ctx->path, "w")))
294 /* No error checking - the parent function will catch it */
299 /* close a compressed mailbox */
300 void mutt_fast_close_compressed (CONTEXT * ctx)
305 /* if the folder was removed, remove the gzipped folder too */
306 if (access (ctx->path, F_OK) != 0 && !option (OPTSAVEEMPTY))
307 remove (ctx->realpath);
312 p_delete(&ctx->cinfo);
316 /* return 0 on success, -1 on failure */
317 int mutt_sync_compressed (CONTEXT * ctx)
324 mutt_message (_("Compressing %s..."), ctx->realpath);
326 cmd = get_compression_cmd (ctx->cinfo->close, ctx);
330 if ((fp = fopen (ctx->realpath, "a")) == NULL) {
331 mutt_perror (ctx->realpath);
335 mutt_block_signals ();
336 if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
338 mutt_unblock_signals ();
339 mutt_error _("Unable to lock mailbox!");
341 ctx->cinfo->size = get_size(ctx->realpath);
349 sprintf (echo_cmd, _("echo Compressing %s..."), ctx->realpath);
350 mutt_system (echo_cmd);
351 if (mutt_system (cmd)) {
352 mutt_any_key_to_continue (NULL);
354 ("%s: Error compressing mailbox! Original mailbox deleted, uncompressed one kept!\n"),
359 mbox_unlock_compressed (ctx, fp);
360 mutt_unblock_signals ();
365 ctx->cinfo->size = get_size(ctx->realpath);
370 int mutt_slow_close_compressed (CONTEXT * ctx)
375 compress_info *ci = ctx->cinfo;
377 if (!(ctx->append && ((append = get_append_command (ctx->realpath, ctx))
378 || (append = ci->close)))) {
379 /* if we can not or should not append, we only have to remove the
380 compressed info, because sync was already called */
381 mutt_fast_close_compressed (ctx);
388 if (append == ci->close)
389 mutt_message (_("Compressing %s..."), ctx->realpath);
391 mutt_message (_("Compressed-appending to %s..."), ctx->realpath);
394 cmd = get_compression_cmd (append, ctx);
398 if ((fp = fopen (ctx->realpath, "a")) == NULL) {
399 mutt_perror (ctx->realpath);
403 mutt_block_signals ();
404 if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
406 mutt_unblock_signals ();
407 mutt_error _("Unable to lock mailbox!");
416 if (append == ci->close)
417 sprintf (echo_cmd, _("echo Compressing %s..."), ctx->realpath);
419 sprintf (echo_cmd, _("echo Compressed-appending to %s..."),
421 mutt_system (echo_cmd);
423 if (mutt_system (cmd)) {
424 mutt_any_key_to_continue (NULL);
425 mutt_error (_(" %s: Error compressing mailbox! Uncompressed one kept!\n"),
428 mbox_unlock_compressed (ctx, fp);
429 mutt_unblock_signals ();
434 mbox_unlock_compressed (ctx, fp);
435 mutt_unblock_signals ();
440 p_delete(&ctx->cinfo);
445 mx_t const compress_mx = {
451 mutt_open_read_compressed,