2
* The contents of this file are subject to the Mozilla Public
3
* License Version 1.1 (the "License"); you may not use this file
4
* except in compliance with the License. You may obtain a copy of
5
* the License at http://www.mozilla.org/MPL/
7
* Software distributed under the License is distributed on an "AS
8
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9
* implied. See the License for the specific language governing
10
* rights and limitations under the License.
12
* The Original Code is the Netscape security libraries.
14
* The Initial Developer of the Original Code is Netscape
15
* Communications Corporation. Portions created by Netscape are
16
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
21
* Alternatively, the contents of this file may be used under the
22
* terms of the GNU General Public License Version 2 or later (the
23
* "GPL"), in which case the provisions of the GPL are applicable
24
* instead of those above. If you wish to allow use of your
25
* version of this file only under the terms of the GPL and not to
26
* allow others to use your version of this file under the MPL,
27
* indicate your decision by deleting the provisions above and
28
* replace them with the notice and other provisions required by
29
* the GPL. If you do not delete the provisions above, a recipient
30
* may use your version of this file under either the MPL or the
37
* Parsing of a Jar file
47
/* commercial compression */
50
#if defined(XP_UNIX) || defined(XP_BEOS)
54
#include "sechash.h" /* for HASH_GetHashObject() */
58
static int jar_guess_jar (char *filename, JAR_FILE fp);
60
static int jar_inflate_memory
61
(unsigned int method, long *length, long expected_out_len, char ZHUGEP **data);
63
static int jar_physical_extraction
64
(JAR_FILE fp, char *outpath, long offset, long length);
66
static int jar_physical_inflate
67
(JAR_FILE fp, char *outpath, long offset,
68
long length, unsigned int method);
70
static int jar_verify_extract
71
(JAR *jar, char *path, char *physical_path);
73
static JAR_Physical *jar_get_physical (JAR *jar, char *pathname);
75
static int jar_extract_manifests (JAR *jar, jarArch format, JAR_FILE fp);
77
static int jar_extract_mf (JAR *jar, jarArch format, JAR_FILE fp, char *ext);
82
static int jar_gen_index (JAR *jar, jarArch format, JAR_FILE fp);
84
static int jar_listtar (JAR *jar, JAR_FILE fp);
86
static int jar_listzip (JAR *jar, JAR_FILE fp);
91
static int dosdate (char *date, char *s);
93
static int dostime (char *time, char *s);
95
static unsigned int xtoint (unsigned char *ii);
97
static unsigned long xtolong (unsigned char *ll);
99
static long atoo (char *s);
102
* J A R _ p a s s _ a r c h i v e
104
* For use by naive clients. Slam an entire archive file
105
* into this function. We extract manifests, parse, index
106
* the archive file, and do whatever nastiness.
111
(JAR *jar, jarArch format, char *filename, const char *url)
116
if (filename == NULL)
117
return JAR_ERR_GENERAL;
119
if ((fp = JAR_FOPEN (filename, "rb")) != NULL)
121
if (format == jarArchGuess)
122
format = (jarArch)jar_guess_jar (filename, fp);
124
jar->format = format;
125
jar->url = url ? PORT_Strdup (url) : NULL;
126
jar->filename = PORT_Strdup (filename);
128
status = jar_gen_index (jar, format, fp);
131
status = jar_extract_manifests (jar, format, fp);
138
/* people were expecting it this way */
149
* J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d
151
* Same as JAR_pass_archive, but doesn't parse signatures.
154
int JAR_pass_archive_unverified
155
(JAR *jar, jarArch format, char *filename, const char *url)
160
if (filename == NULL) {
161
return JAR_ERR_GENERAL;
164
if ((fp = JAR_FOPEN (filename, "rb")) != NULL) {
165
if (format == jarArchGuess) {
166
format = (jarArch)jar_guess_jar (filename, fp);
169
jar->format = format;
170
jar->url = url ? PORT_Strdup (url) : NULL;
171
jar->filename = PORT_Strdup (filename);
173
status = jar_gen_index (jar, format, fp);
176
status = jar_extract_mf(jar, format, fp, "mf");
185
/* people were expecting it this way */
194
* J A R _ v e r i f i e d _ e x t r a c t
196
* Optimization: keep a file descriptor open
197
* inside the JAR structure, so we don't have to
198
* open the file 25 times to run java.
202
int JAR_verified_extract
203
(JAR *jar, char *path, char *outpath)
207
status = JAR_extract (jar, path, outpath);
210
return jar_verify_extract (jar, path, outpath);
216
(JAR *jar, char *path, char *outpath)
222
if (jar->fp == NULL && jar->filename)
224
jar->fp = (FILE*)JAR_FOPEN (jar->filename, "rb");
233
phy = jar_get_physical (jar, path);
237
if (phy->compression != 0 && phy->compression != 8)
239
/* unsupported compression method */
240
result = JAR_ERR_CORRUPT;
243
if (phy->compression == 0)
245
result = jar_physical_extraction
246
((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length);
250
result = jar_physical_inflate
251
((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length,
252
(unsigned int) phy->compression);
255
#if defined(XP_UNIX) || defined(XP_BEOS)
257
chmod (outpath, 0400 | (mode_t) phy->mode);
262
/* pathname not found in archive */
263
result = JAR_ERR_PNF;
270
* p h y s i c a l _ e x t r a c t i o n
272
* This needs to be done in chunks of say 32k, instead of
273
* in one bulk calloc. (Necessary under Win16 platform.)
274
* This is done for uncompressed entries only.
280
static int jar_physical_extraction
281
(JAR_FILE fp, char *outpath, long offset, long length)
290
buffer = (char *) PORT_ZAlloc (CHUNK);
293
return JAR_ERR_MEMORY;
295
if ((out = JAR_FOPEN (outpath, "wb")) != NULL)
299
JAR_FSEEK (fp, offset, (PRSeekWhence)0);
303
chunk = (at + CHUNK <= length) ? CHUNK : length - at;
305
if (JAR_FREAD (fp, buffer, chunk) != chunk)
307
status = JAR_ERR_DISK;
313
if (JAR_FWRITE (out, buffer, chunk) < chunk)
315
/* most likely a disk full error */
316
status = JAR_ERR_DISK;
324
/* error opening output file */
325
status = JAR_ERR_DISK;
333
* j a r _ p h y s i c a l _ i n f l a t e
335
* Inflate a range of bytes in a file, writing the inflated
336
* result to "outpath". Chunk based.
340
/* input and output chunks differ, assume 4x compression */
345
static int jar_physical_inflate
346
(JAR_FILE fp, char *outpath, long offset,
347
long length, unsigned int method)
354
char *inbuf, *outbuf;
358
unsigned long prev_total, ochunk, tin;
360
if ((inbuf = (char *) PORT_ZAlloc (ICHUNK)) == NULL)
361
return JAR_ERR_MEMORY;
363
if ((outbuf = (char *) PORT_ZAlloc (OCHUNK)) == NULL)
366
return JAR_ERR_MEMORY;
369
PORT_Memset (&zs, 0, sizeof (zs));
370
status = inflateInit2 (&zs, -MAX_WBITS);
373
return JAR_ERR_GENERAL;
375
if ((out = JAR_FOPEN (outpath, "wb")) != NULL)
379
JAR_FSEEK (fp, offset, (PRSeekWhence)0);
383
chunk = (at + ICHUNK <= length) ? ICHUNK : length - at;
385
if (JAR_FREAD (fp, inbuf, chunk) != chunk)
387
/* incomplete read */
388
return JAR_ERR_CORRUPT;
393
zs.next_in = (Bytef *) inbuf;
395
zs.avail_out = OCHUNK;
399
while ((zs.total_in - tin < chunk) || (zs.avail_out == 0))
401
prev_total = zs.total_out;
403
zs.next_out = (Bytef *) outbuf;
404
zs.avail_out = OCHUNK;
406
status = inflate (&zs, Z_NO_FLUSH);
408
if (status != Z_OK && status != Z_STREAM_END)
410
/* error during decompression */
411
return JAR_ERR_CORRUPT;
414
ochunk = zs.total_out - prev_total;
416
if (JAR_FWRITE (out, outbuf, ochunk) < ochunk)
418
/* most likely a disk full error */
419
status = JAR_ERR_DISK;
423
if (status == Z_STREAM_END)
429
status = inflateEnd (&zs);
433
/* error opening output file */
434
status = JAR_ERR_DISK;
444
* j a r _ i n f l a t e _ m e m o r y
446
* Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd,
447
* and thus appears to operate inplace to the caller.
451
static int jar_inflate_memory
452
(unsigned int method, long *length, long expected_out_len, char ZHUGEP **data)
459
char *inbuf, *outbuf;
464
outsz = expected_out_len;
465
outbuf = (char*)PORT_ZAlloc (outsz);
468
return JAR_ERR_MEMORY;
470
PORT_Memset (&zs, 0, sizeof (zs));
472
status = inflateInit2 (&zs, -MAX_WBITS);
476
/* error initializing zlib stream */
477
return JAR_ERR_GENERAL;
480
zs.next_in = (Bytef *) inbuf;
481
zs.next_out = (Bytef *) outbuf;
484
zs.avail_out = outsz;
486
status = inflate (&zs, Z_FINISH);
488
if (status != Z_OK && status != Z_STREAM_END)
490
/* error during deflation */
491
return JAR_ERR_GENERAL;
494
status = inflateEnd (&zs);
498
/* error during deflation */
499
return JAR_ERR_GENERAL;
505
*length = zs.total_out;
511
* v e r i f y _ e x t r a c t
513
* Validate signature on the freshly extracted file.
517
static int jar_verify_extract (JAR *jar, char *path, char *physical_path)
522
PORT_Memset (&dig, 0, sizeof (JAR_Digest));
523
status = JAR_digest_file (physical_path, &dig);
526
status = JAR_verify_digest (jar, path, &dig);
532
* g e t _ p h y s i c a l
534
* Let's get physical.
535
* Obtains the offset and length of this file in the jar file.
539
static JAR_Physical *jar_get_physical (JAR *jar, char *pathname)
550
if (ZZ_ListEmpty (list))
553
for (link = ZZ_ListHead (list);
554
!ZZ_ListIterDone (list, link);
558
if (it->type == jarTypePhy
559
&& it->pathname && !PORT_Strcmp (it->pathname, pathname))
561
phy = (JAR_Physical *) it->data;
570
* j a r _ e x t r a c t _ m a n i f e s t s
572
* Extract the manifest files and parse them,
573
* from an open archive file whose contents are known.
577
static int jar_extract_manifests (JAR *jar, jarArch format, JAR_FILE fp)
581
if (format != jarArchZip && format != jarArchTar)
582
return JAR_ERR_CORRUPT;
584
if ((status = jar_extract_mf (jar, format, fp, "mf")) < 0)
587
if ((status = jar_extract_mf (jar, format, fp, "sf")) < 0)
590
if ((status = jar_extract_mf (jar, format, fp, "rsa")) < 0)
593
if ((status = jar_extract_mf (jar, format, fp, "dsa")) < 0)
600
* j a r _ e x t r a c t _ m f
602
* Extracts manifest files based on an extension, which
603
* should be .MF, .SF, .RSA, etc. Order of the files is now no
604
* longer important when zipping jar files.
608
static int jar_extract_mf (JAR *jar, jarArch format, JAR_FILE fp, char *ext)
618
char ZHUGEP *manifest;
621
int status, ret = 0, num;
625
if (ZZ_ListEmpty (list))
628
for (link = ZZ_ListHead (list);
629
!ZZ_ListIterDone (list, link);
633
if (it->type == jarTypePhy
634
&& !PORT_Strncmp (it->pathname, "META-INF", 8))
636
phy = (JAR_Physical *) it->data;
638
if (PORT_Strlen (it->pathname) < 8)
641
fn = it->pathname + 8;
642
if (*fn == '/' || *fn == '\\') fn++;
646
/* just a directory entry */
650
/* skip to extension */
651
for (e = fn; *e && *e != '.'; e++)
657
if (PORT_Strcasecmp (ext, e))
659
/* not the right extension */
663
if (phy->length == 0)
665
/* manifest files cannot be zero length! */
666
return JAR_ERR_CORRUPT;
669
/* Read in the manifest and parse it */
670
/* FIX? Does this break on win16 for very very large manifest files? */
673
PORT_Assert( phy->length+1 < 0xFFFF );
676
manifest = (char ZHUGEP *) PORT_ZAlloc (phy->length + 1);
679
JAR_FSEEK (fp, phy->offset, (PRSeekWhence)0);
680
num = JAR_FREAD (fp, manifest, phy->length);
682
if (num != phy->length)
684
/* corrupt archive file */
685
return JAR_ERR_CORRUPT;
688
if (phy->compression == 8)
690
length = phy->length;
692
status = jar_inflate_memory ((unsigned int) phy->compression, &length, phy->uncompressed_length, &manifest);
697
else if (phy->compression)
699
/* unsupported compression method */
700
return JAR_ERR_CORRUPT;
703
length = phy->length;
705
status = JAR_parse_manifest
706
(jar, manifest, length, it->pathname, "url");
708
PORT_Free (manifest);
710
if (status < 0 && ret == 0) ret = status;
713
return JAR_ERR_MEMORY;
715
else if (it->type == jarTypePhy)
725
* j a r _ g e n _ i n d e x
727
* Generate an index for the various types of
728
* known archive files. Right now .ZIP and .TAR
732
static int jar_gen_index (JAR *jar, jarArch format, JAR_FILE fp)
734
int result = JAR_ERR_CORRUPT;
735
JAR_FSEEK (fp, 0, (PRSeekWhence)0);
740
result = jar_listzip (jar, fp);
744
result = jar_listtar (jar, fp);
749
return JAR_ERR_GENERAL;
752
JAR_FSEEK (fp, 0, (PRSeekWhence)0);
758
* j a r _ l i s t z i p
760
* List the physical contents of a Phil Katz
761
* style .ZIP file into the JAR linked list.
765
static int jar_listzip (JAR *jar, JAR_FILE fp)
770
char filename [JAR_SIZE];
772
char date [9], time [9];
775
unsigned int compression;
776
unsigned int filename_len, extra_len;
778
struct ZipLocal *Local;
779
struct ZipCentral *Central;
788
Local = (struct ZipLocal *) PORT_ZAlloc (30);
789
Central = (struct ZipCentral *) PORT_ZAlloc (46);
790
End = (struct ZipEnd *) PORT_ZAlloc (22);
792
if (!Local || !Central || !End)
795
err = JAR_ERR_MEMORY;
801
JAR_FSEEK (fp, pos, (PRSeekWhence)0);
803
if (JAR_FREAD (fp, (char *) sig, 4) != 4)
805
/* zip file ends prematurely */
806
err = JAR_ERR_CORRUPT;
810
JAR_FSEEK (fp, pos, (PRSeekWhence)0);
812
if (xtolong ((unsigned char *)sig) == LSIG)
814
JAR_FREAD (fp, (char *) Local, 30);
816
filename_len = xtoint ((unsigned char *) Local->filename_len);
817
extra_len = xtoint ((unsigned char *) Local->extrafield_len);
819
if (filename_len >= JAR_SIZE)
821
/* corrupt zip file */
822
err = JAR_ERR_CORRUPT;
826
if (JAR_FREAD (fp, filename, filename_len) != filename_len)
828
/* truncated archive file */
829
err = JAR_ERR_CORRUPT;
833
filename [filename_len] = 0;
835
/* Add this to our jar chain */
837
phy = (JAR_Physical *) PORT_ZAlloc (sizeof (JAR_Physical));
841
err = JAR_ERR_MEMORY;
845
/* We will index any file that comes our way, but when it comes
846
to actually extraction, compression must be 0 or 8 */
848
compression = xtoint ((unsigned char *) Local->method);
849
phy->compression = compression >= 0 &&
850
compression <= 255 ? compression : 222;
852
phy->offset = pos + 30 + filename_len + extra_len;
853
phy->length = xtolong ((unsigned char *) Local->size);
854
phy->uncompressed_length = xtolong((unsigned char *) Local->orglen);
856
dosdate (date, Local->date);
857
dostime (time, Local->time);
859
it = (JAR_Item*)PORT_ZAlloc (sizeof (JAR_Item));
862
err = JAR_ERR_MEMORY;
866
it->pathname = PORT_Strdup (filename);
868
it->type = jarTypePhy;
870
it->data = (unsigned char *) phy;
871
it->size = sizeof (JAR_Physical);
873
ent = ZZ_NewLink (it);
877
err = JAR_ERR_MEMORY;
881
ZZ_AppendLink (jar->phy, ent);
883
pos = phy->offset + phy->length;
885
else if (xtolong ( (unsigned char *)sig) == CSIG)
887
if (JAR_FREAD (fp, (char *) Central, 46) != 46)
889
/* apparently truncated archive */
890
err = JAR_ERR_CORRUPT;
894
#if defined(XP_UNIX) || defined(XP_BEOS)
895
/* with unix we need to locate any bits from
896
the protection mask in the external attributes. */
900
/* determined empirically */
901
attr = Central->external_attributes [2];
905
/* we have to read the filename, again */
907
filename_len = xtoint ((unsigned char *) Central->filename_len);
909
if (filename_len >= JAR_SIZE)
911
/* corrupt in central directory */
912
err = JAR_ERR_CORRUPT;
916
if (JAR_FREAD (fp, filename, filename_len) != filename_len)
918
/* truncated in central directory */
919
err = JAR_ERR_CORRUPT;
923
filename [filename_len] = 0;
925
/* look up this name again */
926
phy = jar_get_physical (jar, filename);
930
/* always allow access by self */
931
phy->mode = 0400 | attr;
937
pos += 46 + xtoint ( (unsigned char *)Central->filename_len)
938
+ xtoint ( (unsigned char *)Central->commentfield_len)
939
+ xtoint ( (unsigned char *)Central->extrafield_len);
941
else if (xtolong ( (unsigned char *)sig) == ESIG)
943
if (JAR_FREAD (fp, (char *) End, 22) != 22)
945
err = JAR_ERR_CORRUPT;
953
/* garbage in archive */
954
err = JAR_ERR_CORRUPT;
961
if (Local) PORT_Free (Local);
962
if (Central) PORT_Free (Central);
963
if (End) PORT_Free (End);
969
* j a r _ l i s t t a r
971
* List the physical contents of a Unix
972
* .tar file into the JAR linked list.
976
static int jar_listtar (JAR *jar, JAR_FILE fp)
982
union TarEntry tarball;
992
JAR_FSEEK (fp, pos, (PRSeekWhence)0);
994
if (JAR_FREAD (fp, (char *) &tarball, 512) < 512)
997
if (!*tarball.val.filename)
1000
when = atoo (tarball.val.time);
1001
sz = atoo (tarball.val.size);
1002
mode = atoo (tarball.val.mode);
1005
/* Tag the end of filename */
1007
s = tarball.val.filename;
1008
while (*s && *s != ' ') s++;
1012
/* Add to our linked list */
1014
phy = (JAR_Physical *) PORT_ZAlloc (sizeof (JAR_Physical));
1017
return JAR_ERR_MEMORY;
1019
phy->compression = 0;
1020
phy->offset = pos + 512;
1023
ADDITEM (jar->phy, jarTypePhy,
1024
tarball.val.filename, phy, sizeof (JAR_Physical));
1027
/* Advance to next file entry */
1030
sz = (sz / 512) * 512;
1041
* Not used right now, but keep it in here because
1042
* it will be needed.
1046
static int dosdate (char *date, char *s)
1048
int num = xtoint ( (unsigned char *)s);
1050
PR_snprintf (date, 9, "%02d-%02d-%02d",
1051
((num >> 5) & 0x0F), (num & 0x1F), ((num >> 9) + 80));
1059
* Not used right now, but keep it in here because
1060
* it will be needed.
1064
static int dostime (char *time, char *s)
1066
int num = xtoint ( (unsigned char *)s);
1068
PR_snprintf (time, 6, "%02d:%02d",
1069
((num >> 11) & 0x1F), ((num >> 5) & 0x3F));
1077
* Converts a two byte ugly endianed integer
1078
* to our platform's integer.
1082
static unsigned int xtoint (unsigned char *ii)
1084
return (int) (ii [0]) | ((int) ii [1] << 8);
1090
* Converts a four byte ugly endianed integer
1091
* to our platform's integer.
1095
static unsigned long xtolong (unsigned char *ll)
1100
(((unsigned long) ll [0]) << 0) |
1101
(((unsigned long) ll [1]) << 8) |
1102
(((unsigned long) ll [2]) << 16) |
1103
(((unsigned long) ll [3]) << 24)
1112
* Ascii octal to decimal.
1113
* Used for integer encoding inside tar files.
1117
static long atoo (char *s)
1121
while (*s == ' ') s++;
1123
while (*s >= '0' && *s <= '7')
1135
* Try to guess what kind of JAR file this is.
1136
* Maybe tar, maybe zip. Look in the file for magic
1137
* or at its filename.
1141
static int jar_guess_jar (char *filename, JAR_FILE fp)
1145
ext = filename + PORT_Strlen (filename) - 4;
1147
if (!PORT_Strcmp (ext, ".tar"))