e89d438944d8fb7a8b13c633b9667f67eef7e7e1
[apps/madmutt.git] / lib-mx / compress.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1997 Alain Penders <Alain@Finale-Dev.com>
4  *
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.
8  */
9
10 #include <lib-lib/lib-lib.h>
11
12 #include <lib-sys/mutt_signal.h>
13 #include <lib-sys/unix.h>
14
15 #include <lib-ui/curses.h>
16
17 #include "mutt.h"
18
19 #include "mx.h"
20 #include "mbox.h"
21 #include "compress.h"
22
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 */
28 };
29
30 char echo_cmd[HUGE_STRING];
31
32 /* parameters:
33  * ctx - context to lock
34  * excl - exclusive lock?
35  * retry - should retry if unable to lock?
36  */
37 static int mbox_lock_compressed (CONTEXT * ctx, FILE * fp, int excl, int retry)
38 {
39   int r;
40
41   if ((r = mx_lock_file (ctx->realpath, fileno (fp), excl, 1, retry)) == 0)
42     ctx->locked = 1;
43   else if (retry && !excl) {
44     ctx->readonly = 1;
45     return 0;
46   }
47
48   return (r);
49 }
50
51 static void mbox_unlock_compressed (CONTEXT * ctx, FILE * fp)
52 {
53   if (ctx->locked) {
54     fflush (fp);
55
56     mx_unlock_file (ctx->realpath, fileno (fp), 1);
57     ctx->locked = 0;
58   }
59 }
60
61 static int is_new (const char *path)
62 {
63   return (access (path, W_OK) != 0 && errno == ENOENT) ? 1 : 0;
64 }
65
66 static const char *find_compress_hook (int type, const char *path)
67 {
68   const char *c = mutt_find_hook (type, path);
69
70   return (!c || !*c) ? NULL : c;
71 }
72
73 int mutt_can_read_compressed (const char *path)
74 {
75   return find_compress_hook (M_OPENHOOK, path) ? 1 : 0;
76 }
77
78 /* if the file is new, we really do not append, but create, and so use
79  * close-hook, and not append-hook 
80  */
81 static const char *get_append_command (const char *path, const CONTEXT * ctx)
82 {
83   return is_new(path) ? ctx->cinfo->close : ctx->cinfo->append;
84 }
85
86 int mutt_can_append_compressed (const char *path)
87 {
88   int magic;
89
90   if (is_new (path))
91     return (find_compress_hook (M_CLOSEHOOK, path) ? 1 : 0);
92
93   magic = mx_get_magic (path);
94
95   if (magic != 0 && magic != M_COMPRESSED)
96     return 0;
97
98   return (find_compress_hook (M_APPENDHOOK, path)
99           || (find_compress_hook (M_OPENHOOK, path)
100               && find_compress_hook (M_CLOSEHOOK, path))) ? 1 : 0;
101 }
102
103 /* open a compressed mailbox */
104 static compress_info *set_compress_info (CONTEXT * ctx)
105 {
106   compress_info *ci = p_new(compress_info, 1);
107
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);
112
113   return (ctx->cinfo = ci);
114 }
115
116 static void set_path (CONTEXT * ctx)
117 {
118   char tmppath[_POSIX_PATH_MAX];
119
120   /* Setup the right paths */
121   ctx->realpath = ctx->path;
122
123   /* Uncompress to /tmp */
124   mutt_mktemp (tmppath);
125   ctx->path = p_dupstr(tmppath, m_strlen(tmppath));
126 }
127
128 static int get_size (const char *path)
129 {
130   struct stat sb;
131
132   if (stat (path, &sb) != 0)
133     return 0;
134   return (sb.st_size);
135 }
136
137 static const char *compresshook_format_str (char *dest, ssize_t destlen,
138                                             char op, const char *src,
139                                             const char *fmt,
140                                             const char *ifstring __attribute__ ((unused)),
141                                             const char *elsestring __attribute__ ((unused)),
142                                             unsigned long data,
143                                             format_flag flags __attribute__ ((unused)))
144 {
145   char tmp[SHORT_STRING];
146
147   CONTEXT *ctx = (CONTEXT *) data;
148
149   switch (op) {
150   case 'f':
151     snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
152     snprintf (dest, destlen, tmp, ctx->realpath);
153     break;
154   case 't':
155     snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
156     snprintf (dest, destlen, tmp, ctx->path);
157     break;
158   }
159   return (src);
160 }
161
162 /* check that the command has both %f and %t
163  * 0 means OK, -1 means error
164  */
165 int mutt_test_compress_command (const char *cmd)
166 {
167   return (strstr (cmd, "%f") && strstr (cmd, "%t")) ? 0 : -1;
168 }
169
170 static char *get_compression_cmd (const char *cmd, const CONTEXT * ctx)
171 {
172   char expanded[_POSIX_PATH_MAX];
173
174   mutt_FormatString (expanded, sizeof (expanded), cmd,
175                      compresshook_format_str, (unsigned long) ctx, 0);
176   return m_strdup(expanded);
177 }
178
179 int mutt_check_mailbox_compressed (CONTEXT * ctx)
180 {
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!");
185
186     return (-1);
187   }
188   return (0);
189 }
190
191 int mutt_open_read_compressed (CONTEXT * ctx)
192 {
193   char *cmd;
194   FILE *fp;
195   int rc;
196
197   compress_info *ci = set_compress_info (ctx);
198
199   if (!ci->open) {
200     ctx->magic = 0;
201     p_delete(&ctx->cinfo);
202     return (-1);
203   }
204   if (!ci->close || access (ctx->path, W_OK) != 0)
205     ctx->readonly = 1;
206
207   set_path (ctx);
208   ctx->cinfo->size = get_size(ctx->realpath);
209
210   if (!ctx->quiet)
211     mutt_message (_("Decompressing %s..."), ctx->realpath);
212
213   cmd = get_compression_cmd (ci->open, ctx);
214   if (cmd == NULL)
215     return (-1);
216
217   if ((fp = fopen (ctx->realpath, "r")) == NULL) {
218     mutt_perror (ctx->realpath);
219     p_delete(&cmd);
220     return (-1);
221   }
222   mutt_block_signals ();
223   if (mbox_lock_compressed (ctx, fp, 0, 1) == -1) {
224     m_fclose(&fp);
225     mutt_unblock_signals ();
226     mutt_error _("Unable to lock mailbox!");
227
228     p_delete(&cmd);
229     return (-1);
230   }
231
232   endwin ();
233   fflush (stdout);
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 ();
239   m_fclose(&fp);
240
241   if (rc) {
242     mutt_any_key_to_continue (NULL);
243     ctx->magic = 0;
244     p_delete(&ctx->cinfo);
245     mutt_error(_("Error executing: %s : unable to open the mailbox!\n"), cmd);
246   }
247   p_delete(&cmd);
248   if (rc)
249     return (-1);
250
251   if (mutt_check_mailbox_compressed (ctx))
252     return (-1);
253
254   ctx->magic = mx_get_magic (ctx->path);
255
256   return (0);
257 }
258
259 static void restore_path (CONTEXT * ctx)
260 {
261   p_delete(&ctx->path);
262   ctx->path = ctx->realpath;
263 }
264
265 /* remove the temporary mailbox */
266 static void remove_file (CONTEXT * ctx)
267 {
268   if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
269     remove (ctx->path);
270 }
271
272 int mutt_open_append_compressed (CONTEXT * ctx)
273 {
274   FILE *fh;
275   compress_info *ci = set_compress_info (ctx);
276
277   if (!get_append_command (ctx->path, ctx)) {
278     if (ci->open && ci->close)
279       return (mutt_open_read_compressed (ctx));
280
281     ctx->magic = 0;
282     p_delete(&ctx->cinfo);
283     return (-1);
284   }
285
286   set_path (ctx);
287
288   ctx->magic = DefaultMagic;
289
290   if (!is_new (ctx->realpath))
291     if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
292       if ((fh = safe_fopen (ctx->path, "w")))
293         m_fclose(&fh);
294   /* No error checking - the parent function will catch it */
295
296   return (0);
297 }
298
299 /* close a compressed mailbox */
300 void mutt_fast_close_compressed (CONTEXT * ctx)
301 {
302   if (ctx->cinfo) {
303     m_fclose(&ctx->fp);
304
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);
308     else
309       remove_file (ctx);
310
311     restore_path (ctx);
312     p_delete(&ctx->cinfo);
313   }
314 }
315
316 /* return 0 on success, -1 on failure */
317 int mutt_sync_compressed (CONTEXT * ctx)
318 {
319   char *cmd;
320   int rc = 0;
321   FILE *fp;
322
323   if (!ctx->quiet)
324     mutt_message (_("Compressing %s..."), ctx->realpath);
325
326   cmd = get_compression_cmd (ctx->cinfo->close, ctx);
327   if (cmd == NULL)
328     return (-1);
329
330   if ((fp = fopen (ctx->realpath, "a")) == NULL) {
331     mutt_perror (ctx->realpath);
332     p_delete(&cmd);
333     return (-1);
334   }
335   mutt_block_signals ();
336   if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
337     m_fclose(&fp);
338     mutt_unblock_signals ();
339     mutt_error _("Unable to lock mailbox!");
340
341     ctx->cinfo->size = get_size(ctx->realpath);
342
343     p_delete(&cmd);
344     return (-1);
345   }
346
347   endwin ();
348   fflush (stdout);
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);
353     mutt_error (_
354                 ("%s: Error compressing mailbox! Original mailbox deleted, uncompressed one kept!\n"),
355                 ctx->path);
356     rc = -1;
357   }
358
359   mbox_unlock_compressed (ctx, fp);
360   mutt_unblock_signals ();
361   m_fclose(&fp);
362
363   p_delete(&cmd);
364
365   ctx->cinfo->size = get_size(ctx->realpath);
366
367   return (rc);
368 }
369
370 int mutt_slow_close_compressed (CONTEXT * ctx)
371 {
372   FILE *fp;
373   const char *append;
374   char *cmd;
375   compress_info *ci = ctx->cinfo;
376
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);
382     return (0);
383   }
384
385   m_fclose(&ctx->fp);
386
387   if (!ctx->quiet) {
388     if (append == ci->close)
389       mutt_message (_("Compressing %s..."), ctx->realpath);
390     else
391       mutt_message (_("Compressed-appending to %s..."), ctx->realpath);
392   }
393
394   cmd = get_compression_cmd (append, ctx);
395   if (cmd == NULL)
396     return (-1);
397
398   if ((fp = fopen (ctx->realpath, "a")) == NULL) {
399     mutt_perror (ctx->realpath);
400     p_delete(&cmd);
401     return (-1);
402   }
403   mutt_block_signals ();
404   if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
405     m_fclose(&fp);
406     mutt_unblock_signals ();
407     mutt_error _("Unable to lock mailbox!");
408
409     p_delete(&cmd);
410     return (-1);
411   }
412
413   endwin ();
414   fflush (stdout);
415
416   if (append == ci->close)
417     sprintf (echo_cmd, _("echo Compressing %s..."), ctx->realpath);
418   else
419     sprintf (echo_cmd, _("echo Compressed-appending to %s..."),
420              ctx->realpath);
421   mutt_system (echo_cmd);
422
423   if (mutt_system (cmd)) {
424     mutt_any_key_to_continue (NULL);
425     mutt_error (_(" %s: Error compressing mailbox!  Uncompressed one kept!\n"),
426                 ctx->path);
427     p_delete(&cmd);
428     mbox_unlock_compressed (ctx, fp);
429     mutt_unblock_signals ();
430     m_fclose(&fp);
431     return (-1);
432   }
433
434   mbox_unlock_compressed (ctx, fp);
435   mutt_unblock_signals ();
436   m_fclose(&fp);
437   remove_file (ctx);
438   restore_path (ctx);
439   p_delete(&cmd);
440   p_delete(&ctx->cinfo);
441
442   return (0);
443 }
444
445 mx_t const compress_mx = {
446     M_COMPRESSED,
447     1,
448     mbox_is_magic,
449     mbox_check_empty,
450     access,
451     mutt_open_read_compressed,
452     NULL,
453     NULL,
454     NULL,
455     NULL,
456     NULL,
457     NULL,
458 };