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