sort out some prototypes, put them where they belong.
[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 <errno.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <sys/stat.h>
14
15 #include <lib-lib/lib-lib.h>
16
17 #include <lib-sys/mutt_signal.h>
18 #include <lib-sys/unix.h>
19
20 #include <lib-ui/curses.h>
21
22 #include "mutt.h"
23
24 #include "mx.h"
25 #include "mbox.h"
26
27 typedef struct {
28   const char *close;            /* close-hook  command */
29   const char *open;             /* open-hook   command */
30   const char *append;           /* append-hook command */
31   off_t size;                   /* size of real folder */
32 } COMPRESS_INFO;
33
34 char echo_cmd[HUGE_STRING];
35
36 /* parameters:
37  * ctx - context to lock
38  * excl - exclusive lock?
39  * retry - should retry if unable to lock?
40  */
41 int mbox_lock_compressed (CONTEXT * ctx, FILE * fp, int excl, int retry)
42 {
43   int r;
44
45   if ((r = mx_lock_file (ctx->realpath, fileno (fp), excl, 1, retry)) == 0)
46     ctx->locked = 1;
47   else if (retry && !excl) {
48     ctx->readonly = 1;
49     return 0;
50   }
51
52   return (r);
53 }
54
55 void mbox_unlock_compressed (CONTEXT * ctx, FILE * fp)
56 {
57   if (ctx->locked) {
58     fflush (fp);
59
60     mx_unlock_file (ctx->realpath, fileno (fp), 1);
61     ctx->locked = 0;
62   }
63 }
64
65 static int is_new (const char *path)
66 {
67   return (access (path, W_OK) != 0 && errno == ENOENT) ? 1 : 0;
68 }
69
70 static const char *find_compress_hook (int type, const char *path)
71 {
72   const char *c = mutt_find_hook (type, path);
73
74   return (!c || !*c) ? NULL : c;
75 }
76
77 int mutt_can_read_compressed (const char *path)
78 {
79   return find_compress_hook (M_OPENHOOK, path) ? 1 : 0;
80 }
81
82 /* if the file is new, we really do not append, but create, and so use
83  * close-hook, and not append-hook 
84  */
85 static const char *get_append_command (const char *path, const CONTEXT * ctx)
86 {
87   COMPRESS_INFO *ci = (COMPRESS_INFO *) ctx->compressinfo;
88
89   return (is_new (path)) ? ci->close : ci->append;
90 }
91
92 int mutt_can_append_compressed (const char *path)
93 {
94   int magic;
95
96   if (is_new (path))
97     return (find_compress_hook (M_CLOSEHOOK, path) ? 1 : 0);
98
99   magic = mx_get_magic (path);
100
101   if (magic != 0 && magic != M_COMPRESSED)
102     return 0;
103
104   return (find_compress_hook (M_APPENDHOOK, path)
105           || (find_compress_hook (M_OPENHOOK, path)
106               && find_compress_hook (M_CLOSEHOOK, path))) ? 1 : 0;
107 }
108
109 /* open a compressed mailbox */
110 static COMPRESS_INFO *set_compress_info (CONTEXT * ctx)
111 {
112   COMPRESS_INFO *ci;
113
114   /* Now lets uncompress this thing */
115   ci = p_new(COMPRESS_INFO, 1);
116   ctx->compressinfo = (void *) ci;
117   ci->append = find_compress_hook (M_APPENDHOOK, ctx->path);
118   ci->open = find_compress_hook (M_OPENHOOK, ctx->path);
119   ci->close = find_compress_hook (M_CLOSEHOOK, ctx->path);
120   return ci;
121 }
122
123 static void set_path (CONTEXT * ctx)
124 {
125   char tmppath[_POSIX_PATH_MAX];
126
127   /* Setup the right paths */
128   ctx->realpath = ctx->path;
129
130   /* Uncompress to /tmp */
131   mutt_mktemp (tmppath);
132   ctx->path = p_dupstr(tmppath, m_strlen(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, ssize_t destlen,
152                                             char op, const char *src,
153                                             const char *fmt,
154                                             const char *ifstring __attribute__ ((unused)),
155                                             const char *elsestring __attribute__ ((unused)),
156                                             unsigned long data,
157                                             format_flag flags __attribute__ ((unused)))
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 m_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     p_delete(&ctx->compressinfo);
199     p_delete(&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     p_delete(&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
233   if ((fp = fopen (ctx->realpath, "r")) == NULL) {
234     mutt_perror (ctx->realpath);
235     p_delete(&cmd);
236     return (-1);
237   }
238   mutt_block_signals ();
239   if (mbox_lock_compressed (ctx, fp, 0, 1) == -1) {
240     fclose (fp);
241     mutt_unblock_signals ();
242     mutt_error _("Unable to lock mailbox!");
243
244     p_delete(&cmd);
245     return (-1);
246   }
247
248   endwin ();
249   fflush (stdout);
250   sprintf (echo_cmd, _("echo Decompressing %s..."), ctx->realpath);
251   mutt_system (echo_cmd);
252   rc = mutt_system (cmd);
253   mbox_unlock_compressed (ctx, fp);
254   mutt_unblock_signals ();
255   fclose (fp);
256
257   if (rc) {
258     mutt_any_key_to_continue (NULL);
259     ctx->magic = 0;
260     p_delete(&ctx->compressinfo);
261     mutt_error (_("Error executing: %s : unable to open the mailbox!\n"),
262                 cmd);
263   }
264   p_delete(&cmd);
265   if (rc)
266     return (-1);
267
268   if (mutt_check_mailbox_compressed (ctx))
269     return (-1);
270
271   ctx->magic = mx_get_magic (ctx->path);
272
273   return (0);
274 }
275
276 void restore_path (CONTEXT * ctx)
277 {
278   p_delete(&ctx->path);
279   ctx->path = ctx->realpath;
280 }
281
282 /* remove the temporary mailbox */
283 void remove_file (CONTEXT * ctx)
284 {
285   if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
286     remove (ctx->path);
287 }
288
289 int mutt_open_append_compressed (CONTEXT * ctx)
290 {
291   FILE *fh;
292   COMPRESS_INFO *ci = set_compress_info (ctx);
293
294   if (!get_append_command (ctx->path, ctx)) {
295     if (ci->open && ci->close)
296       return (mutt_open_read_compressed (ctx));
297
298     ctx->magic = 0;
299     p_delete(&ctx->compressinfo);
300     return (-1);
301   }
302
303   set_path (ctx);
304
305   ctx->magic = DefaultMagic;
306
307   if (!is_new (ctx->realpath))
308     if (ctx->magic == M_MBOX || ctx->magic == M_MMDF)
309       if ((fh = safe_fopen (ctx->path, "w")))
310         fclose (fh);
311   /* No error checking - the parent function will catch it */
312
313   return (0);
314 }
315
316 /* close a compressed mailbox */
317 void mutt_fast_close_compressed (CONTEXT * ctx)
318 {
319   if (ctx->compressinfo) {
320     if (ctx->fp)
321       fclose (ctx->fp);
322     ctx->fp = NULL;
323     /* if the folder was removed, remove the gzipped folder too */
324     if (access (ctx->path, F_OK) != 0 && !option (OPTSAVEEMPTY))
325       remove (ctx->realpath);
326     else
327       remove_file (ctx);
328
329     restore_path (ctx);
330     p_delete(&ctx->compressinfo);
331   }
332 }
333
334 /* return 0 on success, -1 on failure */
335 int mutt_sync_compressed (CONTEXT * ctx)
336 {
337   char *cmd;
338   int rc = 0;
339   FILE *fp;
340   COMPRESS_INFO *ci = (COMPRESS_INFO *) ctx->compressinfo;
341
342   if (!ctx->quiet)
343     mutt_message (_("Compressing %s..."), ctx->realpath);
344
345   cmd = get_compression_cmd (ci->close, ctx);
346   if (cmd == NULL)
347     return (-1);
348
349   if ((fp = fopen (ctx->realpath, "a")) == NULL) {
350     mutt_perror (ctx->realpath);
351     p_delete(&cmd);
352     return (-1);
353   }
354   mutt_block_signals ();
355   if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
356     fclose (fp);
357     mutt_unblock_signals ();
358     mutt_error _("Unable to lock mailbox!");
359
360     store_size (ctx);
361
362     p_delete(&cmd);
363     return (-1);
364   }
365
366   endwin ();
367   fflush (stdout);
368   sprintf (echo_cmd, _("echo Compressing %s..."), ctx->realpath);
369   mutt_system (echo_cmd);
370   if (mutt_system (cmd)) {
371     mutt_any_key_to_continue (NULL);
372     mutt_error (_
373                 ("%s: Error compressing mailbox! Original mailbox deleted, uncompressed one kept!\n"),
374                 ctx->path);
375     rc = -1;
376   }
377
378   mbox_unlock_compressed (ctx, fp);
379   mutt_unblock_signals ();
380   fclose (fp);
381
382   p_delete(&cmd);
383
384   store_size (ctx);
385
386   return (rc);
387 }
388
389 int mutt_slow_close_compressed (CONTEXT * ctx)
390 {
391   FILE *fp;
392   const char *append;
393   char *cmd;
394   COMPRESS_INFO *ci = (COMPRESS_INFO *) ctx->compressinfo;
395
396   if (!(ctx->append && ((append = get_append_command (ctx->realpath, ctx))
397                         || (append = ci->close)))) {    /* if we can not or should not append,
398                                                          * we only have to remove the compressed info, because sync was already
399                                                          * called 
400                                                          */
401     mutt_fast_close_compressed (ctx);
402     return (0);
403   }
404
405   if (ctx->fp)
406     fclose (ctx->fp);
407   ctx->fp = NULL;
408
409   if (!ctx->quiet) {
410     if (append == ci->close)
411       mutt_message (_("Compressing %s..."), ctx->realpath);
412     else
413       mutt_message (_("Compressed-appending to %s..."), ctx->realpath);
414   }
415
416   cmd = get_compression_cmd (append, ctx);
417   if (cmd == NULL)
418     return (-1);
419
420   if ((fp = fopen (ctx->realpath, "a")) == NULL) {
421     mutt_perror (ctx->realpath);
422     p_delete(&cmd);
423     return (-1);
424   }
425   mutt_block_signals ();
426   if (mbox_lock_compressed (ctx, fp, 1, 1) == -1) {
427     fclose (fp);
428     mutt_unblock_signals ();
429     mutt_error _("Unable to lock mailbox!");
430
431     p_delete(&cmd);
432     return (-1);
433   }
434
435   endwin ();
436   fflush (stdout);
437
438   if (append == ci->close)
439     sprintf (echo_cmd, _("echo Compressing %s..."), ctx->realpath);
440   else
441     sprintf (echo_cmd, _("echo Compressed-appending to %s..."),
442              ctx->realpath);
443   mutt_system (echo_cmd);
444
445   if (mutt_system (cmd)) {
446     mutt_any_key_to_continue (NULL);
447     mutt_error (_
448                 (" %s: Error compressing mailbox!  Uncompressed one kept!\n"),
449                 ctx->path);
450     p_delete(&cmd);
451     mbox_unlock_compressed (ctx, fp);
452     mutt_unblock_signals ();
453     fclose (fp);
454     return (-1);
455   }
456
457   mbox_unlock_compressed (ctx, fp);
458   mutt_unblock_signals ();
459   fclose (fp);
460   remove_file (ctx);
461   restore_path (ctx);
462   p_delete(&cmd);
463   p_delete(&ctx->compressinfo);
464
465   return (0);
466 }
467
468 mx_t* compress_reg_mx (void) {
469   mx_t* fmt = p_new(mx_t, 1);
470   fmt->type = M_COMPRESSED;
471   fmt->local = 1;
472   fmt->mx_is_magic = mbox_is_magic;
473   fmt->mx_check_empty = mbox_check_empty;
474   fmt->mx_access = access;
475   fmt->mx_open_mailbox = mutt_open_read_compressed;
476   return (fmt);
477 }