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/lib-ui.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, retry)) == 0)
43 else if (retry && !excl) {
51 static void mbox_unlock_compressed (CONTEXT * ctx, FILE * fp)
55 mx_unlock_file(ctx->realpath, fileno(fp));
60 static int is_new (const char *path)
62 return access (path, W_OK) && errno == ENOENT;
65 static const char *find_compress_hook (int type, const char *path)
67 int len = strlen(path);
68 if (len > 3 && !strcmp(path + len - 3, ".gz")) {
70 case M_OPENHOOK: return "gzip -cd %f > %t";
71 case M_CLOSEHOOK: return "gzip -cd %t > %f";
72 case M_APPENDHOOK: return "gzip -cd %t >> %f";
76 if (len > 4 && !strcmp(path + len - 4, ".bz2")) {
78 case M_OPENHOOK: return "bzip2 -cd %f > %t";
79 case M_CLOSEHOOK: return "bzip2 -cd %t > %f";
80 case M_APPENDHOOK: return "bzip2 -cd %t >> %f";
87 int mutt_can_read_compressed (const char *path)
89 return find_compress_hook (M_OPENHOOK, path) ? 1 : 0;
92 /* if the file is new, we really do not append, but create, and so use
93 * close-hook, and not append-hook
95 static const char *get_append_command (const char *path, const CONTEXT * ctx)
97 return is_new(path) ? ctx->cinfo->close : ctx->cinfo->append;
100 int mutt_can_append_compressed (const char *path)
105 return find_compress_hook (M_CLOSEHOOK, path) ? 1 : 0;
107 magic = mx_get_magic (path);
109 if (magic != 0 && magic != M_COMPRESSED)
112 return (find_compress_hook (M_APPENDHOOK, path)
113 || (find_compress_hook (M_OPENHOOK, path)
114 && find_compress_hook (M_CLOSEHOOK, path))) ? 1 : 0;
117 /* open a compressed mailbox */
118 static compress_info *set_compress_info (CONTEXT * ctx)
120 compress_info *ci = p_new(compress_info, 1);
122 /* Now lets uncompress this thing */
123 ci->append = find_compress_hook (M_APPENDHOOK, ctx->path);
124 ci->open = find_compress_hook (M_OPENHOOK, ctx->path);
125 ci->close = find_compress_hook (M_CLOSEHOOK, ctx->path);
127 return (ctx->cinfo = ci);
130 static int get_size (const char *path)
134 if (stat (path, &sb) != 0)
140 compresshook_format_str(char *dest, ssize_t destlen,
141 char op, const char *src, const char *fmt,
142 const char *ifstr __attribute__ ((unused)),
143 const char *elstr __attribute__ ((unused)),
145 format_flag flags __attribute__ ((unused)))
149 CONTEXT *ctx = data.ptr;
153 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
154 snprintf (dest, destlen, tmp, ctx->realpath);
157 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
158 snprintf (dest, destlen, tmp, ctx->path);
164 /* check that the command has both %f and %t
165 * 0 means OK, -1 means error
167 int mutt_test_compress_command (const char *cmd)
169 return (strstr (cmd, "%f") && strstr (cmd, "%t")) ? 0 : -1;
172 static char *get_compression_cmd(const char *cmd, const CONTEXT *ctx)
174 char buf[_POSIX_PATH_MAX];
175 m_strformat(buf, sizeof(buf), 0, cmd, compresshook_format_str, (void*)ctx, 0);
176 return m_strdup(buf);
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)
196 char tmppath[_POSIX_PATH_MAX];
199 compress_info *ci = set_compress_info (ctx);
203 p_delete(&ctx->cinfo);
206 if (!ci->close || access (ctx->path, W_OK) != 0)
209 /* Setup the right paths */
210 ctx->realpath = ctx->path;
212 /* Uncompress to /tmp */
213 tmpfd = m_tempfd(tmppath, sizeof(tmppath), NONULL(mod_core.tmpdir), NULL);
214 /* If we cannot open tempfile, that means the file already exists (!?)
215 * or we are following a symlink, which is bad and insecure.
222 ctx->path = p_dupstr(tmppath, m_strlen(tmppath));
223 ctx->cinfo->size = get_size(ctx->realpath);
226 mutt_message (_("Decompressing %s..."), ctx->realpath);
228 cmd = get_compression_cmd (ci->open, ctx);
232 if ((fp = fopen (ctx->realpath, "r")) == NULL) {
233 mutt_perror (ctx->realpath);
237 mutt_block_signals ();
238 if (mbox_lock_compressed (ctx, fp, 0, 1) == -1) {
240 mutt_unblock_signals ();
241 mutt_error _("Unable to lock mailbox!");
249 sprintf (echo_cmd, _("echo Decompressing %s..."), ctx->realpath);
250 mutt_system (echo_cmd);
251 rc = mutt_system (cmd);
252 mbox_unlock_compressed (ctx, fp);
253 mutt_unblock_signals ();
257 mutt_any_key_to_continue (NULL);
259 p_delete(&ctx->cinfo);
260 mutt_error(_("Error executing: %s : unable to open the mailbox!\n"), cmd);
266 if (mutt_check_mailbox_compressed (ctx))
269 ctx->magic = mx_get_magic (ctx->path);
274 static void restore_path (CONTEXT * ctx)
276 p_delete(&ctx->path);
277 ctx->path = ctx->realpath;
280 /* remove the temporary mailbox */
281 static void remove_file (CONTEXT * ctx)
283 if (ctx->magic == M_MBOX)
287 int mutt_open_append_compressed (CONTEXT * ctx)
290 compress_info *ci = set_compress_info (ctx);
291 char tmppath[_POSIX_PATH_MAX];
293 if (!get_append_command (ctx->path, ctx)) {
294 if (ci->open && ci->close)
295 return mutt_open_read_compressed(ctx);
298 p_delete(&ctx->cinfo);
302 /* Setup the right paths */
303 ctx->realpath = ctx->path;
305 /* Uncompress to /tmp */
306 fh = m_tempfile(tmppath, sizeof(tmppath), NONULL(mod_core.tmpdir), NULL);
309 ctx->path = p_dupstr(tmppath, m_strlen(tmppath));
311 if (is_new (ctx->realpath) || ctx->magic != M_MBOX)
314 /* No error checking - the parent function will catch it */
318 /* close a compressed mailbox */
319 void mutt_fast_close_compressed (CONTEXT * ctx)
324 /* if the folder was removed, remove the gzipped folder too */
327 p_delete(&ctx->cinfo);
331 /* return 0 on success, -1 on failure */
332 int mutt_sync_compressed (CONTEXT * ctx)
339 mutt_message (_("Compressing %s..."), ctx->realpath);
341 cmd = get_compression_cmd (ctx->cinfo->close, ctx);
345 if ((fp = fopen (ctx->realpath, "a")) == NULL) {
346 mutt_perror (ctx->realpath);
350 mutt_block_signals ();
351 if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
353 mutt_unblock_signals ();
354 mutt_error _("Unable to lock mailbox!");
356 ctx->cinfo->size = get_size(ctx->realpath);
364 sprintf (echo_cmd, _("echo Compressing %s..."), ctx->realpath);
365 mutt_system (echo_cmd);
366 if (mutt_system (cmd)) {
367 mutt_any_key_to_continue (NULL);
369 ("%s: Error compressing mailbox! Original mailbox deleted, uncompressed one kept!\n"),
374 mbox_unlock_compressed (ctx, fp);
375 mutt_unblock_signals ();
380 ctx->cinfo->size = get_size(ctx->realpath);
385 int mutt_slow_close_compressed (CONTEXT * ctx)
390 compress_info *ci = ctx->cinfo;
392 if (!(ctx->append && ((append = get_append_command (ctx->realpath, ctx))
393 || (append = ci->close)))) {
394 /* if we can not or should not append, we only have to remove the
395 compressed info, because sync was already called */
396 mutt_fast_close_compressed (ctx);
403 if (append == ci->close)
404 mutt_message (_("Compressing %s..."), ctx->realpath);
406 mutt_message (_("Compressed-appending to %s..."), ctx->realpath);
409 cmd = get_compression_cmd (append, ctx);
413 if ((fp = fopen (ctx->realpath, "a")) == NULL) {
414 mutt_perror (ctx->realpath);
418 mutt_block_signals ();
419 if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
421 mutt_unblock_signals ();
422 mutt_error _("Unable to lock mailbox!");
431 if (append == ci->close)
432 sprintf (echo_cmd, _("echo Compressing %s..."), ctx->realpath);
434 sprintf (echo_cmd, _("echo Compressed-appending to %s..."),
436 mutt_system (echo_cmd);
438 if (mutt_system (cmd)) {
439 mutt_any_key_to_continue (NULL);
440 mutt_error (_(" %s: Error compressing mailbox! Uncompressed one kept!\n"),
443 mbox_unlock_compressed (ctx, fp);
444 mutt_unblock_signals ();
449 mbox_unlock_compressed (ctx, fp);
450 mutt_unblock_signals ();
455 p_delete(&ctx->cinfo);
460 mx_t const compress_mx = {
466 mutt_open_read_compressed,