|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 /* |
|
6 * JARFILE |
|
7 * |
|
8 * Parsing of a Jar file |
|
9 */ |
|
10 #define JAR_SIZE 256 |
|
11 |
|
12 #include "jar.h" |
|
13 #include "jarint.h" |
|
14 #include "jarfile.h" |
|
15 |
|
16 /* commercial compression */ |
|
17 #include "jzlib.h" |
|
18 |
|
19 #if defined(XP_UNIX) || defined(XP_BEOS) |
|
20 #include "sys/stat.h" |
|
21 #endif |
|
22 |
|
23 #include "sechash.h" /* for HASH_GetHashObject() */ |
|
24 |
|
25 PR_STATIC_ASSERT(46 == sizeof(struct ZipCentral)); |
|
26 PR_STATIC_ASSERT(30 == sizeof(struct ZipLocal)); |
|
27 PR_STATIC_ASSERT(22 == sizeof(struct ZipEnd)); |
|
28 PR_STATIC_ASSERT(512 == sizeof(union TarEntry)); |
|
29 |
|
30 /* extracting */ |
|
31 static int |
|
32 jar_guess_jar(const char *filename, JAR_FILE fp); |
|
33 |
|
34 static int |
|
35 jar_inflate_memory(unsigned int method, long *length, long expected_out_len, |
|
36 char **data); |
|
37 |
|
38 static int |
|
39 jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length); |
|
40 |
|
41 static int |
|
42 jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, |
|
43 unsigned int method); |
|
44 |
|
45 static int |
|
46 jar_verify_extract(JAR *jar, char *path, char *physical_path); |
|
47 |
|
48 static JAR_Physical * |
|
49 jar_get_physical(JAR *jar, char *pathname); |
|
50 |
|
51 static int |
|
52 jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp); |
|
53 |
|
54 static int |
|
55 jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext); |
|
56 |
|
57 |
|
58 /* indexing */ |
|
59 static int |
|
60 jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp); |
|
61 |
|
62 static int |
|
63 jar_listtar(JAR *jar, JAR_FILE fp); |
|
64 |
|
65 static int |
|
66 jar_listzip(JAR *jar, JAR_FILE fp); |
|
67 |
|
68 |
|
69 /* conversions */ |
|
70 static int |
|
71 dosdate(char *date, const char *s); |
|
72 |
|
73 static int |
|
74 dostime(char *time, const char *s); |
|
75 |
|
76 #ifdef NSS_X86_OR_X64 |
|
77 #define x86ShortToUint32(ii) ((const PRUint32)*((const PRUint16 *)(ii))) |
|
78 #define x86LongToUint32(ii) (*(const PRUint32 *)(ii)) |
|
79 #else |
|
80 static PRUint32 |
|
81 x86ShortToUint32(const void *ii); |
|
82 |
|
83 static PRUint32 |
|
84 x86LongToUint32(const void *ll); |
|
85 #endif |
|
86 |
|
87 static long |
|
88 octalToLong(const char *s); |
|
89 |
|
90 /* |
|
91 * J A R _ p a s s _ a r c h i v e |
|
92 * |
|
93 * For use by naive clients. Slam an entire archive file |
|
94 * into this function. We extract manifests, parse, index |
|
95 * the archive file, and do whatever nastiness. |
|
96 * |
|
97 */ |
|
98 int |
|
99 JAR_pass_archive(JAR *jar, jarArch format, char *filename, const char *url) |
|
100 { |
|
101 JAR_FILE fp; |
|
102 int status = 0; |
|
103 |
|
104 if (filename == NULL) |
|
105 return JAR_ERR_GENERAL; |
|
106 |
|
107 if ((fp = JAR_FOPEN (filename, "rb")) != NULL) { |
|
108 if (format == jarArchGuess) |
|
109 format = (jarArch)jar_guess_jar (filename, fp); |
|
110 |
|
111 jar->format = format; |
|
112 jar->url = url ? PORT_Strdup (url) : NULL; |
|
113 jar->filename = PORT_Strdup (filename); |
|
114 |
|
115 status = jar_gen_index (jar, format, fp); |
|
116 if (status == 0) |
|
117 status = jar_extract_manifests (jar, format, fp); |
|
118 |
|
119 JAR_FCLOSE (fp); |
|
120 if (status < 0) |
|
121 return status; |
|
122 |
|
123 /* people were expecting it this way */ |
|
124 return jar->valid; |
|
125 } |
|
126 /* file not found */ |
|
127 return JAR_ERR_FNF; |
|
128 } |
|
129 |
|
130 /* |
|
131 * J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d |
|
132 * |
|
133 * Same as JAR_pass_archive, but doesn't parse signatures. |
|
134 * |
|
135 */ |
|
136 int |
|
137 JAR_pass_archive_unverified(JAR *jar, jarArch format, char *filename, |
|
138 const char *url) |
|
139 { |
|
140 JAR_FILE fp; |
|
141 int status = 0; |
|
142 |
|
143 if (filename == NULL) { |
|
144 return JAR_ERR_GENERAL; |
|
145 } |
|
146 |
|
147 if ((fp = JAR_FOPEN (filename, "rb")) != NULL) { |
|
148 if (format == jarArchGuess) { |
|
149 format = (jarArch)jar_guess_jar (filename, fp); |
|
150 } |
|
151 |
|
152 jar->format = format; |
|
153 jar->url = url ? PORT_Strdup (url) : NULL; |
|
154 jar->filename = PORT_Strdup (filename); |
|
155 |
|
156 status = jar_gen_index (jar, format, fp); |
|
157 if (status == 0) { |
|
158 status = jar_extract_mf(jar, format, fp, "mf"); |
|
159 } |
|
160 |
|
161 JAR_FCLOSE (fp); |
|
162 if (status < 0) { |
|
163 return status; |
|
164 } |
|
165 |
|
166 /* people were expecting it this way */ |
|
167 return jar->valid; |
|
168 } |
|
169 /* file not found */ |
|
170 return JAR_ERR_FNF; |
|
171 } |
|
172 |
|
173 /* |
|
174 * J A R _ v e r i f i e d _ e x t r a c t |
|
175 * |
|
176 * Optimization: keep a file descriptor open |
|
177 * inside the JAR structure, so we don't have to |
|
178 * open the file 25 times to run java. |
|
179 * |
|
180 */ |
|
181 |
|
182 int |
|
183 JAR_verified_extract(JAR *jar, char *path, char *outpath) |
|
184 { |
|
185 int status = JAR_extract (jar, path, outpath); |
|
186 |
|
187 if (status >= 0) |
|
188 return jar_verify_extract(jar, path, outpath); |
|
189 return status; |
|
190 } |
|
191 |
|
192 int |
|
193 JAR_extract(JAR *jar, char *path, char *outpath) |
|
194 { |
|
195 int result; |
|
196 JAR_Physical *phy; |
|
197 |
|
198 if (jar->fp == NULL && jar->filename) { |
|
199 jar->fp = (FILE*)JAR_FOPEN (jar->filename, "rb"); |
|
200 } |
|
201 if (jar->fp == NULL) { |
|
202 /* file not found */ |
|
203 return JAR_ERR_FNF; |
|
204 } |
|
205 |
|
206 phy = jar_get_physical (jar, path); |
|
207 if (phy) { |
|
208 if (phy->compression != 0 && phy->compression != 8) { |
|
209 /* unsupported compression method */ |
|
210 result = JAR_ERR_CORRUPT; |
|
211 } |
|
212 if (phy->compression == 0) { |
|
213 result = jar_physical_extraction |
|
214 ((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length); |
|
215 } else { |
|
216 result = jar_physical_inflate((PRFileDesc*)jar->fp, outpath, |
|
217 phy->offset, phy->length, |
|
218 (unsigned int) phy->compression); |
|
219 } |
|
220 |
|
221 #if defined(XP_UNIX) || defined(XP_BEOS) |
|
222 if (phy->mode) |
|
223 chmod (outpath, 0400 | (mode_t) phy->mode); |
|
224 #endif |
|
225 } else { |
|
226 /* pathname not found in archive */ |
|
227 result = JAR_ERR_PNF; |
|
228 } |
|
229 return result; |
|
230 } |
|
231 |
|
232 /* |
|
233 * p h y s i c a l _ e x t r a c t i o n |
|
234 * |
|
235 * This needs to be done in chunks of say 32k, instead of |
|
236 * in one bulk calloc. (Necessary under Win16 platform.) |
|
237 * This is done for uncompressed entries only. |
|
238 * |
|
239 */ |
|
240 |
|
241 #define CHUNK 32768 |
|
242 |
|
243 static int |
|
244 jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length) |
|
245 { |
|
246 JAR_FILE out; |
|
247 char *buffer = (char *)PORT_ZAlloc(CHUNK); |
|
248 int status = 0; |
|
249 |
|
250 if (buffer == NULL) |
|
251 return JAR_ERR_MEMORY; |
|
252 |
|
253 if ((out = JAR_FOPEN (outpath, "wb")) != NULL) { |
|
254 long at = 0; |
|
255 |
|
256 JAR_FSEEK (fp, offset, (PRSeekWhence)0); |
|
257 while (at < length) { |
|
258 long chunk = (at + CHUNK <= length) ? CHUNK : length - at; |
|
259 if (JAR_FREAD (fp, buffer, chunk) != chunk) { |
|
260 status = JAR_ERR_DISK; |
|
261 break; |
|
262 } |
|
263 at += chunk; |
|
264 if (JAR_FWRITE (out, buffer, chunk) < chunk) { |
|
265 /* most likely a disk full error */ |
|
266 status = JAR_ERR_DISK; |
|
267 break; |
|
268 } |
|
269 } |
|
270 JAR_FCLOSE (out); |
|
271 } else { |
|
272 /* error opening output file */ |
|
273 status = JAR_ERR_DISK; |
|
274 } |
|
275 PORT_Free (buffer); |
|
276 return status; |
|
277 } |
|
278 |
|
279 /* |
|
280 * j a r _ p h y s i c a l _ i n f l a t e |
|
281 * |
|
282 * Inflate a range of bytes in a file, writing the inflated |
|
283 * result to "outpath". Chunk based. |
|
284 * |
|
285 */ |
|
286 /* input and output chunks differ, assume 4x compression */ |
|
287 |
|
288 #define ICHUNK 8192 |
|
289 #define OCHUNK 32768 |
|
290 |
|
291 static int |
|
292 jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, |
|
293 unsigned int method) |
|
294 { |
|
295 char *inbuf, *outbuf; |
|
296 int status = 0; |
|
297 z_stream zs; |
|
298 JAR_FILE out; |
|
299 |
|
300 /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ |
|
301 if ((inbuf = (char *)PORT_ZAlloc(ICHUNK + 1)) == NULL) |
|
302 return JAR_ERR_MEMORY; |
|
303 |
|
304 if ((outbuf = (char *)PORT_ZAlloc(OCHUNK)) == NULL) { |
|
305 PORT_Free (inbuf); |
|
306 return JAR_ERR_MEMORY; |
|
307 } |
|
308 |
|
309 PORT_Memset (&zs, 0, sizeof (zs)); |
|
310 status = inflateInit2 (&zs, -MAX_WBITS); |
|
311 if (status != Z_OK) { |
|
312 PORT_Free (inbuf); |
|
313 PORT_Free (outbuf); |
|
314 return JAR_ERR_GENERAL; |
|
315 } |
|
316 |
|
317 if ((out = JAR_FOPEN (outpath, "wb")) != NULL) { |
|
318 long at = 0; |
|
319 |
|
320 JAR_FSEEK (fp, offset, (PRSeekWhence)0); |
|
321 while (at < length) { |
|
322 long chunk = (at + ICHUNK <= length) ? ICHUNK : length - at; |
|
323 unsigned long tin; |
|
324 |
|
325 if (JAR_FREAD (fp, inbuf, chunk) != chunk) { |
|
326 /* incomplete read */ |
|
327 JAR_FCLOSE (out); |
|
328 PORT_Free (inbuf); |
|
329 PORT_Free (outbuf); |
|
330 return JAR_ERR_CORRUPT; |
|
331 } |
|
332 at += chunk; |
|
333 if (at == length) { |
|
334 /* add an extra dummy byte at the end */ |
|
335 inbuf[chunk++] = 0xDD; |
|
336 } |
|
337 zs.next_in = (Bytef *) inbuf; |
|
338 zs.avail_in = chunk; |
|
339 zs.avail_out = OCHUNK; |
|
340 tin = zs.total_in; |
|
341 while ((zs.total_in - tin < chunk) || (zs.avail_out == 0)) { |
|
342 unsigned long prev_total = zs.total_out; |
|
343 unsigned long ochunk; |
|
344 |
|
345 zs.next_out = (Bytef *) outbuf; |
|
346 zs.avail_out = OCHUNK; |
|
347 status = inflate (&zs, Z_NO_FLUSH); |
|
348 if (status != Z_OK && status != Z_STREAM_END) { |
|
349 /* error during decompression */ |
|
350 JAR_FCLOSE (out); |
|
351 PORT_Free (inbuf); |
|
352 PORT_Free (outbuf); |
|
353 return JAR_ERR_CORRUPT; |
|
354 } |
|
355 ochunk = zs.total_out - prev_total; |
|
356 if (JAR_FWRITE (out, outbuf, ochunk) < ochunk) { |
|
357 /* most likely a disk full error */ |
|
358 status = JAR_ERR_DISK; |
|
359 break; |
|
360 } |
|
361 if (status == Z_STREAM_END) |
|
362 break; |
|
363 } |
|
364 } |
|
365 JAR_FCLOSE (out); |
|
366 status = inflateEnd (&zs); |
|
367 } else { |
|
368 /* error opening output file */ |
|
369 status = JAR_ERR_DISK; |
|
370 } |
|
371 PORT_Free (inbuf); |
|
372 PORT_Free (outbuf); |
|
373 return status; |
|
374 } |
|
375 |
|
376 /* |
|
377 * j a r _ i n f l a t e _ m e m o r y |
|
378 * |
|
379 * Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd, |
|
380 * and thus appears to operate inplace to the caller. |
|
381 * |
|
382 */ |
|
383 static int |
|
384 jar_inflate_memory(unsigned int method, long *length, long expected_out_len, |
|
385 char **data) |
|
386 { |
|
387 char *inbuf = *data; |
|
388 char *outbuf = (char*)PORT_ZAlloc(expected_out_len); |
|
389 long insz = *length; |
|
390 int status; |
|
391 z_stream zs; |
|
392 |
|
393 if (outbuf == NULL) |
|
394 return JAR_ERR_MEMORY; |
|
395 |
|
396 PORT_Memset(&zs, 0, sizeof zs); |
|
397 status = inflateInit2 (&zs, -MAX_WBITS); |
|
398 if (status < 0) { |
|
399 /* error initializing zlib stream */ |
|
400 PORT_Free (outbuf); |
|
401 return JAR_ERR_GENERAL; |
|
402 } |
|
403 |
|
404 zs.next_in = (Bytef *) inbuf; |
|
405 zs.next_out = (Bytef *) outbuf; |
|
406 zs.avail_in = insz; |
|
407 zs.avail_out = expected_out_len; |
|
408 |
|
409 status = inflate (&zs, Z_FINISH); |
|
410 if (status != Z_OK && status != Z_STREAM_END) { |
|
411 /* error during deflation */ |
|
412 PORT_Free (outbuf); |
|
413 return JAR_ERR_GENERAL; |
|
414 } |
|
415 |
|
416 status = inflateEnd (&zs); |
|
417 if (status != Z_OK) { |
|
418 /* error during deflation */ |
|
419 PORT_Free (outbuf); |
|
420 return JAR_ERR_GENERAL; |
|
421 } |
|
422 PORT_Free(*data); |
|
423 *data = outbuf; |
|
424 *length = zs.total_out; |
|
425 return 0; |
|
426 } |
|
427 |
|
428 /* |
|
429 * v e r i f y _ e x t r a c t |
|
430 * |
|
431 * Validate signature on the freshly extracted file. |
|
432 * |
|
433 */ |
|
434 static int |
|
435 jar_verify_extract(JAR *jar, char *path, char *physical_path) |
|
436 { |
|
437 int status; |
|
438 JAR_Digest dig; |
|
439 |
|
440 PORT_Memset (&dig, 0, sizeof dig); |
|
441 status = JAR_digest_file (physical_path, &dig); |
|
442 if (!status) |
|
443 status = JAR_verify_digest (jar, path, &dig); |
|
444 return status; |
|
445 } |
|
446 |
|
447 /* |
|
448 * g e t _ p h y s i c a l |
|
449 * |
|
450 * Let's get physical. |
|
451 * Obtains the offset and length of this file in the jar file. |
|
452 * |
|
453 */ |
|
454 static JAR_Physical * |
|
455 jar_get_physical(JAR *jar, char *pathname) |
|
456 { |
|
457 ZZLink *link; |
|
458 ZZList *list = jar->phy; |
|
459 |
|
460 if (ZZ_ListEmpty (list)) |
|
461 return NULL; |
|
462 |
|
463 for (link = ZZ_ListHead (list); |
|
464 !ZZ_ListIterDone (list, link); |
|
465 link = link->next) { |
|
466 JAR_Item *it = link->thing; |
|
467 |
|
468 if (it->type == jarTypePhy && |
|
469 it->pathname && !PORT_Strcmp (it->pathname, pathname)) { |
|
470 JAR_Physical *phy = (JAR_Physical *)it->data; |
|
471 return phy; |
|
472 } |
|
473 } |
|
474 return NULL; |
|
475 } |
|
476 |
|
477 /* |
|
478 * j a r _ e x t r a c t _ m a n i f e s t s |
|
479 * |
|
480 * Extract the manifest files and parse them, |
|
481 * from an open archive file whose contents are known. |
|
482 * |
|
483 */ |
|
484 static int |
|
485 jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp) |
|
486 { |
|
487 int status, signatures; |
|
488 |
|
489 if (format != jarArchZip && format != jarArchTar) |
|
490 return JAR_ERR_CORRUPT; |
|
491 |
|
492 if ((status = jar_extract_mf (jar, format, fp, "mf")) < 0) |
|
493 return status; |
|
494 if (!status) |
|
495 return JAR_ERR_ORDER; |
|
496 if ((status = jar_extract_mf (jar, format, fp, "sf")) < 0) |
|
497 return status; |
|
498 if (!status) |
|
499 return JAR_ERR_ORDER; |
|
500 if ((status = jar_extract_mf (jar, format, fp, "rsa")) < 0) |
|
501 return status; |
|
502 signatures = status; |
|
503 if ((status = jar_extract_mf (jar, format, fp, "dsa")) < 0) |
|
504 return status; |
|
505 if (!(signatures += status)) |
|
506 return JAR_ERR_SIG; |
|
507 return 0; |
|
508 } |
|
509 |
|
510 /* |
|
511 * j a r _ e x t r a c t _ m f |
|
512 * |
|
513 * Extracts manifest files based on an extension, which |
|
514 * should be .MF, .SF, .RSA, etc. Order of the files is now no |
|
515 * longer important when zipping jar files. |
|
516 * |
|
517 */ |
|
518 static int |
|
519 jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext) |
|
520 { |
|
521 ZZLink *link; |
|
522 ZZList *list = jar->phy; |
|
523 int ret = 0; |
|
524 |
|
525 if (ZZ_ListEmpty (list)) |
|
526 return JAR_ERR_PNF; |
|
527 |
|
528 for (link = ZZ_ListHead (list); |
|
529 ret >= 0 && !ZZ_ListIterDone (list, link); |
|
530 link = link->next) { |
|
531 JAR_Item *it = link->thing; |
|
532 |
|
533 if (it->type == jarTypePhy && |
|
534 !PORT_Strncmp (it->pathname, "META-INF", 8)) |
|
535 { |
|
536 JAR_Physical *phy = (JAR_Physical *) it->data; |
|
537 char *fn = it->pathname + 8; |
|
538 char *e; |
|
539 char *manifest; |
|
540 long length; |
|
541 int num, status; |
|
542 |
|
543 if (PORT_Strlen (it->pathname) < 8) |
|
544 continue; |
|
545 |
|
546 if (*fn == '/' || *fn == '\\') |
|
547 fn++; |
|
548 if (*fn == 0) { |
|
549 /* just a directory entry */ |
|
550 continue; |
|
551 } |
|
552 |
|
553 /* skip to extension */ |
|
554 for (e = fn; *e && *e != '.'; e++) |
|
555 /* yip */ ; |
|
556 |
|
557 /* and skip dot */ |
|
558 if (*e == '.') |
|
559 e++; |
|
560 if (PORT_Strcasecmp (ext, e)) { |
|
561 /* not the right extension */ |
|
562 continue; |
|
563 } |
|
564 if (phy->length == 0 || phy->length > 0xFFFF) { |
|
565 /* manifest files cannot be zero length or too big! */ |
|
566 /* the 0xFFFF limit is per J2SE SDK */ |
|
567 return JAR_ERR_CORRUPT; |
|
568 } |
|
569 |
|
570 /* Read in the manifest and parse it */ |
|
571 /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ |
|
572 manifest = (char *)PORT_ZAlloc(phy->length + 1); |
|
573 if (!manifest) |
|
574 return JAR_ERR_MEMORY; |
|
575 |
|
576 JAR_FSEEK (fp, phy->offset, (PRSeekWhence)0); |
|
577 num = JAR_FREAD (fp, manifest, phy->length); |
|
578 if (num != phy->length) { |
|
579 /* corrupt archive file */ |
|
580 PORT_Free (manifest); |
|
581 return JAR_ERR_CORRUPT; |
|
582 } |
|
583 |
|
584 if (phy->compression == 8) { |
|
585 length = phy->length; |
|
586 /* add an extra dummy byte at the end */ |
|
587 manifest[length++] = 0xDD; |
|
588 status = jar_inflate_memory((unsigned int)phy->compression, |
|
589 &length, |
|
590 phy->uncompressed_length, |
|
591 &manifest); |
|
592 if (status < 0) { |
|
593 PORT_Free (manifest); |
|
594 return status; |
|
595 } |
|
596 } else if (phy->compression) { |
|
597 /* unsupported compression method */ |
|
598 PORT_Free (manifest); |
|
599 return JAR_ERR_CORRUPT; |
|
600 } else |
|
601 length = phy->length; |
|
602 |
|
603 status = JAR_parse_manifest(jar, manifest, length, |
|
604 it->pathname, "url"); |
|
605 PORT_Free (manifest); |
|
606 if (status < 0) |
|
607 ret = status; |
|
608 else |
|
609 ++ret; |
|
610 } else if (it->type == jarTypePhy) { |
|
611 /* ordinary file */ |
|
612 } |
|
613 } |
|
614 return ret; |
|
615 } |
|
616 |
|
617 /* |
|
618 * j a r _ g e n _ i n d e x |
|
619 * |
|
620 * Generate an index for the various types of |
|
621 * known archive files. Right now .ZIP and .TAR |
|
622 * |
|
623 */ |
|
624 static int |
|
625 jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp) |
|
626 { |
|
627 int result = JAR_ERR_CORRUPT; |
|
628 |
|
629 JAR_FSEEK (fp, 0, (PRSeekWhence)0); |
|
630 switch (format) { |
|
631 case jarArchZip: |
|
632 result = jar_listzip (jar, fp); |
|
633 break; |
|
634 |
|
635 case jarArchTar: |
|
636 result = jar_listtar (jar, fp); |
|
637 break; |
|
638 |
|
639 case jarArchGuess: |
|
640 case jarArchNone: |
|
641 return JAR_ERR_GENERAL; |
|
642 } |
|
643 JAR_FSEEK (fp, 0, (PRSeekWhence)0); |
|
644 return result; |
|
645 } |
|
646 |
|
647 /* |
|
648 * j a r _ l i s t z i p |
|
649 * |
|
650 * List the physical contents of a Phil Katz |
|
651 * style .ZIP file into the JAR linked list. |
|
652 * |
|
653 */ |
|
654 static int |
|
655 jar_listzip(JAR *jar, JAR_FILE fp) |
|
656 { |
|
657 ZZLink *ent; |
|
658 JAR_Item *it; |
|
659 JAR_Physical *phy; |
|
660 struct ZipLocal *Local = PORT_ZNew(struct ZipLocal); |
|
661 struct ZipCentral *Central = PORT_ZNew(struct ZipCentral); |
|
662 struct ZipEnd *End = PORT_ZNew(struct ZipEnd); |
|
663 |
|
664 int err = 0; |
|
665 long pos = 0L; |
|
666 unsigned int compression; |
|
667 unsigned int filename_len, extra_len; |
|
668 |
|
669 char filename[JAR_SIZE]; |
|
670 char date[9], time[9]; |
|
671 char sig[4]; |
|
672 |
|
673 if (!Local || !Central || !End) { |
|
674 /* out of memory */ |
|
675 err = JAR_ERR_MEMORY; |
|
676 goto loser; |
|
677 } |
|
678 |
|
679 while (1) { |
|
680 PRUint32 sigVal; |
|
681 JAR_FSEEK (fp, pos, (PRSeekWhence)0); |
|
682 |
|
683 if (JAR_FREAD(fp, sig, sizeof sig) != sizeof sig) { |
|
684 /* zip file ends prematurely */ |
|
685 err = JAR_ERR_CORRUPT; |
|
686 goto loser; |
|
687 } |
|
688 |
|
689 JAR_FSEEK (fp, pos, (PRSeekWhence)0); |
|
690 sigVal = x86LongToUint32(sig); |
|
691 if (sigVal == LSIG) { |
|
692 JAR_FREAD (fp, Local, sizeof *Local); |
|
693 |
|
694 filename_len = x86ShortToUint32(Local->filename_len); |
|
695 extra_len = x86ShortToUint32(Local->extrafield_len); |
|
696 if (filename_len >= JAR_SIZE) { |
|
697 /* corrupt zip file */ |
|
698 err = JAR_ERR_CORRUPT; |
|
699 goto loser; |
|
700 } |
|
701 |
|
702 if (JAR_FREAD (fp, filename, filename_len) != filename_len) { |
|
703 /* truncated archive file */ |
|
704 err = JAR_ERR_CORRUPT; |
|
705 goto loser; |
|
706 } |
|
707 filename [filename_len] = 0; |
|
708 /* Add this to our jar chain */ |
|
709 phy = PORT_ZNew(JAR_Physical); |
|
710 if (phy == NULL) { |
|
711 err = JAR_ERR_MEMORY; |
|
712 goto loser; |
|
713 } |
|
714 |
|
715 /* We will index any file that comes our way, but when it comes |
|
716 to actually extraction, compression must be 0 or 8 */ |
|
717 compression = x86ShortToUint32(Local->method); |
|
718 phy->compression = (compression <= 255) ? compression : 222; |
|
719 /* XXX 222 is bad magic. */ |
|
720 |
|
721 phy->offset = pos + (sizeof *Local) + filename_len + extra_len; |
|
722 phy->length = x86LongToUint32(Local->size); |
|
723 phy->uncompressed_length = x86LongToUint32(Local->orglen); |
|
724 |
|
725 dosdate (date, Local->date); |
|
726 dostime (time, Local->time); |
|
727 |
|
728 it = PORT_ZNew(JAR_Item); |
|
729 if (it == NULL) { |
|
730 err = JAR_ERR_MEMORY; |
|
731 goto loser; |
|
732 } |
|
733 |
|
734 it->pathname = PORT_Strdup(filename); |
|
735 it->type = jarTypePhy; |
|
736 it->data = (unsigned char *) phy; |
|
737 it->size = sizeof (JAR_Physical); |
|
738 |
|
739 ent = ZZ_NewLink (it); |
|
740 if (ent == NULL) { |
|
741 err = JAR_ERR_MEMORY; |
|
742 goto loser; |
|
743 } |
|
744 |
|
745 ZZ_AppendLink (jar->phy, ent); |
|
746 pos = phy->offset + phy->length; |
|
747 } else if (sigVal == CSIG) { |
|
748 unsigned int attr = 0; |
|
749 if (JAR_FREAD(fp, Central, sizeof *Central) != sizeof *Central) { |
|
750 /* apparently truncated archive */ |
|
751 err = JAR_ERR_CORRUPT; |
|
752 goto loser; |
|
753 } |
|
754 |
|
755 #if defined(XP_UNIX) || defined(XP_BEOS) |
|
756 /* with unix we need to locate any bits from |
|
757 the protection mask in the external attributes. */ |
|
758 attr = Central->external_attributes [2]; /* magic */ |
|
759 if (attr) { |
|
760 /* we have to read the filename, again */ |
|
761 filename_len = x86ShortToUint32(Central->filename_len); |
|
762 if (filename_len >= JAR_SIZE) { |
|
763 /* corrupt in central directory */ |
|
764 err = JAR_ERR_CORRUPT; |
|
765 goto loser; |
|
766 } |
|
767 |
|
768 if (JAR_FREAD(fp, filename, filename_len) != filename_len) { |
|
769 /* truncated in central directory */ |
|
770 err = JAR_ERR_CORRUPT; |
|
771 goto loser; |
|
772 } |
|
773 filename [filename_len] = 0; |
|
774 |
|
775 /* look up this name again */ |
|
776 phy = jar_get_physical (jar, filename); |
|
777 if (phy) { |
|
778 /* always allow access by self */ |
|
779 phy->mode = 0400 | attr; |
|
780 } |
|
781 } |
|
782 #endif |
|
783 pos += sizeof(struct ZipCentral) |
|
784 + x86ShortToUint32(Central->filename_len) |
|
785 + x86ShortToUint32(Central->commentfield_len) |
|
786 + x86ShortToUint32(Central->extrafield_len); |
|
787 } else if (sigVal == ESIG) { |
|
788 if (JAR_FREAD(fp, End, sizeof *End) != sizeof *End) { |
|
789 err = JAR_ERR_CORRUPT; |
|
790 goto loser; |
|
791 } |
|
792 break; |
|
793 } else { |
|
794 /* garbage in archive */ |
|
795 err = JAR_ERR_CORRUPT; |
|
796 goto loser; |
|
797 } |
|
798 } |
|
799 |
|
800 loser: |
|
801 if (Local) |
|
802 PORT_Free(Local); |
|
803 if (Central) |
|
804 PORT_Free(Central); |
|
805 if (End) |
|
806 PORT_Free(End); |
|
807 return err; |
|
808 } |
|
809 |
|
810 /* |
|
811 * j a r _ l i s t t a r |
|
812 * |
|
813 * List the physical contents of a Unix |
|
814 * .tar file into the JAR linked list. |
|
815 * |
|
816 */ |
|
817 static int |
|
818 jar_listtar(JAR *jar, JAR_FILE fp) |
|
819 { |
|
820 char *s; |
|
821 JAR_Physical *phy; |
|
822 long pos = 0L; |
|
823 long sz, mode; |
|
824 time_t when; |
|
825 union TarEntry tarball; |
|
826 |
|
827 while (1) { |
|
828 JAR_FSEEK (fp, pos, (PRSeekWhence)0); |
|
829 |
|
830 if (JAR_FREAD (fp, &tarball, sizeof tarball) < sizeof tarball) |
|
831 break; |
|
832 |
|
833 if (!*tarball.val.filename) |
|
834 break; |
|
835 |
|
836 when = octalToLong (tarball.val.time); |
|
837 sz = octalToLong (tarball.val.size); |
|
838 mode = octalToLong (tarball.val.mode); |
|
839 |
|
840 /* Tag the end of filename */ |
|
841 s = tarball.val.filename; |
|
842 while (*s && *s != ' ') |
|
843 s++; |
|
844 *s = 0; |
|
845 |
|
846 /* Add to our linked list */ |
|
847 phy = PORT_ZNew(JAR_Physical); |
|
848 if (phy == NULL) |
|
849 return JAR_ERR_MEMORY; |
|
850 |
|
851 phy->compression = 0; |
|
852 phy->offset = pos + sizeof tarball; |
|
853 phy->length = sz; |
|
854 |
|
855 ADDITEM(jar->phy, jarTypePhy, tarball.val.filename, phy, |
|
856 sizeof *phy); |
|
857 |
|
858 /* Advance to next file entry */ |
|
859 sz = PR_ROUNDUP(sz,sizeof tarball); |
|
860 pos += sz + sizeof tarball; |
|
861 } |
|
862 |
|
863 return 0; |
|
864 } |
|
865 |
|
866 /* |
|
867 * d o s d a t e |
|
868 * |
|
869 * Not used right now, but keep it in here because |
|
870 * it will be needed. |
|
871 * |
|
872 */ |
|
873 static int |
|
874 dosdate(char *date, const char *s) |
|
875 { |
|
876 PRUint32 num = x86ShortToUint32(s); |
|
877 |
|
878 PR_snprintf(date, 9, "%02d-%02d-%02d", ((num >> 5) & 0x0F), (num & 0x1F), |
|
879 ((num >> 9) + 80)); |
|
880 return 0; |
|
881 } |
|
882 |
|
883 /* |
|
884 * d o s t i m e |
|
885 * |
|
886 * Not used right now, but keep it in here because |
|
887 * it will be needed. |
|
888 * |
|
889 */ |
|
890 static int |
|
891 dostime (char *time, const char *s) |
|
892 { |
|
893 PRUint32 num = x86ShortToUint32(s); |
|
894 |
|
895 PR_snprintf (time, 6, "%02d:%02d", ((num >> 11) & 0x1F), |
|
896 ((num >> 5) & 0x3F)); |
|
897 return 0; |
|
898 } |
|
899 |
|
900 #ifndef NSS_X86_OR_X64 |
|
901 /* |
|
902 * Simulates an x86 (little endian, unaligned) ushort fetch from any address. |
|
903 */ |
|
904 static PRUint32 |
|
905 x86ShortToUint32(const void * v) |
|
906 { |
|
907 const unsigned char *ii = (const unsigned char *)v; |
|
908 PRUint32 ret = (PRUint32)(ii[0]) | ((PRUint32)(ii[1]) << 8); |
|
909 return ret; |
|
910 } |
|
911 |
|
912 /* |
|
913 * Simulates an x86 (little endian, unaligned) uint fetch from any address. |
|
914 */ |
|
915 static PRUint32 |
|
916 x86LongToUint32(const void *v) |
|
917 { |
|
918 const unsigned char *ll = (const unsigned char *)v; |
|
919 PRUint32 ret; |
|
920 |
|
921 ret = ((((PRUint32)(ll[0])) << 0) | |
|
922 (((PRUint32)(ll[1])) << 8) | |
|
923 (((PRUint32)(ll[2])) << 16) | |
|
924 (((PRUint32)(ll[3])) << 24)); |
|
925 return ret; |
|
926 } |
|
927 #endif |
|
928 |
|
929 /* |
|
930 * ASCII octal to binary long. |
|
931 * Used for integer encoding inside tar files. |
|
932 * |
|
933 */ |
|
934 static long |
|
935 octalToLong(const char *s) |
|
936 { |
|
937 long num = 0L; |
|
938 |
|
939 while (*s == ' ') |
|
940 s++; |
|
941 while (*s >= '0' && *s <= '7') { |
|
942 num <<= 3; |
|
943 num += *s++ - '0'; |
|
944 } |
|
945 return num; |
|
946 } |
|
947 |
|
948 /* |
|
949 * g u e s s _ j a r |
|
950 * |
|
951 * Try to guess what kind of JAR file this is. |
|
952 * Maybe tar, maybe zip. Look in the file for magic |
|
953 * or at its filename. |
|
954 * |
|
955 */ |
|
956 static int |
|
957 jar_guess_jar(const char *filename, JAR_FILE fp) |
|
958 { |
|
959 PRInt32 len = PORT_Strlen(filename); |
|
960 const char *ext = filename + len - 4; /* 4 for ".tar" */ |
|
961 |
|
962 if (len >= 4 && !PL_strcasecmp(ext, ".tar")) |
|
963 return jarArchTar; |
|
964 return jarArchZip; |
|
965 } |