drop the horrible list2_t for good.
[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 <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 typedef struct {
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 } COMPRESS_INFO;
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   COMPRESS_INFO *ci = (COMPRESS_INFO *) ctx->compressinfo;
84
85   return (is_new (path)) ? ci->close : ci->append;
86 }
87
88 int mutt_can_append_compressed (const char *path)
89 {
90   int magic;
91
92   if (is_new (path))
93     return (find_compress_hook (M_CLOSEHOOK, path) ? 1 : 0);
94
95   magic = mx_get_magic (path);
96
97   if (magic != 0 && magic != M_COMPRESSED)
98     return 0;
99
100   return (find_compress_hook (M_APPENDHOOK, path)
101           || (find_compress_hook (M_OPENHOOK, path)
102               && find_compress_hook (M_CLOSEHOOK, path))) ? 1 : 0;
103 }
104
105 /* open a compressed mailbox */
106 static COMPRESS_INFO *set_compress_info (CONTEXT * ctx)
107 {
108   COMPRESS_INFO *ci;
109
110   /* Now lets uncompress this thing */
111   ci = p_new(COMPRESS_INFO, 1);
112   ctx->compressinfo = (void *) ci;
113   ci->append = find_compress_hook (M_APPENDHOOK, ctx->path);
114   ci->open = find_compress_hook (M_OPENHOOK, ctx->path);
115   ci->close = find_compress_hook (M_CLOSEHOOK, ctx->path);
116   return ci;
117 }
118
119 static void set_path (CONTEXT * ctx)
120 {
121   char tmppath[_POSIX_PATH_MAX];
122
123   /* Setup the right paths */
124   ctx->realpath = ctx->path;
125
126   /* Uncompress to /tmp */
127   mutt_mktemp (tmppath);
128   ctx->path = p_dupstr(tmppath, m_strlen(tmppath));
129 }
130
131 static int get_size (const char *path)
132 {
133   struct stat sb;
134
135   if (stat (path, &sb) != 0)
136     return 0;
137   return (sb.st_size);
138 }
139
140 static void store_size (CONTEXT * ctx)
141 {
142   COMPRESS_INFO *ci = (COMPRESS_INFO *) ctx->compressinfo;
143
144   ci->size = get_size (ctx->realpath);
145 }
146
147 static const char *compresshook_format_str (char *dest, ssize_t destlen,
148                                             char op, const char *src,
149                                             const char *fmt,
150                                             const char *ifstring __attribute__ ((unused)),
151                                             const char *elsestring __attribute__ ((unused)),
152                                             unsigned long data,
153                                             format_flag flags __attribute__ ((unused)))
154 {
155   char tmp[SHORT_STRING];
156
157   CONTEXT *ctx = (CONTEXT *) data;
158
159   switch (op) {
160   case 'f':
161     snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
162     snprintf (dest, destlen, tmp, ctx->realpath);
163     break;
164   case 't':
165     snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
166     snprintf (dest, destlen, tmp, ctx->path);
167     break;
168   }
169   return (src);
170 }
171
172 /* check that the command has both %f and %t
173  * 0 means OK, -1 means error
174  */
175 int mutt_test_compress_command (const char *cmd)
176 {
177   return (strstr (cmd, "%f") && strstr (cmd, "%t")) ? 0 : -1;
178 }
179
180 static char *get_compression_cmd (const char *cmd, const CONTEXT * ctx)
181 {
182   char expanded[_POSIX_PATH_MAX];
183
184   mutt_FormatString (expanded, sizeof (expanded), cmd,
185                      compresshook_format_str, (unsigned long) ctx, 0);
186   return m_strdup(expanded);
187 }
188
189 int mutt_check_mailbox_compressed (CONTEXT * ctx)
190 {
191   COMPRESS_INFO *ci = (COMPRESS_INFO *) ctx->compressinfo;
192
193   if (ci->size != get_size (ctx->realpath)) {
194     p_delete(&ctx->compressinfo);
195     p_delete(&ctx->realpath);
196     mutt_error _("Mailbox was corrupted!");
197
198     return (-1);
199   }
200   return (0);
201 }
202
203 int mutt_open_read_compressed (CONTEXT * ctx)
204 {
205   char *cmd;
206   FILE *fp;
207   int rc;
208
209   COMPRESS_INFO *ci = set_compress_info (ctx);
210
211   if (!ci->open) {
212     ctx->magic = 0;
213     p_delete(&ctx->compressinfo);
214     return (-1);
215   }
216   if (!ci->close || access (ctx->path, W_OK) != 0)
217     ctx->readonly = 1;
218
219   set_path (ctx);
220   store_size (ctx);
221
222   if (!ctx->quiet)
223     mutt_message (_("Decompressing %s..."), ctx->realpath);
224
225   cmd = get_compression_cmd (ci->open, ctx);
226   if (cmd == NULL)
227     return (-1);
228
229   if ((fp = fopen (ctx->realpath, "r")) == NULL) {
230     mutt_perror (ctx->realpath);
231     p_delete(&cmd);
232     return (-1);
233   }
234   mutt_block_signals ();
235   if (mbox_lock_compressed (ctx, fp, 0, 1) == -1) {
236     fclose (fp);
237     mutt_unblock_signals ();
238     mutt_error _("Unable to lock mailbox!");
239
240     p_delete(&cmd);
241     return (-1);
242   }
243
244   endwin ();
245   fflush (stdout);
246   sprintf (echo_cmd, _("echo Decompressing %s..."), ctx->realpath);
247   mutt_system (echo_cmd);
248   rc = mutt_system (cmd);
249   mbox_unlock_compressed (ctx, fp);
250   mutt_unblock_signals ();
251   fclose (fp);
252
253   if (rc) {
254     mutt_any_key_to_continue (NULL);
255     ctx->magic = 0;
256     p_delete(&ctx->compressinfo);
257     mutt_error (_("Error executing: %s : unable to open the mailbox!\n"),
258                 cmd);
259   }
260   p_delete(&cmd);
261   if (rc)
262     return (-1);
263
264   if (mutt_check_mailbox_compressed (ctx))
265     return (-1);
266
267   ctx->magic = mx_get_magic (ctx->path);
268
269   return (0);
270 }
271
272 static void restore_path (CONTEXT * ctx)
273 {
274   p_delete(&ctx->path);
275   ctx->path = ctx->realpath;
276 }
277
278 /* remove the temporary mailbox */
279 static void remove_file (CONTEXT * ctx)
280 {
281   if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
282     remove (ctx->path);
283 }
284
285 int mutt_open_append_compressed (CONTEXT * ctx)
286 {
287   FILE *fh;
288   COMPRESS_INFO *ci = set_compress_info (ctx);
289
290   if (!get_append_command (ctx->path, ctx)) {
291     if (ci->open && ci->close)
292       return (mutt_open_read_compressed (ctx));
293
294     ctx->magic = 0;
295     p_delete(&ctx->compressinfo);
296     return (-1);
297   }
298
299   set_path (ctx);
300
301   ctx->magic = DefaultMagic;
302
303   if (!is_new (ctx->realpath))
304     if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
305       if ((fh = safe_fopen (ctx->path, "w")))
306         fclose (fh);
307   /* No error checking - the parent function will catch it */
308
309   return (0);
310 }
311
312 /* close a compressed mailbox */
313 void mutt_fast_close_compressed (CONTEXT * ctx)
314 {
315   if (ctx->compressinfo) {
316     if (ctx->fp)
317       fclose (ctx->fp);
318     ctx->fp = NULL;
319     /* if the folder was removed, remove the gzipped folder too */
320     if (access (ctx->path, F_OK) != 0 && !option (OPTSAVEEMPTY))
321       remove (ctx->realpath);
322     else
323       remove_file (ctx);
324
325     restore_path (ctx);
326     p_delete(&ctx->compressinfo);
327   }
328 }
329
330 /* return 0 on success, -1 on failure */
331 int mutt_sync_compressed (CONTEXT * ctx)
332 {
333   char *cmd;
334   int rc = 0;
335   FILE *fp;
336   COMPRESS_INFO *ci = (COMPRESS_INFO *) ctx->compressinfo;
337
338   if (!ctx->quiet)
339     mutt_message (_("Compressing %s..."), ctx->realpath);
340
341   cmd = get_compression_cmd (ci->close, ctx);
342   if (cmd == NULL)
343     return (-1);
344
345   if ((fp = fopen (ctx->realpath, "a")) == NULL) {
346     mutt_perror (ctx->realpath);
347     p_delete(&cmd);
348     return (-1);
349   }
350   mutt_block_signals ();
351   if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
352     fclose (fp);
353     mutt_unblock_signals ();
354     mutt_error _("Unable to lock mailbox!");
355
356     store_size (ctx);
357
358     p_delete(&cmd);
359     return (-1);
360   }
361
362   endwin ();
363   fflush (stdout);
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);
368     mutt_error (_
369                 ("%s: Error compressing mailbox! Original mailbox deleted, uncompressed one kept!\n"),
370                 ctx->path);
371     rc = -1;
372   }
373
374   mbox_unlock_compressed (ctx, fp);
375   mutt_unblock_signals ();
376   fclose (fp);
377
378   p_delete(&cmd);
379
380   store_size (ctx);
381
382   return (rc);
383 }
384
385 int mutt_slow_close_compressed (CONTEXT * ctx)
386 {
387   FILE *fp;
388   const char *append;
389   char *cmd;
390   COMPRESS_INFO *ci = (COMPRESS_INFO *) ctx->compressinfo;
391
392   if (!(ctx->append && ((append = get_append_command (ctx->realpath, ctx))
393                         || (append = ci->close)))) {    /* if we can not or should not append,
394                                                          * we only have to remove the compressed info, because sync was already
395                                                          * called 
396                                                          */
397     mutt_fast_close_compressed (ctx);
398     return (0);
399   }
400
401   if (ctx->fp)
402     fclose (ctx->fp);
403   ctx->fp = NULL;
404
405   if (!ctx->quiet) {
406     if (append == ci->close)
407       mutt_message (_("Compressing %s..."), ctx->realpath);
408     else
409       mutt_message (_("Compressed-appending to %s..."), ctx->realpath);
410   }
411
412   cmd = get_compression_cmd (append, ctx);
413   if (cmd == NULL)
414     return (-1);
415
416   if ((fp = fopen (ctx->realpath, "a")) == NULL) {
417     mutt_perror (ctx->realpath);
418     p_delete(&cmd);
419     return (-1);
420   }
421   mutt_block_signals ();
422   if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
423     fclose (fp);
424     mutt_unblock_signals ();
425     mutt_error _("Unable to lock mailbox!");
426
427     p_delete(&cmd);
428     return (-1);
429   }
430
431   endwin ();
432   fflush (stdout);
433
434   if (append == ci->close)
435     sprintf (echo_cmd, _("echo Compressing %s..."), ctx->realpath);
436   else
437     sprintf (echo_cmd, _("echo Compressed-appending to %s..."),
438              ctx->realpath);
439   mutt_system (echo_cmd);
440
441   if (mutt_system (cmd)) {
442     mutt_any_key_to_continue (NULL);
443     mutt_error (_
444                 (" %s: Error compressing mailbox!  Uncompressed one kept!\n"),
445                 ctx->path);
446     p_delete(&cmd);
447     mbox_unlock_compressed (ctx, fp);
448     mutt_unblock_signals ();
449     fclose (fp);
450     return (-1);
451   }
452
453   mbox_unlock_compressed (ctx, fp);
454   mutt_unblock_signals ();
455   fclose (fp);
456   remove_file (ctx);
457   restore_path (ctx);
458   p_delete(&cmd);
459   p_delete(&ctx->compressinfo);
460
461   return (0);
462 }
463
464 mx_t const compress_mx = {
465     M_COMPRESSED,
466     1,
467     mbox_is_magic,
468     mbox_check_empty,
469     access,
470     mutt_open_read_compressed,
471     NULL,
472     NULL,
473     NULL,
474     NULL,
475     NULL,
476     NULL,
477 };