1 /*
2 ziptool.c -- tool for modifying zip archive in multiple ways
3 Copyright (C) 2012-2020 Dieter Baron and Thomas Klausner
4
5 This file is part of libzip, a library to manipulate ZIP archives.
6 The authors can be contacted at <libzip@nih.at>
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in
15 the documentation and/or other materials provided with the
16 distribution.
17 3. The names of the authors may not be used to endorse or promote
18 products derived from this software without specific prior
19 written permission.
20
21 THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "config.h"
35
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #ifdef _WIN32
44 /* WIN32 needs <fcntl.h> for _O_BINARY */
45 #include <fcntl.h>
46 #ifndef STDIN_FILENO
47 #define STDIN_FILENO _fileno(stdin)
48 #endif
49 #endif
50
51 #ifndef HAVE_GETOPT
52 #include "getopt.h"
53 #endif
54 extern int optopt;
55
56 #include "zip.h"
57
58 #include "compat.h"
59
60 typedef struct dispatch_table_s {
61 const char *cmdline_name;
62 int argument_count;
63 const char *arg_names;
64 const char *description;
65 int (*function)(int argc, char *argv[]);
66 } dispatch_table_t;
67
68 static zip_flags_t get_flags(const char *arg);
69 static zip_int32_t get_compression_method(const char *arg);
70 static zip_uint16_t get_encryption_method(const char *arg);
71 static void hexdump(const zip_uint8_t *data, zip_uint16_t len);
72 int ziptool_post_close(const char *archive);
73
74 #ifndef FOR_REGRESS
75 #define OPTIONS_REGRESS ""
76 #define USAGE_REGRESS ""
77 #endif
78
79 zip_t *za, *z_in[16];
80 unsigned int z_in_count;
81 zip_flags_t stat_flags;
82
83 static int
add(int argc,char * argv[])84 add(int argc, char *argv[]) {
85 zip_source_t *zs;
86
87 if ((zs = zip_source_buffer(za, argv[1], strlen(argv[1]), 0)) == NULL) {
88 fprintf(stderr, "can't create zip_source from buffer: %s\n", zip_strerror(za));
89 return -1;
90 }
91
92 if (zip_add(za, argv[0], zs) == -1) {
93 zip_source_free(zs);
94 fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
95 return -1;
96 }
97 return 0;
98 }
99
100 static int
add_dir(int argc,char * argv[])101 add_dir(int argc, char *argv[]) {
102 /* add directory */
103 if (zip_add_dir(za, argv[0]) < 0) {
104 fprintf(stderr, "can't add directory '%s': %s\n", argv[0], zip_strerror(za));
105 return -1;
106 }
107 return 0;
108 }
109
110 static int
add_file(int argc,char * argv[])111 add_file(int argc, char *argv[]) {
112 zip_source_t *zs;
113 zip_uint64_t start = strtoull(argv[2], NULL, 10);
114 zip_int64_t len = strtoll(argv[3], NULL, 10);
115
116 if (strcmp(argv[1], "/dev/stdin") == 0) {
117 if ((zs = zip_source_filep(za, stdin, start, len)) == NULL) {
118 fprintf(stderr, "can't create zip_source from stdin: %s\n", zip_strerror(za));
119 return -1;
120 }
121 }
122 else {
123 if ((zs = zip_source_file(za, argv[1], start, len)) == NULL) {
124 fprintf(stderr, "can't create zip_source from file: %s\n", zip_strerror(za));
125 return -1;
126 }
127 }
128
129 if (zip_add(za, argv[0], zs) == -1) {
130 zip_source_free(zs);
131 fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
132 return -1;
133 }
134 return 0;
135 }
136
137 static int
add_from_zip(int argc,char * argv[])138 add_from_zip(int argc, char *argv[]) {
139 zip_uint64_t idx, start;
140 zip_int64_t len;
141 int err;
142 zip_source_t *zs;
143 /* add from another zip file */
144 idx = strtoull(argv[2], NULL, 10);
145 start = strtoull(argv[3], NULL, 10);
146 len = strtoll(argv[4], NULL, 10);
147 if ((z_in[z_in_count] = zip_open(argv[1], ZIP_CHECKCONS, &err)) == NULL) {
148 zip_error_t error;
149 zip_error_init_with_code(&error, err);
150 fprintf(stderr, "can't open zip archive '%s': %s\n", argv[1], zip_error_strerror(&error));
151 zip_error_fini(&error);
152 return -1;
153 }
154 if ((zs = zip_source_zip(za, z_in[z_in_count], idx, 0, start, len)) == NULL) {
155 fprintf(stderr, "error creating file source from '%s' index '%" PRIu64 "': %s\n", argv[1], idx, zip_strerror(za));
156 zip_close(z_in[z_in_count]);
157 return -1;
158 }
159 if (zip_add(za, argv[0], zs) == -1) {
160 fprintf(stderr, "can't add file '%s': %s\n", argv[0], zip_strerror(za));
161 zip_source_free(zs);
162 zip_close(z_in[z_in_count]);
163 return -1;
164 }
165 z_in_count++;
166 return 0;
167 }
168
169 static int
cat(int argc,char * argv[])170 cat(int argc, char *argv[]) {
171 /* output file contents to stdout */
172 zip_uint64_t idx;
173 zip_int64_t n;
174 zip_file_t *zf;
175 char buf[8192];
176 int err;
177 idx = strtoull(argv[0], NULL, 10);
178
179 #ifdef _WIN32
180 /* Need to set stdout to binary mode for Windows */
181 setmode(fileno(stdout), _O_BINARY);
182 #endif
183 if ((zf = zip_fopen_index(za, idx, 0)) == NULL) {
184 fprintf(stderr, "can't open file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
185 return -1;
186 }
187 while ((n = zip_fread(zf, buf, sizeof(buf))) > 0) {
188 if (fwrite(buf, (size_t)n, 1, stdout) != 1) {
189 zip_fclose(zf);
190 fprintf(stderr, "can't write file contents to stdout: %s\n", strerror(errno));
191 return -1;
192 }
193 }
194 if (n == -1) {
195 fprintf(stderr, "can't read file at index '%" PRIu64 "': %s\n", idx, zip_file_strerror(zf));
196 zip_fclose(zf);
197 return -1;
198 }
199 if ((err = zip_fclose(zf)) != 0) {
200 zip_error_t error;
201
202 zip_error_init_with_code(&error, err);
203 fprintf(stderr, "can't close file at index '%" PRIu64 "': %s\n", idx, zip_error_strerror(&error));
204 return -1;
205 }
206
207 return 0;
208 }
209
210 static int
count_extra(int argc,char * argv[])211 count_extra(int argc, char *argv[]) {
212 zip_int16_t count;
213 zip_uint64_t idx;
214 zip_flags_t ceflags = 0;
215 idx = strtoull(argv[0], NULL, 10);
216 ceflags = get_flags(argv[1]);
217 if ((count = zip_file_extra_fields_count(za, idx, ceflags)) < 0) {
218 fprintf(stderr, "can't get extra field count for file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
219 return -1;
220 }
221 else {
222 printf("Extra field count: %d\n", count);
223 }
224 return 0;
225 }
226
227 static int
count_extra_by_id(int argc,char * argv[])228 count_extra_by_id(int argc, char *argv[]) {
229 zip_int16_t count;
230 zip_uint16_t eid;
231 zip_flags_t ceflags = 0;
232 zip_uint64_t idx;
233 idx = strtoull(argv[0], NULL, 10);
234 eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
235 ceflags = get_flags(argv[2]);
236 if ((count = zip_file_extra_fields_count_by_id(za, idx, eid, ceflags)) < 0) {
237 fprintf(stderr, "can't get extra field count for file at index '%" PRIu64 "' and for id '%d': %s\n", idx, eid, zip_strerror(za));
238 return -1;
239 }
240 else {
241 printf("Extra field count: %d\n", count);
242 }
243 return 0;
244 }
245
delete(int argc,char * argv[])246 static int delete (int argc, char *argv[]) {
247 zip_uint64_t idx;
248 idx = strtoull(argv[0], NULL, 10);
249 if (zip_delete(za, idx) < 0) {
250 fprintf(stderr, "can't delete file at index '%" PRIu64 "': %s\n", idx, zip_strerror(za));
251 return -1;
252 }
253 return 0;
254 }
255
256 static int
delete_extra(int argc,char * argv[])257 delete_extra(int argc, char *argv[]) {
258 zip_flags_t geflags;
259 zip_uint16_t eid;
260 zip_uint64_t idx;
261 idx = strtoull(argv[0], NULL, 10);
262 eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
263 geflags = get_flags(argv[2]);
264 if ((zip_file_extra_field_delete(za, idx, eid, geflags)) < 0) {
265 fprintf(stderr, "can't delete extra field data for file at index '%" PRIu64 "', extra field id '%d': %s\n", idx, eid, zip_strerror(za));
266 return -1;
267 }
268 return 0;
269 }
270
271 static int
delete_extra_by_id(int argc,char * argv[])272 delete_extra_by_id(int argc, char *argv[]) {
273 zip_flags_t geflags;
274 zip_uint16_t eid, eidx;
275 zip_uint64_t idx;
276 idx = strtoull(argv[0], NULL, 10);
277 eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
278 eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
279 geflags = get_flags(argv[3]);
280 if ((zip_file_extra_field_delete_by_id(za, idx, eid, eidx, geflags)) < 0) {
281 fprintf(stderr, "can't delete extra field data for file at index '%" PRIu64 "', extra field id '%d', extra field idx '%d': %s\n", idx, eid, eidx, zip_strerror(za));
282 return -1;
283 }
284 return 0;
285 }
286
287 static int
get_archive_comment(int argc,char * argv[])288 get_archive_comment(int argc, char *argv[]) {
289 const char *comment;
290 int len;
291 /* get archive comment */
292 if ((comment = zip_get_archive_comment(za, &len, 0)) == NULL)
293 printf("No archive comment\n");
294 else
295 printf("Archive comment: %.*s\n", len, comment);
296 return 0;
297 }
298
299 static int
get_extra(int argc,char * argv[])300 get_extra(int argc, char *argv[]) {
301 zip_flags_t geflags;
302 zip_uint16_t id, eidx, eflen;
303 const zip_uint8_t *efdata;
304 zip_uint64_t idx;
305 /* get extra field data */
306 idx = strtoull(argv[0], NULL, 10);
307 eidx = (zip_uint16_t)strtoull(argv[1], NULL, 10);
308 geflags = get_flags(argv[2]);
309 if ((efdata = zip_file_extra_field_get(za, idx, eidx, &id, &eflen, geflags)) == NULL) {
310 fprintf(stderr, "can't get extra field data for file at index %" PRIu64 ", extra field %d, flags %u: %s\n", idx, eidx, geflags, zip_strerror(za));
311 return -1;
312 }
313 printf("Extra field 0x%04x: len %d", id, eflen);
314 if (eflen > 0) {
315 printf(", data ");
316 hexdump(efdata, eflen);
317 }
318 printf("\n");
319 return 0;
320 }
321
322 static int
get_extra_by_id(int argc,char * argv[])323 get_extra_by_id(int argc, char *argv[]) {
324 zip_flags_t geflags;
325 zip_uint16_t eid, eidx, eflen;
326 const zip_uint8_t *efdata;
327 zip_uint64_t idx;
328 idx = strtoull(argv[0], NULL, 10);
329 eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
330 eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
331 geflags = get_flags(argv[3]);
332 if ((efdata = zip_file_extra_field_get_by_id(za, idx, eid, eidx, &eflen, geflags)) == NULL) {
333 fprintf(stderr, "can't get extra field data for file at index %" PRIu64 ", extra field id %d, ef index %d, flags %u: %s\n", idx, eid, eidx, geflags, zip_strerror(za));
334 return -1;
335 }
336 printf("Extra field 0x%04x: len %d", eid, eflen);
337 if (eflen > 0) {
338 printf(", data ");
339 hexdump(efdata, eflen);
340 }
341 printf("\n");
342 return 0;
343 }
344
345 static int
get_file_comment(int argc,char * argv[])346 get_file_comment(int argc, char *argv[]) {
347 const char *comment;
348 int len;
349 zip_uint64_t idx;
350 /* get file comment */
351 idx = strtoull(argv[0], NULL, 10);
352 if ((comment = zip_get_file_comment(za, idx, &len, 0)) == NULL) {
353 fprintf(stderr, "can't get comment for '%s': %s\n", zip_get_name(za, idx, 0), zip_strerror(za));
354 return -1;
355 }
356 else if (len == 0)
357 printf("No comment for '%s'\n", zip_get_name(za, idx, 0));
358 else
359 printf("File comment for '%s': %.*s\n", zip_get_name(za, idx, 0), len, comment);
360 return 0;
361 }
362
363 static int
get_num_entries(int argc,char * argv[])364 get_num_entries(int argc, char *argv[]) {
365 zip_int64_t count;
366 zip_flags_t flags;
367 /* get number of entries in archive */
368 flags = get_flags(argv[0]);
369 count = zip_get_num_entries(za, flags);
370 printf("%" PRId64 " entr%s in archive\n", count, count == 1 ? "y" : "ies");
371 return 0;
372 }
373
374 static int
name_locate(int argc,char * argv[])375 name_locate(int argc, char *argv[]) {
376 zip_flags_t flags;
377 zip_int64_t idx;
378 flags = get_flags(argv[1]);
379
380 if ((idx = zip_name_locate(za, argv[0], flags)) < 0) {
381 fprintf(stderr, "can't find entry with name '%s' using flags '%s'\n", argv[0], argv[1]);
382 }
383 else {
384 printf("name '%s' using flags '%s' found at index %" PRId64 "\n", argv[0], argv[1], idx);
385 }
386
387 return 0;
388 }
389
390 struct progress_userdata_s {
391 double percentage;
392 double limit;
393 };
394
395 struct progress_userdata_s progress_userdata;
396
397 static void
progress_callback(zip_t * archive,double percentage,void * ud)398 progress_callback(zip_t *archive, double percentage, void *ud) {
399 printf("%.1f%% done\n", percentage * 100);
400 progress_userdata.percentage = percentage;
401 }
402
403 static int
print_progress(int argc,char * argv[])404 print_progress(int argc, char *argv[]) {
405 zip_register_progress_callback_with_state(za, 0.001, progress_callback, NULL, NULL);
406 return 0;
407 }
408
409 static int
zrename(int argc,char * argv[])410 zrename(int argc, char *argv[]) {
411 zip_uint64_t idx;
412 idx = strtoull(argv[0], NULL, 10);
413 if (zip_rename(za, idx, argv[1]) < 0) {
414 fprintf(stderr, "can't rename file at index '%" PRIu64 "' to '%s': %s\n", idx, argv[1], zip_strerror(za));
415 return -1;
416 }
417 return 0;
418 }
419
420 static int
replace_file_contents(int argc,char * argv[])421 replace_file_contents(int argc, char *argv[]) {
422 /* replace file contents with data from command line */
423 const char *content;
424 zip_source_t *s;
425 zip_uint64_t idx;
426 idx = strtoull(argv[0], NULL, 10);
427 content = argv[1];
428 if ((s = zip_source_buffer(za, content, strlen(content), 0)) == NULL || zip_file_replace(za, idx, s, 0) < 0) {
429 zip_source_free(s);
430 fprintf(stderr, "error replacing file data: %s\n", zip_strerror(za));
431 return -1;
432 }
433 return 0;
434 }
435
436 static int
set_extra(int argc,char * argv[])437 set_extra(int argc, char *argv[]) {
438 zip_flags_t geflags;
439 zip_uint16_t eid, eidx;
440 const zip_uint8_t *efdata;
441 zip_uint64_t idx;
442 idx = strtoull(argv[0], NULL, 10);
443 eid = (zip_uint16_t)strtoull(argv[1], NULL, 10);
444 eidx = (zip_uint16_t)strtoull(argv[2], NULL, 10);
445 geflags = get_flags(argv[3]);
446 efdata = (zip_uint8_t *)argv[4];
447 if ((zip_file_extra_field_set(za, idx, eid, eidx, efdata, (zip_uint16_t)strlen((const char *)efdata), geflags)) < 0) {
448 fprintf(stderr, "can't set extra field data for file at index '%" PRIu64 "', extra field id '%d', index '%d': %s\n", idx, eid, eidx, zip_strerror(za));
449 return -1;
450 }
451 return 0;
452 }
453
454 static int
set_archive_comment(int argc,char * argv[])455 set_archive_comment(int argc, char *argv[]) {
456 if (zip_set_archive_comment(za, argv[0], (zip_uint16_t)strlen(argv[0])) < 0) {
457 fprintf(stderr, "can't set archive comment to '%s': %s\n", argv[0], zip_strerror(za));
458 return -1;
459 }
460 return 0;
461 }
462
463 static int
set_file_comment(int argc,char * argv[])464 set_file_comment(int argc, char *argv[]) {
465 zip_uint64_t idx;
466 idx = strtoull(argv[0], NULL, 10);
467 if (zip_file_set_comment(za, idx, argv[1], (zip_uint16_t)strlen(argv[1]), 0) < 0) {
468 fprintf(stderr, "can't set file comment at index '%" PRIu64 "' to '%s': %s\n", idx, argv[1], zip_strerror(za));
469 return -1;
470 }
471 return 0;
472 }
473
474 static int
set_file_compression(int argc,char * argv[])475 set_file_compression(int argc, char *argv[]) {
476 zip_int32_t method;
477 zip_uint32_t flags;
478 zip_uint64_t idx;
479 idx = strtoull(argv[0], NULL, 10);
480 method = get_compression_method(argv[1]);
481 flags = (zip_uint32_t)strtoull(argv[2], NULL, 10);
482 if (zip_set_file_compression(za, idx, method, flags) < 0) {
483 fprintf(stderr, "can't set file compression method at index '%" PRIu64 "' to '%s', flags '%" PRIu32 "': %s\n", idx, argv[1], flags, zip_strerror(za));
484 return -1;
485 }
486 return 0;
487 }
488
489 static int
set_file_encryption(int argc,char * argv[])490 set_file_encryption(int argc, char *argv[]) {
491 zip_uint16_t method;
492 zip_uint64_t idx;
493 char *password;
494 idx = strtoull(argv[0], NULL, 10);
495 method = get_encryption_method(argv[1]);
496 password = argv[2];
497 if (strlen(password) == 0) {
498 password = NULL;
499 }
500 if (zip_file_set_encryption(za, idx, method, password) < 0) {
501 fprintf(stderr, "can't set file encryption method at index '%" PRIu64 "' to '%s': %s\n", idx, argv[1], zip_strerror(za));
502 return -1;
503 }
504 return 0;
505 }
506
507 static int
set_file_dostime(int argc,char * argv[])508 set_file_dostime(int argc, char *argv[]) {
509 /* set file last modification time (mtime) directly */
510 zip_uint16_t dostime, dosdate;
511 zip_uint64_t idx;
512 idx = strtoull(argv[0], NULL, 10);
513 dostime = (zip_uint16_t)strtoull(argv[1], NULL, 10);
514 dosdate = (zip_uint16_t)strtoull(argv[2], NULL, 10);
515 if (zip_file_set_dostime(za, idx, dostime, dosdate, 0) < 0) {
516 fprintf(stderr, "can't set file dostime at index '%" PRIu64 "' to '%d'/'%d': %s\n", idx, (int)dostime, (int)dosdate, zip_strerror(za));
517 return -1;
518 }
519 return 0;
520 }
521
522 static int
set_file_mtime(int argc,char * argv[])523 set_file_mtime(int argc, char *argv[]) {
524 /* set file last modification time (mtime) */
525 time_t mtime;
526 zip_uint64_t idx;
527 idx = strtoull(argv[0], NULL, 10);
528 mtime = (time_t)strtoull(argv[1], NULL, 10);
529 if (zip_file_set_mtime(za, idx, mtime, 0) < 0) {
530 fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%lld': %s\n", idx, (long long)mtime, zip_strerror(za));
531 return -1;
532 }
533 return 0;
534 }
535
536 static int
set_file_mtime_all(int argc,char * argv[])537 set_file_mtime_all(int argc, char *argv[]) {
538 /* set last modification time (mtime) for all files */
539 time_t mtime;
540 zip_int64_t num_entries;
541 zip_uint64_t idx;
542 mtime = (time_t)strtoull(argv[0], NULL, 10);
543
544 if ((num_entries = zip_get_num_entries(za, 0)) < 0) {
545 fprintf(stderr, "can't get number of entries: %s\n", zip_strerror(za));
546 return -1;
547 }
548 for (idx = 0; idx < (zip_uint64_t)num_entries; idx++) {
549 if (zip_file_set_mtime(za, idx, mtime, 0) < 0) {
550 fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%lld': %s\n", idx, (long long)mtime, zip_strerror(za));
551 return -1;
552 }
553 }
554 return 0;
555 }
556
557 static int
set_password(int argc,char * argv[])558 set_password(int argc, char *argv[]) {
559 /* set default password */
560 if (zip_set_default_password(za, argv[0]) < 0) {
561 fprintf(stderr, "can't set default password to '%s'\n", argv[0]);
562 return -1;
563 }
564 return 0;
565 }
566
567 static int
zstat(int argc,char * argv[])568 zstat(int argc, char *argv[]) {
569 zip_uint64_t idx;
570 char buf[100];
571 struct zip_stat sb;
572 idx = strtoull(argv[0], NULL, 10);
573
574 if (zip_stat_index(za, idx, stat_flags, &sb) < 0) {
575 fprintf(stderr, "zip_stat_index failed on '%" PRIu64 "' failed: %s\n", idx, zip_strerror(za));
576 return -1;
577 }
578
579 if (sb.valid & ZIP_STAT_NAME)
580 printf("name: '%s'\n", sb.name);
581 if (sb.valid & ZIP_STAT_INDEX)
582 printf("index: '%" PRIu64 "'\n", sb.index);
583 if (sb.valid & ZIP_STAT_SIZE)
584 printf("size: '%" PRIu64 "'\n", sb.size);
585 if (sb.valid & ZIP_STAT_COMP_SIZE)
586 printf("compressed size: '%" PRIu64 "'\n", sb.comp_size);
587 if (sb.valid & ZIP_STAT_MTIME) {
588 struct tm *tpm;
589 #ifdef HAVE_LOCALTIME_R
590 struct tm tm;
591 tpm = localtime_r(&sb.mtime, &tm);
592 #else
593 tpm = localtime(&sb.mtime);
594 #endif
595 if (tpm == NULL) {
596 printf("mtime: <not valid>\n");
597 } else {
598 strftime(buf, sizeof(buf), "%a %b %d %Y %H:%M:%S", tpm);
599 printf("mtime: '%s'\n", buf);
600 }
601 }
602 if (sb.valid & ZIP_STAT_CRC)
603 printf("crc: '%0x'\n", sb.crc);
604 if (sb.valid & ZIP_STAT_COMP_METHOD)
605 printf("compression method: '%d'\n", sb.comp_method);
606 if (sb.valid & ZIP_STAT_ENCRYPTION_METHOD)
607 printf("encryption method: '%d'\n", sb.encryption_method);
608 if (sb.valid & ZIP_STAT_FLAGS)
609 printf("flags: '%ld'\n", (long)sb.flags);
610 printf("\n");
611
612 return 0;
613 }
614
615 static zip_flags_t
get_flags(const char * arg)616 get_flags(const char *arg) {
617 zip_flags_t flags = 0;
618 if (strchr(arg, 'C') != NULL)
619 flags |= ZIP_FL_NOCASE;
620 if (strchr(arg, 'c') != NULL)
621 flags |= ZIP_FL_CENTRAL;
622 if (strchr(arg, 'd') != NULL)
623 flags |= ZIP_FL_NODIR;
624 if (strchr(arg, 'l') != NULL)
625 flags |= ZIP_FL_LOCAL;
626 if (strchr(arg, 'u') != NULL)
627 flags |= ZIP_FL_UNCHANGED;
628 return flags;
629 }
630
631 static zip_int32_t
get_compression_method(const char * arg)632 get_compression_method(const char *arg) {
633 if (strcasecmp(arg, "default") == 0)
634 return ZIP_CM_DEFAULT;
635 else if (strcasecmp(arg, "store") == 0)
636 return ZIP_CM_STORE;
637 else if (strcasecmp(arg, "deflate") == 0)
638 return ZIP_CM_DEFLATE;
639 #if defined(HAVE_LIBBZ2)
640 else if (strcasecmp(arg, "bzip2") == 0)
641 return ZIP_CM_BZIP2;
642 #endif
643 #if defined(HAVE_LIBLZMA)
644 /* Disabled - because 7z isn't able to unpack ZIP+LZMA ZIP+LZMA2
645 archives made this way - and vice versa.
646
647 else if (strcasecmp(arg, "lzma") == 0)
648 return ZIP_CM_LZMA;
649 else if (strcasecmp(arg, "lzma2") == 0)
650 return ZIP_CM_LZMA2;
651 */
652 else if (strcasecmp(arg, "xz") == 0)
653 return ZIP_CM_XZ;
654
655 #endif
656 else if (strcasecmp(arg, "unknown") == 0)
657 return 100;
658 return 0; /* TODO: error handling */
659 }
660
661 static zip_uint16_t
get_encryption_method(const char * arg)662 get_encryption_method(const char *arg) {
663 if (strcasecmp(arg, "none") == 0)
664 return ZIP_EM_NONE;
665 else if (strcasecmp(arg, "PKWARE") == 0)
666 return ZIP_EM_TRAD_PKWARE;
667 else if (strcasecmp(arg, "AES-128") == 0)
668 return ZIP_EM_AES_128;
669 else if (strcasecmp(arg, "AES-192") == 0)
670 return ZIP_EM_AES_192;
671 else if (strcasecmp(arg, "AES-256") == 0)
672 return ZIP_EM_AES_256;
673 else if (strcasecmp(arg, "unknown") == 0)
674 return 100;
675 return (zip_uint16_t)-1; /* TODO: error handling */
676 }
677
678 static void
hexdump(const zip_uint8_t * data,zip_uint16_t len)679 hexdump(const zip_uint8_t *data, zip_uint16_t len) {
680 zip_uint16_t i;
681
682 if (len <= 0)
683 return;
684
685 printf("0x");
686
687 for (i = 0; i < len; i++)
688 printf("%02x", data[i]);
689
690 return;
691 }
692
693
694 static zip_t *
read_from_file(const char * archive,int flags,zip_error_t * error,zip_uint64_t offset,zip_uint64_t length)695 read_from_file(const char *archive, int flags, zip_error_t *error, zip_uint64_t offset, zip_uint64_t length) {
696 zip_t *zaa;
697 zip_source_t *source;
698 int err;
699
700 if (offset == 0 && length == 0) {
701 if (strcmp(archive, "/dev/stdin") == 0) {
702 zaa = zip_fdopen(STDIN_FILENO, flags & ~ZIP_CREATE, &err);
703 }
704 else {
705 zaa = zip_open(archive, flags, &err);
706 }
707 if (zaa == NULL) {
708 zip_error_set(error, err, errno);
709 return NULL;
710 }
711 }
712 else {
713 if (length > ZIP_INT64_MAX) {
714 zip_error_set(error, ZIP_ER_INVAL, 0);
715 return NULL;
716 }
717 if ((source = zip_source_file_create(archive, offset, (zip_int64_t)length, error)) == NULL || (zaa = zip_open_from_source(source, flags, error)) == NULL) {
718 zip_source_free(source);
719 return NULL;
720 }
721 }
722
723 return zaa;
724 }
725
726 dispatch_table_t dispatch_table[] = {{"add", 2, "name content", "add file called name using content", add},
727 {"add_dir", 1, "name", "add directory", add_dir},
728 {"add_file", 4, "name file_to_add offset len", "add file to archive, len bytes starting from offset", add_file},
729 {"add_from_zip", 5, "name archivename index offset len", "add file from another archive, len bytes starting from offset", add_from_zip},
730 {"cat", 1, "index", "output file contents to stdout", cat},
731 {"count_extra", 2, "index flags", "show number of extra fields for archive entry", count_extra},
732 {"count_extra_by_id", 3, "index extra_id flags", "show number of extra fields of type extra_id for archive entry", count_extra_by_id},
733 {"delete", 1, "index", "remove entry", delete},
734 {"delete_extra", 3, "index extra_idx flags", "remove extra field", delete_extra},
735 {"delete_extra_by_id", 4, "index extra_id extra_index flags", "remove extra field of type extra_id", delete_extra_by_id},
736 {"get_archive_comment", 0, "", "show archive comment", get_archive_comment},
737 {"get_extra", 3, "index extra_index flags", "show extra field", get_extra},
738 {"get_extra_by_id", 4, "index extra_id extra_index flags", "show extra field of type extra_id", get_extra_by_id},
739 {"get_file_comment", 1, "index", "get file comment", get_file_comment},
740 {"get_num_entries", 1, "flags", "get number of entries in archive", get_num_entries},
741 {"name_locate", 2, "name flags", "find entry in archive", name_locate},
742 {"print_progress", 0, "", "print progress during zip_close()", print_progress},
743 {"rename", 2, "index name", "rename entry", zrename},
744 {"replace_file_contents", 2, "index data", "replace entry with data", replace_file_contents},
745 {"set_archive_comment", 1, "comment", "set archive comment", set_archive_comment},
746 {"set_extra", 5, "index extra_id extra_index flags value", "set extra field", set_extra},
747 {"set_file_comment", 2, "index comment", "set file comment", set_file_comment},
748 {"set_file_compression", 3, "index method compression_flags", "set file compression method", set_file_compression},
749 {"set_file_dostime", 3, "index time date", "set file modification time and date (DOS format)", set_file_dostime},
750 {"set_file_encryption", 3, "index method password", "set file encryption method", set_file_encryption},
751 {"set_file_mtime", 2, "index timestamp", "set file modification time", set_file_mtime},
752 {"set_file_mtime_all", 1, "timestamp", "set file modification time for all files", set_file_mtime_all},
753 {"set_password", 1, "password", "set default password for encryption", set_password},
754 {"stat", 1, "index", "print information about entry", zstat}
755 #ifdef DISPATCH_REGRESS
756 ,
757 DISPATCH_REGRESS
758 #endif
759 };
760
761 static int
dispatch(int argc,char * argv[])762 dispatch(int argc, char *argv[]) {
763 unsigned int i;
764 for (i = 0; i < sizeof(dispatch_table) / sizeof(dispatch_table_t); i++) {
765 if (strcmp(dispatch_table[i].cmdline_name, argv[0]) == 0) {
766 argc--;
767 argv++;
768 /* 1 for the command, argument_count for the arguments */
769 if (argc < dispatch_table[i].argument_count) {
770 fprintf(stderr, "not enough arguments for command '%s': %d available, %d needed\n", dispatch_table[i].cmdline_name, argc, dispatch_table[i].argument_count);
771 return -1;
772 }
773 if (dispatch_table[i].function(argc, argv) == 0)
774 return 1 + dispatch_table[i].argument_count;
775 return -1;
776 }
777 }
778
779 fprintf(stderr, "unknown command '%s'\n", argv[0]);
780 return -1;
781 }
782
783
784 static void
usage(const char * progname,const char * reason)785 usage(const char *progname, const char *reason) {
786 unsigned int i;
787 FILE *out;
788 if (reason == NULL)
789 out = stdout;
790 else
791 out = stderr;
792 fprintf(out, "usage: %s [-ceghnrst]" USAGE_REGRESS " [-l len] [-o offset] archive command1 [args] [command2 [args] ...]\n", progname);
793 if (reason != NULL) {
794 fprintf(out, "%s\n", reason);
795 exit(1);
796 }
797
798 fprintf(out, "\nSupported options are:\n"
799 "\t-c\t\tcheck consistency\n"
800 "\t-e\t\terror if archive already exists (only useful with -n)\n"
801 #ifdef FOR_REGRESS
802 "\t-F size\t\tfragment size for in memory archive\n"
803 #endif
804 "\t-g\t\tguess file name encoding (for stat)\n"
805 #ifdef FOR_REGRESS
806 "\t-H\t\twrite files with holes compactly\n"
807 #endif
808 "\t-h\t\tdisplay this usage\n"
809 "\t-l len\t\tonly use len bytes of file\n"
810 #ifdef FOR_REGRESS
811 "\t-m\t\tread archive into memory, and modify there; write out at end\n"
812 #endif
813 "\t-n\t\tcreate archive if it doesn't exist\n"
814 "\t-o offset\tstart reading file at offset\n"
815 "\t-r\t\tprint raw file name encoding without translation (for stat)\n"
816 "\t-s\t\tfollow file name convention strictly (for stat)\n"
817 "\t-t\t\tdisregard current archive contents, if any\n");
818 fprintf(out, "\nSupported commands and arguments are:\n");
819 for (i = 0; i < sizeof(dispatch_table) / sizeof(dispatch_table_t); i++) {
820 fprintf(out, "\t%s %s\n\t %s\n\n", dispatch_table[i].cmdline_name, dispatch_table[i].arg_names, dispatch_table[i].description);
821 }
822 fprintf(out, "\nSupported flags are:\n"
823 "\t0\t(no flags)\n"
824 "\tC\tZIP_FL_NOCASE\n"
825 "\tc\tZIP_FL_CENTRAL\n"
826 "\td\tZIP_FL_NODIR\n"
827 "\tl\tZIP_FL_LOCAL\n"
828 "\tu\tZIP_FL_UNCHANGED\n");
829 fprintf(out, "\nSupported compression methods are:\n"
830 "\tdefault\n");
831 if (zip_compression_method_supported(ZIP_CM_BZIP2, 1)) {
832 fprintf(out, "\tbzip2\n");
833 }
834 fprintf(out, "\tdeflate\n"
835 "\tstore\n");
836 if (zip_compression_method_supported(ZIP_CM_XZ, 1)) {
837 fprintf(out, "\txz\n");
838 }
839 fprintf(out, "\nSupported encryption methods are:\n"
840 "\tnone\n");
841 if (zip_encryption_method_supported(ZIP_EM_AES_128, 1)) {
842 fprintf(out, "\tAES-128\n");
843 }
844 if (zip_encryption_method_supported(ZIP_EM_AES_192, 1)) {
845 fprintf(out, "\tAES-192\n");
846 }
847 if (zip_encryption_method_supported(ZIP_EM_AES_256, 1)) {
848 fprintf(out, "\tAES-256\n");
849 }
850 fprintf(out, "\tPKWARE\n");
851 fprintf(out, "\nThe index is zero-based.\n");
852 exit(0);
853 }
854
855 #ifndef FOR_REGRESS
856 #define ziptool_open read_from_file
857 int
ziptool_post_close(const char * archive)858 ziptool_post_close(const char *archive) {
859 return 0;
860 }
861 #endif
862
863 int
main(int argc,char * argv[])864 main(int argc, char *argv[]) {
865 const char *archive;
866 unsigned int i;
867 int c, arg, err, flags;
868 const char *prg;
869 zip_uint64_t len = 0, offset = 0;
870 zip_error_t error;
871
872 flags = 0;
873 prg = argv[0];
874
875 while ((c = getopt(argc, argv, "ceghl:no:rst" OPTIONS_REGRESS)) != -1) {
876 switch (c) {
877 case 'c':
878 flags |= ZIP_CHECKCONS;
879 break;
880 case 'e':
881 flags |= ZIP_EXCL;
882 break;
883 case 'g':
884 stat_flags = ZIP_FL_ENC_GUESS;
885 break;
886 case 'h':
887 usage(prg, NULL);
888 break;
889 case 'l':
890 len = strtoull(optarg, NULL, 10);
891 break;
892 case 'n':
893 flags |= ZIP_CREATE;
894 break;
895 case 'o':
896 offset = strtoull(optarg, NULL, 10);
897 break;
898 case 'r':
899 stat_flags = ZIP_FL_ENC_RAW;
900 break;
901 case 's':
902 stat_flags = ZIP_FL_ENC_STRICT;
903 break;
904 case 't':
905 flags |= ZIP_TRUNCATE;
906 break;
907 #ifdef GETOPT_REGRESS
908 GETOPT_REGRESS
909 #endif
910
911 default: {
912 char reason[128];
913 snprintf(reason, sizeof(reason), "invalid option -%c", optopt);
914 usage(prg, reason);
915 }
916 }
917 }
918
919 if (optind >= argc - 1)
920 usage(prg, "too few arguments");
921
922 arg = optind;
923
924 archive = argv[arg++];
925
926 if (flags == 0)
927 flags = ZIP_CREATE;
928
929 zip_error_init(&error);
930 za = ziptool_open(archive, flags, &error, offset, len);
931 if (za == NULL) {
932 fprintf(stderr, "can't open zip archive '%s': %s\n", archive, zip_error_strerror(&error));
933 zip_error_fini(&error);
934 return 1;
935 }
936 zip_error_fini(&error);
937
938 err = 0;
939 while (arg < argc) {
940 int ret;
941 ret = dispatch(argc - arg, argv + arg);
942 if (ret > 0) {
943 arg += ret;
944 }
945 else {
946 err = 1;
947 break;
948 }
949 }
950
951 if (zip_close(za) == -1) {
952 fprintf(stderr, "can't close zip archive '%s': %s\n", archive, zip_strerror(za));
953 return 1;
954 }
955 if (ziptool_post_close(archive) < 0) {
956 err = 1;
957 }
958
959 for (i = 0; i < z_in_count; i++) {
960 if (zip_close(z_in[i]) < 0) {
961 err = 1;
962 }
963 }
964
965 return err;
966 }
967