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