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