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