michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * JARFILE michael@0: * michael@0: * Parsing of a Jar file michael@0: */ michael@0: #define JAR_SIZE 256 michael@0: michael@0: #include "jar.h" michael@0: #include "jarint.h" michael@0: #include "jarfile.h" michael@0: michael@0: /* commercial compression */ michael@0: #include "jzlib.h" michael@0: michael@0: #if defined(XP_UNIX) || defined(XP_BEOS) michael@0: #include "sys/stat.h" michael@0: #endif michael@0: michael@0: #include "sechash.h" /* for HASH_GetHashObject() */ michael@0: michael@0: PR_STATIC_ASSERT(46 == sizeof(struct ZipCentral)); michael@0: PR_STATIC_ASSERT(30 == sizeof(struct ZipLocal)); michael@0: PR_STATIC_ASSERT(22 == sizeof(struct ZipEnd)); michael@0: PR_STATIC_ASSERT(512 == sizeof(union TarEntry)); michael@0: michael@0: /* extracting */ michael@0: static int michael@0: jar_guess_jar(const char *filename, JAR_FILE fp); michael@0: michael@0: static int michael@0: jar_inflate_memory(unsigned int method, long *length, long expected_out_len, michael@0: char **data); michael@0: michael@0: static int michael@0: jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length); michael@0: michael@0: static int michael@0: jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, michael@0: unsigned int method); michael@0: michael@0: static int michael@0: jar_verify_extract(JAR *jar, char *path, char *physical_path); michael@0: michael@0: static JAR_Physical * michael@0: jar_get_physical(JAR *jar, char *pathname); michael@0: michael@0: static int michael@0: jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp); michael@0: michael@0: static int michael@0: jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext); michael@0: michael@0: michael@0: /* indexing */ michael@0: static int michael@0: jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp); michael@0: michael@0: static int michael@0: jar_listtar(JAR *jar, JAR_FILE fp); michael@0: michael@0: static int michael@0: jar_listzip(JAR *jar, JAR_FILE fp); michael@0: michael@0: michael@0: /* conversions */ michael@0: static int michael@0: dosdate(char *date, const char *s); michael@0: michael@0: static int michael@0: dostime(char *time, const char *s); michael@0: michael@0: #ifdef NSS_X86_OR_X64 michael@0: #define x86ShortToUint32(ii) ((const PRUint32)*((const PRUint16 *)(ii))) michael@0: #define x86LongToUint32(ii) (*(const PRUint32 *)(ii)) michael@0: #else michael@0: static PRUint32 michael@0: x86ShortToUint32(const void *ii); michael@0: michael@0: static PRUint32 michael@0: x86LongToUint32(const void *ll); michael@0: #endif michael@0: michael@0: static long michael@0: octalToLong(const char *s); michael@0: michael@0: /* michael@0: * J A R _ p a s s _ a r c h i v e michael@0: * michael@0: * For use by naive clients. Slam an entire archive file michael@0: * into this function. We extract manifests, parse, index michael@0: * the archive file, and do whatever nastiness. michael@0: * michael@0: */ michael@0: int michael@0: JAR_pass_archive(JAR *jar, jarArch format, char *filename, const char *url) michael@0: { michael@0: JAR_FILE fp; michael@0: int status = 0; michael@0: michael@0: if (filename == NULL) michael@0: return JAR_ERR_GENERAL; michael@0: michael@0: if ((fp = JAR_FOPEN (filename, "rb")) != NULL) { michael@0: if (format == jarArchGuess) michael@0: format = (jarArch)jar_guess_jar (filename, fp); michael@0: michael@0: jar->format = format; michael@0: jar->url = url ? PORT_Strdup (url) : NULL; michael@0: jar->filename = PORT_Strdup (filename); michael@0: michael@0: status = jar_gen_index (jar, format, fp); michael@0: if (status == 0) michael@0: status = jar_extract_manifests (jar, format, fp); michael@0: michael@0: JAR_FCLOSE (fp); michael@0: if (status < 0) michael@0: return status; michael@0: michael@0: /* people were expecting it this way */ michael@0: return jar->valid; michael@0: } michael@0: /* file not found */ michael@0: return JAR_ERR_FNF; michael@0: } michael@0: michael@0: /* michael@0: * J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d michael@0: * michael@0: * Same as JAR_pass_archive, but doesn't parse signatures. michael@0: * michael@0: */ michael@0: int michael@0: JAR_pass_archive_unverified(JAR *jar, jarArch format, char *filename, michael@0: const char *url) michael@0: { michael@0: JAR_FILE fp; michael@0: int status = 0; michael@0: michael@0: if (filename == NULL) { michael@0: return JAR_ERR_GENERAL; michael@0: } michael@0: michael@0: if ((fp = JAR_FOPEN (filename, "rb")) != NULL) { michael@0: if (format == jarArchGuess) { michael@0: format = (jarArch)jar_guess_jar (filename, fp); michael@0: } michael@0: michael@0: jar->format = format; michael@0: jar->url = url ? PORT_Strdup (url) : NULL; michael@0: jar->filename = PORT_Strdup (filename); michael@0: michael@0: status = jar_gen_index (jar, format, fp); michael@0: if (status == 0) { michael@0: status = jar_extract_mf(jar, format, fp, "mf"); michael@0: } michael@0: michael@0: JAR_FCLOSE (fp); michael@0: if (status < 0) { michael@0: return status; michael@0: } michael@0: michael@0: /* people were expecting it this way */ michael@0: return jar->valid; michael@0: } michael@0: /* file not found */ michael@0: return JAR_ERR_FNF; michael@0: } michael@0: michael@0: /* michael@0: * J A R _ v e r i f i e d _ e x t r a c t michael@0: * michael@0: * Optimization: keep a file descriptor open michael@0: * inside the JAR structure, so we don't have to michael@0: * open the file 25 times to run java. michael@0: * michael@0: */ michael@0: michael@0: int michael@0: JAR_verified_extract(JAR *jar, char *path, char *outpath) michael@0: { michael@0: int status = JAR_extract (jar, path, outpath); michael@0: michael@0: if (status >= 0) michael@0: return jar_verify_extract(jar, path, outpath); michael@0: return status; michael@0: } michael@0: michael@0: int michael@0: JAR_extract(JAR *jar, char *path, char *outpath) michael@0: { michael@0: int result; michael@0: JAR_Physical *phy; michael@0: michael@0: if (jar->fp == NULL && jar->filename) { michael@0: jar->fp = (FILE*)JAR_FOPEN (jar->filename, "rb"); michael@0: } michael@0: if (jar->fp == NULL) { michael@0: /* file not found */ michael@0: return JAR_ERR_FNF; michael@0: } michael@0: michael@0: phy = jar_get_physical (jar, path); michael@0: if (phy) { michael@0: if (phy->compression != 0 && phy->compression != 8) { michael@0: /* unsupported compression method */ michael@0: result = JAR_ERR_CORRUPT; michael@0: } michael@0: if (phy->compression == 0) { michael@0: result = jar_physical_extraction michael@0: ((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length); michael@0: } else { michael@0: result = jar_physical_inflate((PRFileDesc*)jar->fp, outpath, michael@0: phy->offset, phy->length, michael@0: (unsigned int) phy->compression); michael@0: } michael@0: michael@0: #if defined(XP_UNIX) || defined(XP_BEOS) michael@0: if (phy->mode) michael@0: chmod (outpath, 0400 | (mode_t) phy->mode); michael@0: #endif michael@0: } else { michael@0: /* pathname not found in archive */ michael@0: result = JAR_ERR_PNF; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /* michael@0: * p h y s i c a l _ e x t r a c t i o n michael@0: * michael@0: * This needs to be done in chunks of say 32k, instead of michael@0: * in one bulk calloc. (Necessary under Win16 platform.) michael@0: * This is done for uncompressed entries only. michael@0: * michael@0: */ michael@0: michael@0: #define CHUNK 32768 michael@0: michael@0: static int michael@0: jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length) michael@0: { michael@0: JAR_FILE out; michael@0: char *buffer = (char *)PORT_ZAlloc(CHUNK); michael@0: int status = 0; michael@0: michael@0: if (buffer == NULL) michael@0: return JAR_ERR_MEMORY; michael@0: michael@0: if ((out = JAR_FOPEN (outpath, "wb")) != NULL) { michael@0: long at = 0; michael@0: michael@0: JAR_FSEEK (fp, offset, (PRSeekWhence)0); michael@0: while (at < length) { michael@0: long chunk = (at + CHUNK <= length) ? CHUNK : length - at; michael@0: if (JAR_FREAD (fp, buffer, chunk) != chunk) { michael@0: status = JAR_ERR_DISK; michael@0: break; michael@0: } michael@0: at += chunk; michael@0: if (JAR_FWRITE (out, buffer, chunk) < chunk) { michael@0: /* most likely a disk full error */ michael@0: status = JAR_ERR_DISK; michael@0: break; michael@0: } michael@0: } michael@0: JAR_FCLOSE (out); michael@0: } else { michael@0: /* error opening output file */ michael@0: status = JAR_ERR_DISK; michael@0: } michael@0: PORT_Free (buffer); michael@0: return status; michael@0: } michael@0: michael@0: /* michael@0: * j a r _ p h y s i c a l _ i n f l a t e michael@0: * michael@0: * Inflate a range of bytes in a file, writing the inflated michael@0: * result to "outpath". Chunk based. michael@0: * michael@0: */ michael@0: /* input and output chunks differ, assume 4x compression */ michael@0: michael@0: #define ICHUNK 8192 michael@0: #define OCHUNK 32768 michael@0: michael@0: static int michael@0: jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, michael@0: unsigned int method) michael@0: { michael@0: char *inbuf, *outbuf; michael@0: int status = 0; michael@0: z_stream zs; michael@0: JAR_FILE out; michael@0: michael@0: /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ michael@0: if ((inbuf = (char *)PORT_ZAlloc(ICHUNK + 1)) == NULL) michael@0: return JAR_ERR_MEMORY; michael@0: michael@0: if ((outbuf = (char *)PORT_ZAlloc(OCHUNK)) == NULL) { michael@0: PORT_Free (inbuf); michael@0: return JAR_ERR_MEMORY; michael@0: } michael@0: michael@0: PORT_Memset (&zs, 0, sizeof (zs)); michael@0: status = inflateInit2 (&zs, -MAX_WBITS); michael@0: if (status != Z_OK) { michael@0: PORT_Free (inbuf); michael@0: PORT_Free (outbuf); michael@0: return JAR_ERR_GENERAL; michael@0: } michael@0: michael@0: if ((out = JAR_FOPEN (outpath, "wb")) != NULL) { michael@0: long at = 0; michael@0: michael@0: JAR_FSEEK (fp, offset, (PRSeekWhence)0); michael@0: while (at < length) { michael@0: long chunk = (at + ICHUNK <= length) ? ICHUNK : length - at; michael@0: unsigned long tin; michael@0: michael@0: if (JAR_FREAD (fp, inbuf, chunk) != chunk) { michael@0: /* incomplete read */ michael@0: JAR_FCLOSE (out); michael@0: PORT_Free (inbuf); michael@0: PORT_Free (outbuf); michael@0: return JAR_ERR_CORRUPT; michael@0: } michael@0: at += chunk; michael@0: if (at == length) { michael@0: /* add an extra dummy byte at the end */ michael@0: inbuf[chunk++] = 0xDD; michael@0: } michael@0: zs.next_in = (Bytef *) inbuf; michael@0: zs.avail_in = chunk; michael@0: zs.avail_out = OCHUNK; michael@0: tin = zs.total_in; michael@0: while ((zs.total_in - tin < chunk) || (zs.avail_out == 0)) { michael@0: unsigned long prev_total = zs.total_out; michael@0: unsigned long ochunk; michael@0: michael@0: zs.next_out = (Bytef *) outbuf; michael@0: zs.avail_out = OCHUNK; michael@0: status = inflate (&zs, Z_NO_FLUSH); michael@0: if (status != Z_OK && status != Z_STREAM_END) { michael@0: /* error during decompression */ michael@0: JAR_FCLOSE (out); michael@0: PORT_Free (inbuf); michael@0: PORT_Free (outbuf); michael@0: return JAR_ERR_CORRUPT; michael@0: } michael@0: ochunk = zs.total_out - prev_total; michael@0: if (JAR_FWRITE (out, outbuf, ochunk) < ochunk) { michael@0: /* most likely a disk full error */ michael@0: status = JAR_ERR_DISK; michael@0: break; michael@0: } michael@0: if (status == Z_STREAM_END) michael@0: break; michael@0: } michael@0: } michael@0: JAR_FCLOSE (out); michael@0: status = inflateEnd (&zs); michael@0: } else { michael@0: /* error opening output file */ michael@0: status = JAR_ERR_DISK; michael@0: } michael@0: PORT_Free (inbuf); michael@0: PORT_Free (outbuf); michael@0: return status; michael@0: } michael@0: michael@0: /* michael@0: * j a r _ i n f l a t e _ m e m o r y michael@0: * michael@0: * Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd, michael@0: * and thus appears to operate inplace to the caller. michael@0: * michael@0: */ michael@0: static int michael@0: jar_inflate_memory(unsigned int method, long *length, long expected_out_len, michael@0: char **data) michael@0: { michael@0: char *inbuf = *data; michael@0: char *outbuf = (char*)PORT_ZAlloc(expected_out_len); michael@0: long insz = *length; michael@0: int status; michael@0: z_stream zs; michael@0: michael@0: if (outbuf == NULL) michael@0: return JAR_ERR_MEMORY; michael@0: michael@0: PORT_Memset(&zs, 0, sizeof zs); michael@0: status = inflateInit2 (&zs, -MAX_WBITS); michael@0: if (status < 0) { michael@0: /* error initializing zlib stream */ michael@0: PORT_Free (outbuf); michael@0: return JAR_ERR_GENERAL; michael@0: } michael@0: michael@0: zs.next_in = (Bytef *) inbuf; michael@0: zs.next_out = (Bytef *) outbuf; michael@0: zs.avail_in = insz; michael@0: zs.avail_out = expected_out_len; michael@0: michael@0: status = inflate (&zs, Z_FINISH); michael@0: if (status != Z_OK && status != Z_STREAM_END) { michael@0: /* error during deflation */ michael@0: PORT_Free (outbuf); michael@0: return JAR_ERR_GENERAL; michael@0: } michael@0: michael@0: status = inflateEnd (&zs); michael@0: if (status != Z_OK) { michael@0: /* error during deflation */ michael@0: PORT_Free (outbuf); michael@0: return JAR_ERR_GENERAL; michael@0: } michael@0: PORT_Free(*data); michael@0: *data = outbuf; michael@0: *length = zs.total_out; michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: * v e r i f y _ e x t r a c t michael@0: * michael@0: * Validate signature on the freshly extracted file. michael@0: * michael@0: */ michael@0: static int michael@0: jar_verify_extract(JAR *jar, char *path, char *physical_path) michael@0: { michael@0: int status; michael@0: JAR_Digest dig; michael@0: michael@0: PORT_Memset (&dig, 0, sizeof dig); michael@0: status = JAR_digest_file (physical_path, &dig); michael@0: if (!status) michael@0: status = JAR_verify_digest (jar, path, &dig); michael@0: return status; michael@0: } michael@0: michael@0: /* michael@0: * g e t _ p h y s i c a l michael@0: * michael@0: * Let's get physical. michael@0: * Obtains the offset and length of this file in the jar file. michael@0: * michael@0: */ michael@0: static JAR_Physical * michael@0: jar_get_physical(JAR *jar, char *pathname) michael@0: { michael@0: ZZLink *link; michael@0: ZZList *list = jar->phy; michael@0: michael@0: if (ZZ_ListEmpty (list)) michael@0: return NULL; michael@0: michael@0: for (link = ZZ_ListHead (list); michael@0: !ZZ_ListIterDone (list, link); michael@0: link = link->next) { michael@0: JAR_Item *it = link->thing; michael@0: michael@0: if (it->type == jarTypePhy && michael@0: it->pathname && !PORT_Strcmp (it->pathname, pathname)) { michael@0: JAR_Physical *phy = (JAR_Physical *)it->data; michael@0: return phy; michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: /* michael@0: * j a r _ e x t r a c t _ m a n i f e s t s michael@0: * michael@0: * Extract the manifest files and parse them, michael@0: * from an open archive file whose contents are known. michael@0: * michael@0: */ michael@0: static int michael@0: jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp) michael@0: { michael@0: int status, signatures; michael@0: michael@0: if (format != jarArchZip && format != jarArchTar) michael@0: return JAR_ERR_CORRUPT; michael@0: michael@0: if ((status = jar_extract_mf (jar, format, fp, "mf")) < 0) michael@0: return status; michael@0: if (!status) michael@0: return JAR_ERR_ORDER; michael@0: if ((status = jar_extract_mf (jar, format, fp, "sf")) < 0) michael@0: return status; michael@0: if (!status) michael@0: return JAR_ERR_ORDER; michael@0: if ((status = jar_extract_mf (jar, format, fp, "rsa")) < 0) michael@0: return status; michael@0: signatures = status; michael@0: if ((status = jar_extract_mf (jar, format, fp, "dsa")) < 0) michael@0: return status; michael@0: if (!(signatures += status)) michael@0: return JAR_ERR_SIG; michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: * j a r _ e x t r a c t _ m f michael@0: * michael@0: * Extracts manifest files based on an extension, which michael@0: * should be .MF, .SF, .RSA, etc. Order of the files is now no michael@0: * longer important when zipping jar files. michael@0: * michael@0: */ michael@0: static int michael@0: jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext) michael@0: { michael@0: ZZLink *link; michael@0: ZZList *list = jar->phy; michael@0: int ret = 0; michael@0: michael@0: if (ZZ_ListEmpty (list)) michael@0: return JAR_ERR_PNF; michael@0: michael@0: for (link = ZZ_ListHead (list); michael@0: ret >= 0 && !ZZ_ListIterDone (list, link); michael@0: link = link->next) { michael@0: JAR_Item *it = link->thing; michael@0: michael@0: if (it->type == jarTypePhy && michael@0: !PORT_Strncmp (it->pathname, "META-INF", 8)) michael@0: { michael@0: JAR_Physical *phy = (JAR_Physical *) it->data; michael@0: char *fn = it->pathname + 8; michael@0: char *e; michael@0: char *manifest; michael@0: long length; michael@0: int num, status; michael@0: michael@0: if (PORT_Strlen (it->pathname) < 8) michael@0: continue; michael@0: michael@0: if (*fn == '/' || *fn == '\\') michael@0: fn++; michael@0: if (*fn == 0) { michael@0: /* just a directory entry */ michael@0: continue; michael@0: } michael@0: michael@0: /* skip to extension */ michael@0: for (e = fn; *e && *e != '.'; e++) michael@0: /* yip */ ; michael@0: michael@0: /* and skip dot */ michael@0: if (*e == '.') michael@0: e++; michael@0: if (PORT_Strcasecmp (ext, e)) { michael@0: /* not the right extension */ michael@0: continue; michael@0: } michael@0: if (phy->length == 0 || phy->length > 0xFFFF) { michael@0: /* manifest files cannot be zero length or too big! */ michael@0: /* the 0xFFFF limit is per J2SE SDK */ michael@0: return JAR_ERR_CORRUPT; michael@0: } michael@0: michael@0: /* Read in the manifest and parse it */ michael@0: /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ michael@0: manifest = (char *)PORT_ZAlloc(phy->length + 1); michael@0: if (!manifest) michael@0: return JAR_ERR_MEMORY; michael@0: michael@0: JAR_FSEEK (fp, phy->offset, (PRSeekWhence)0); michael@0: num = JAR_FREAD (fp, manifest, phy->length); michael@0: if (num != phy->length) { michael@0: /* corrupt archive file */ michael@0: PORT_Free (manifest); michael@0: return JAR_ERR_CORRUPT; michael@0: } michael@0: michael@0: if (phy->compression == 8) { michael@0: length = phy->length; michael@0: /* add an extra dummy byte at the end */ michael@0: manifest[length++] = 0xDD; michael@0: status = jar_inflate_memory((unsigned int)phy->compression, michael@0: &length, michael@0: phy->uncompressed_length, michael@0: &manifest); michael@0: if (status < 0) { michael@0: PORT_Free (manifest); michael@0: return status; michael@0: } michael@0: } else if (phy->compression) { michael@0: /* unsupported compression method */ michael@0: PORT_Free (manifest); michael@0: return JAR_ERR_CORRUPT; michael@0: } else michael@0: length = phy->length; michael@0: michael@0: status = JAR_parse_manifest(jar, manifest, length, michael@0: it->pathname, "url"); michael@0: PORT_Free (manifest); michael@0: if (status < 0) michael@0: ret = status; michael@0: else michael@0: ++ret; michael@0: } else if (it->type == jarTypePhy) { michael@0: /* ordinary file */ michael@0: } michael@0: } michael@0: return ret; michael@0: } michael@0: michael@0: /* michael@0: * j a r _ g e n _ i n d e x michael@0: * michael@0: * Generate an index for the various types of michael@0: * known archive files. Right now .ZIP and .TAR michael@0: * michael@0: */ michael@0: static int michael@0: jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp) michael@0: { michael@0: int result = JAR_ERR_CORRUPT; michael@0: michael@0: JAR_FSEEK (fp, 0, (PRSeekWhence)0); michael@0: switch (format) { michael@0: case jarArchZip: michael@0: result = jar_listzip (jar, fp); michael@0: break; michael@0: michael@0: case jarArchTar: michael@0: result = jar_listtar (jar, fp); michael@0: break; michael@0: michael@0: case jarArchGuess: michael@0: case jarArchNone: michael@0: return JAR_ERR_GENERAL; michael@0: } michael@0: JAR_FSEEK (fp, 0, (PRSeekWhence)0); michael@0: return result; michael@0: } michael@0: michael@0: /* michael@0: * j a r _ l i s t z i p michael@0: * michael@0: * List the physical contents of a Phil Katz michael@0: * style .ZIP file into the JAR linked list. michael@0: * michael@0: */ michael@0: static int michael@0: jar_listzip(JAR *jar, JAR_FILE fp) michael@0: { michael@0: ZZLink *ent; michael@0: JAR_Item *it; michael@0: JAR_Physical *phy; michael@0: struct ZipLocal *Local = PORT_ZNew(struct ZipLocal); michael@0: struct ZipCentral *Central = PORT_ZNew(struct ZipCentral); michael@0: struct ZipEnd *End = PORT_ZNew(struct ZipEnd); michael@0: michael@0: int err = 0; michael@0: long pos = 0L; michael@0: unsigned int compression; michael@0: unsigned int filename_len, extra_len; michael@0: michael@0: char filename[JAR_SIZE]; michael@0: char date[9], time[9]; michael@0: char sig[4]; michael@0: michael@0: if (!Local || !Central || !End) { michael@0: /* out of memory */ michael@0: err = JAR_ERR_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: while (1) { michael@0: PRUint32 sigVal; michael@0: JAR_FSEEK (fp, pos, (PRSeekWhence)0); michael@0: michael@0: if (JAR_FREAD(fp, sig, sizeof sig) != sizeof sig) { michael@0: /* zip file ends prematurely */ michael@0: err = JAR_ERR_CORRUPT; michael@0: goto loser; michael@0: } michael@0: michael@0: JAR_FSEEK (fp, pos, (PRSeekWhence)0); michael@0: sigVal = x86LongToUint32(sig); michael@0: if (sigVal == LSIG) { michael@0: JAR_FREAD (fp, Local, sizeof *Local); michael@0: michael@0: filename_len = x86ShortToUint32(Local->filename_len); michael@0: extra_len = x86ShortToUint32(Local->extrafield_len); michael@0: if (filename_len >= JAR_SIZE) { michael@0: /* corrupt zip file */ michael@0: err = JAR_ERR_CORRUPT; michael@0: goto loser; michael@0: } michael@0: michael@0: if (JAR_FREAD (fp, filename, filename_len) != filename_len) { michael@0: /* truncated archive file */ michael@0: err = JAR_ERR_CORRUPT; michael@0: goto loser; michael@0: } michael@0: filename [filename_len] = 0; michael@0: /* Add this to our jar chain */ michael@0: phy = PORT_ZNew(JAR_Physical); michael@0: if (phy == NULL) { michael@0: err = JAR_ERR_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: /* We will index any file that comes our way, but when it comes michael@0: to actually extraction, compression must be 0 or 8 */ michael@0: compression = x86ShortToUint32(Local->method); michael@0: phy->compression = (compression <= 255) ? compression : 222; michael@0: /* XXX 222 is bad magic. */ michael@0: michael@0: phy->offset = pos + (sizeof *Local) + filename_len + extra_len; michael@0: phy->length = x86LongToUint32(Local->size); michael@0: phy->uncompressed_length = x86LongToUint32(Local->orglen); michael@0: michael@0: dosdate (date, Local->date); michael@0: dostime (time, Local->time); michael@0: michael@0: it = PORT_ZNew(JAR_Item); michael@0: if (it == NULL) { michael@0: err = JAR_ERR_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: it->pathname = PORT_Strdup(filename); michael@0: it->type = jarTypePhy; michael@0: it->data = (unsigned char *) phy; michael@0: it->size = sizeof (JAR_Physical); michael@0: michael@0: ent = ZZ_NewLink (it); michael@0: if (ent == NULL) { michael@0: err = JAR_ERR_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: ZZ_AppendLink (jar->phy, ent); michael@0: pos = phy->offset + phy->length; michael@0: } else if (sigVal == CSIG) { michael@0: unsigned int attr = 0; michael@0: if (JAR_FREAD(fp, Central, sizeof *Central) != sizeof *Central) { michael@0: /* apparently truncated archive */ michael@0: err = JAR_ERR_CORRUPT; michael@0: goto loser; michael@0: } michael@0: michael@0: #if defined(XP_UNIX) || defined(XP_BEOS) michael@0: /* with unix we need to locate any bits from michael@0: the protection mask in the external attributes. */ michael@0: attr = Central->external_attributes [2]; /* magic */ michael@0: if (attr) { michael@0: /* we have to read the filename, again */ michael@0: filename_len = x86ShortToUint32(Central->filename_len); michael@0: if (filename_len >= JAR_SIZE) { michael@0: /* corrupt in central directory */ michael@0: err = JAR_ERR_CORRUPT; michael@0: goto loser; michael@0: } michael@0: michael@0: if (JAR_FREAD(fp, filename, filename_len) != filename_len) { michael@0: /* truncated in central directory */ michael@0: err = JAR_ERR_CORRUPT; michael@0: goto loser; michael@0: } michael@0: filename [filename_len] = 0; michael@0: michael@0: /* look up this name again */ michael@0: phy = jar_get_physical (jar, filename); michael@0: if (phy) { michael@0: /* always allow access by self */ michael@0: phy->mode = 0400 | attr; michael@0: } michael@0: } michael@0: #endif michael@0: pos += sizeof(struct ZipCentral) michael@0: + x86ShortToUint32(Central->filename_len) michael@0: + x86ShortToUint32(Central->commentfield_len) michael@0: + x86ShortToUint32(Central->extrafield_len); michael@0: } else if (sigVal == ESIG) { michael@0: if (JAR_FREAD(fp, End, sizeof *End) != sizeof *End) { michael@0: err = JAR_ERR_CORRUPT; michael@0: goto loser; michael@0: } michael@0: break; michael@0: } else { michael@0: /* garbage in archive */ michael@0: err = JAR_ERR_CORRUPT; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: loser: michael@0: if (Local) michael@0: PORT_Free(Local); michael@0: if (Central) michael@0: PORT_Free(Central); michael@0: if (End) michael@0: PORT_Free(End); michael@0: return err; michael@0: } michael@0: michael@0: /* michael@0: * j a r _ l i s t t a r michael@0: * michael@0: * List the physical contents of a Unix michael@0: * .tar file into the JAR linked list. michael@0: * michael@0: */ michael@0: static int michael@0: jar_listtar(JAR *jar, JAR_FILE fp) michael@0: { michael@0: char *s; michael@0: JAR_Physical *phy; michael@0: long pos = 0L; michael@0: long sz, mode; michael@0: time_t when; michael@0: union TarEntry tarball; michael@0: michael@0: while (1) { michael@0: JAR_FSEEK (fp, pos, (PRSeekWhence)0); michael@0: michael@0: if (JAR_FREAD (fp, &tarball, sizeof tarball) < sizeof tarball) michael@0: break; michael@0: michael@0: if (!*tarball.val.filename) michael@0: break; michael@0: michael@0: when = octalToLong (tarball.val.time); michael@0: sz = octalToLong (tarball.val.size); michael@0: mode = octalToLong (tarball.val.mode); michael@0: michael@0: /* Tag the end of filename */ michael@0: s = tarball.val.filename; michael@0: while (*s && *s != ' ') michael@0: s++; michael@0: *s = 0; michael@0: michael@0: /* Add to our linked list */ michael@0: phy = PORT_ZNew(JAR_Physical); michael@0: if (phy == NULL) michael@0: return JAR_ERR_MEMORY; michael@0: michael@0: phy->compression = 0; michael@0: phy->offset = pos + sizeof tarball; michael@0: phy->length = sz; michael@0: michael@0: ADDITEM(jar->phy, jarTypePhy, tarball.val.filename, phy, michael@0: sizeof *phy); michael@0: michael@0: /* Advance to next file entry */ michael@0: sz = PR_ROUNDUP(sz,sizeof tarball); michael@0: pos += sz + sizeof tarball; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: * d o s d a t e michael@0: * michael@0: * Not used right now, but keep it in here because michael@0: * it will be needed. michael@0: * michael@0: */ michael@0: static int michael@0: dosdate(char *date, const char *s) michael@0: { michael@0: PRUint32 num = x86ShortToUint32(s); michael@0: michael@0: PR_snprintf(date, 9, "%02d-%02d-%02d", ((num >> 5) & 0x0F), (num & 0x1F), michael@0: ((num >> 9) + 80)); michael@0: return 0; michael@0: } michael@0: michael@0: /* michael@0: * d o s t i m e michael@0: * michael@0: * Not used right now, but keep it in here because michael@0: * it will be needed. michael@0: * michael@0: */ michael@0: static int michael@0: dostime (char *time, const char *s) michael@0: { michael@0: PRUint32 num = x86ShortToUint32(s); michael@0: michael@0: PR_snprintf (time, 6, "%02d:%02d", ((num >> 11) & 0x1F), michael@0: ((num >> 5) & 0x3F)); michael@0: return 0; michael@0: } michael@0: michael@0: #ifndef NSS_X86_OR_X64 michael@0: /* michael@0: * Simulates an x86 (little endian, unaligned) ushort fetch from any address. michael@0: */ michael@0: static PRUint32 michael@0: x86ShortToUint32(const void * v) michael@0: { michael@0: const unsigned char *ii = (const unsigned char *)v; michael@0: PRUint32 ret = (PRUint32)(ii[0]) | ((PRUint32)(ii[1]) << 8); michael@0: return ret; michael@0: } michael@0: michael@0: /* michael@0: * Simulates an x86 (little endian, unaligned) uint fetch from any address. michael@0: */ michael@0: static PRUint32 michael@0: x86LongToUint32(const void *v) michael@0: { michael@0: const unsigned char *ll = (const unsigned char *)v; michael@0: PRUint32 ret; michael@0: michael@0: ret = ((((PRUint32)(ll[0])) << 0) | michael@0: (((PRUint32)(ll[1])) << 8) | michael@0: (((PRUint32)(ll[2])) << 16) | michael@0: (((PRUint32)(ll[3])) << 24)); michael@0: return ret; michael@0: } michael@0: #endif michael@0: michael@0: /* michael@0: * ASCII octal to binary long. michael@0: * Used for integer encoding inside tar files. michael@0: * michael@0: */ michael@0: static long michael@0: octalToLong(const char *s) michael@0: { michael@0: long num = 0L; michael@0: michael@0: while (*s == ' ') michael@0: s++; michael@0: while (*s >= '0' && *s <= '7') { michael@0: num <<= 3; michael@0: num += *s++ - '0'; michael@0: } michael@0: return num; michael@0: } michael@0: michael@0: /* michael@0: * g u e s s _ j a r michael@0: * michael@0: * Try to guess what kind of JAR file this is. michael@0: * Maybe tar, maybe zip. Look in the file for magic michael@0: * or at its filename. michael@0: * michael@0: */ michael@0: static int michael@0: jar_guess_jar(const char *filename, JAR_FILE fp) michael@0: { michael@0: PRInt32 len = PORT_Strlen(filename); michael@0: const char *ext = filename + len - 4; /* 4 for ".tar" */ michael@0: michael@0: if (len >= 4 && !PL_strcasecmp(ext, ".tar")) michael@0: return jarArchTar; michael@0: return jarArchZip; michael@0: }