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