summaryrefslogtreecommitdiff
path: root/security/nss/lib/jar/jarfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/jar/jarfile.c')
-rw-r--r--security/nss/lib/jar/jarfile.c1151
1 files changed, 1151 insertions, 0 deletions
diff --git a/security/nss/lib/jar/jarfile.c b/security/nss/lib/jar/jarfile.c
new file mode 100644
index 000000000..19a3eaa02
--- /dev/null
+++ b/security/nss/lib/jar/jarfile.c
@@ -0,0 +1,1151 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * JARFILE
+ *
+ * Parsing of a Jar file
+ */
+
+#define JAR_SIZE 256
+
+#include "jar.h"
+
+#include "jarint.h"
+#include "jarfile.h"
+
+/* commercial compression */
+#include "jzlib.h"
+
+#if defined(XP_UNIX) || defined(XP_BEOS)
+#include "sys/stat.h"
+#endif
+
+#include "sechash.h" /* for HASH_GetHashObject() */
+
+/* extracting */
+
+static int jar_guess_jar (char *filename, JAR_FILE fp);
+
+static int jar_inflate_memory
+ (unsigned int method, long *length, long expected_out_len, char ZHUGEP **data);
+
+static int jar_physical_extraction
+ (JAR_FILE fp, char *outpath, long offset, long length);
+
+static int jar_physical_inflate
+ (JAR_FILE fp, char *outpath, long offset,
+ long length, unsigned int method);
+
+static int jar_verify_extract
+ (JAR *jar, char *path, char *physical_path);
+
+static JAR_Physical *jar_get_physical (JAR *jar, char *pathname);
+
+static int jar_extract_manifests (JAR *jar, jarArch format, JAR_FILE fp);
+
+static int jar_extract_mf (JAR *jar, jarArch format, JAR_FILE fp, char *ext);
+
+
+/* indexing */
+
+static int jar_gen_index (JAR *jar, jarArch format, JAR_FILE fp);
+
+static int jar_listtar (JAR *jar, JAR_FILE fp);
+
+static int jar_listzip (JAR *jar, JAR_FILE fp);
+
+
+/* conversions */
+
+static int dosdate (char *date, char *s);
+
+static int dostime (char *time, char *s);
+
+static unsigned int xtoint (unsigned char *ii);
+
+static unsigned long xtolong (unsigned char *ll);
+
+static long atoo (char *s);
+
+/*
+ * J A R _ p a s s _ a r c h i v e
+ *
+ * For use by naive clients. Slam an entire archive file
+ * into this function. We extract manifests, parse, index
+ * the archive file, and do whatever nastiness.
+ *
+ */
+
+int JAR_pass_archive
+ (JAR *jar, jarArch format, char *filename, const char *url)
+ {
+ JAR_FILE fp;
+ int status = 0;
+
+ if (filename == NULL)
+ return JAR_ERR_GENERAL;
+
+ if ((fp = JAR_FOPEN (filename, "rb")) != NULL)
+ {
+ if (format == jarArchGuess)
+ format = (jarArch)jar_guess_jar (filename, fp);
+
+ jar->format = format;
+ jar->url = url ? PORT_Strdup (url) : NULL;
+ jar->filename = PORT_Strdup (filename);
+
+ status = jar_gen_index (jar, format, fp);
+
+ if (status == 0)
+ status = jar_extract_manifests (jar, format, fp);
+
+ JAR_FCLOSE (fp);
+
+ if (status < 0)
+ return status;
+
+ /* people were expecting it this way */
+ return jar->valid;
+ }
+ else
+ {
+ /* file not found */
+ return JAR_ERR_FNF;
+ }
+ }
+
+/*
+ * J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d
+ *
+ * Same as JAR_pass_archive, but doesn't parse signatures.
+ *
+ */
+int JAR_pass_archive_unverified
+ (JAR *jar, jarArch format, char *filename, const char *url)
+{
+ JAR_FILE fp;
+ int status = 0;
+
+ if (filename == NULL) {
+ return JAR_ERR_GENERAL;
+ }
+
+ if ((fp = JAR_FOPEN (filename, "rb")) != NULL) {
+ if (format == jarArchGuess) {
+ format = (jarArch)jar_guess_jar (filename, fp);
+ }
+
+ jar->format = format;
+ jar->url = url ? PORT_Strdup (url) : NULL;
+ jar->filename = PORT_Strdup (filename);
+
+ status = jar_gen_index (jar, format, fp);
+
+ if (status == 0) {
+ status = jar_extract_mf(jar, format, fp, "mf");
+ }
+
+ JAR_FCLOSE (fp);
+
+ if (status < 0) {
+ return status;
+ }
+
+ /* people were expecting it this way */
+ return jar->valid;
+ } else {
+ /* file not found */
+ return JAR_ERR_FNF;
+ }
+}
+
+/*
+ * J A R _ v e r i f i e d _ e x t r a c t
+ *
+ * Optimization: keep a file descriptor open
+ * inside the JAR structure, so we don't have to
+ * open the file 25 times to run java.
+ *
+ */
+
+int JAR_verified_extract
+ (JAR *jar, char *path, char *outpath)
+ {
+ int status;
+
+ status = JAR_extract (jar, path, outpath);
+
+ if (status >= 0)
+ return jar_verify_extract (jar, path, outpath);
+ else
+ return status;
+ }
+
+int JAR_extract
+ (JAR *jar, char *path, char *outpath)
+ {
+ int result;
+
+ JAR_Physical *phy;
+
+ if (jar->fp == NULL && jar->filename)
+ {
+ jar->fp = (FILE*)JAR_FOPEN (jar->filename, "rb");
+ }
+
+ if (jar->fp == NULL)
+ {
+ /* file not found */
+ return JAR_ERR_FNF;
+ }
+
+ phy = jar_get_physical (jar, path);
+
+ if (phy)
+ {
+ if (phy->compression != 0 && phy->compression != 8)
+ {
+ /* unsupported compression method */
+ result = JAR_ERR_CORRUPT;
+ }
+
+ if (phy->compression == 0)
+ {
+ result = jar_physical_extraction
+ ((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length);
+ }
+ else
+ {
+ result = jar_physical_inflate
+ ((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length,
+ (unsigned int) phy->compression);
+ }
+
+#if defined(XP_UNIX) || defined(XP_BEOS)
+ if (phy->mode)
+ chmod (outpath, 0400 | (mode_t) phy->mode);
+#endif
+ }
+ else
+ {
+ /* pathname not found in archive */
+ result = JAR_ERR_PNF;
+ }
+
+ return result;
+ }
+
+/*
+ * p h y s i c a l _ e x t r a c t i o n
+ *
+ * This needs to be done in chunks of say 32k, instead of
+ * in one bulk calloc. (Necessary under Win16 platform.)
+ * This is done for uncompressed entries only.
+ *
+ */
+
+#define CHUNK 32768
+
+static int jar_physical_extraction
+ (JAR_FILE fp, char *outpath, long offset, long length)
+ {
+ JAR_FILE out;
+
+ char *buffer;
+ long at, chunk;
+
+ int status = 0;
+
+ buffer = (char *) PORT_ZAlloc (CHUNK);
+
+ if (buffer == NULL)
+ return JAR_ERR_MEMORY;
+
+ if ((out = JAR_FOPEN (outpath, "wb")) != NULL)
+ {
+ at = 0;
+
+ JAR_FSEEK (fp, offset, (PRSeekWhence)0);
+
+ while (at < length)
+ {
+ chunk = (at + CHUNK <= length) ? CHUNK : length - at;
+
+ if (JAR_FREAD (fp, buffer, chunk) != chunk)
+ {
+ status = JAR_ERR_DISK;
+ break;
+ }
+
+ at += chunk;
+
+ if (JAR_FWRITE (out, buffer, chunk) < chunk)
+ {
+ /* most likely a disk full error */
+ status = JAR_ERR_DISK;
+ break;
+ }
+ }
+ JAR_FCLOSE (out);
+ }
+ else
+ {
+ /* error opening output file */
+ status = JAR_ERR_DISK;
+ }
+
+ PORT_Free (buffer);
+ return status;
+ }
+
+/*
+ * j a r _ p h y s i c a l _ i n f l a t e
+ *
+ * Inflate a range of bytes in a file, writing the inflated
+ * result to "outpath". Chunk based.
+ *
+ */
+
+/* input and output chunks differ, assume 4x compression */
+
+#define ICHUNK 8192
+#define OCHUNK 32768
+
+static int jar_physical_inflate
+ (JAR_FILE fp, char *outpath, long offset,
+ long length, unsigned int method)
+ {
+ z_stream zs;
+
+ JAR_FILE out;
+
+ long at, chunk;
+ char *inbuf, *outbuf;
+
+ int status = 0;
+
+ unsigned long prev_total, ochunk, tin;
+
+ if ((inbuf = (char *) PORT_ZAlloc (ICHUNK)) == NULL)
+ return JAR_ERR_MEMORY;
+
+ if ((outbuf = (char *) PORT_ZAlloc (OCHUNK)) == NULL)
+ {
+ PORT_Free (inbuf);
+ return JAR_ERR_MEMORY;
+ }
+
+ PORT_Memset (&zs, 0, sizeof (zs));
+ status = inflateInit2 (&zs, -MAX_WBITS);
+
+ if (status != Z_OK)
+ return JAR_ERR_GENERAL;
+
+ if ((out = JAR_FOPEN (outpath, "wb")) != NULL)
+ {
+ at = 0;
+
+ JAR_FSEEK (fp, offset, (PRSeekWhence)0);
+
+ while (at < length)
+ {
+ chunk = (at + ICHUNK <= length) ? ICHUNK : length - at;
+
+ if (JAR_FREAD (fp, inbuf, chunk) != chunk)
+ {
+ /* incomplete read */
+ return JAR_ERR_CORRUPT;
+ }
+
+ at += chunk;
+
+ zs.next_in = (Bytef *) inbuf;
+ zs.avail_in = chunk;
+ zs.avail_out = OCHUNK;
+
+ tin = zs.total_in;
+
+ while ((zs.total_in - tin < chunk) || (zs.avail_out == 0))
+ {
+ prev_total = zs.total_out;
+
+ zs.next_out = (Bytef *) outbuf;
+ zs.avail_out = OCHUNK;
+
+ status = inflate (&zs, Z_NO_FLUSH);
+
+ if (status != Z_OK && status != Z_STREAM_END)
+ {
+ /* error during decompression */
+ return JAR_ERR_CORRUPT;
+ }
+
+ ochunk = zs.total_out - prev_total;
+
+ if (JAR_FWRITE (out, outbuf, ochunk) < ochunk)
+ {
+ /* most likely a disk full error */
+ status = JAR_ERR_DISK;
+ break;
+ }
+
+ if (status == Z_STREAM_END)
+ break;
+ }
+ }
+
+ JAR_FCLOSE (out);
+ status = inflateEnd (&zs);
+ }
+ else
+ {
+ /* error opening output file */
+ status = JAR_ERR_DISK;
+ }
+
+ PORT_Free (inbuf);
+ PORT_Free (outbuf);
+
+ return status;
+ }
+
+/*
+ * j a r _ i n f l a t e _ m e m o r y
+ *
+ * Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd,
+ * and thus appears to operate inplace to the caller.
+ *
+ */
+
+static int jar_inflate_memory
+ (unsigned int method, long *length, long expected_out_len, char ZHUGEP **data)
+ {
+ int status;
+ z_stream zs;
+
+ long insz, outsz;
+
+ char *inbuf, *outbuf;
+
+ inbuf = *data;
+ insz = *length;
+
+ outsz = expected_out_len;
+ outbuf = (char*)PORT_ZAlloc (outsz);
+
+ if (outbuf == NULL)
+ return JAR_ERR_MEMORY;
+
+ PORT_Memset (&zs, 0, sizeof (zs));
+
+ status = inflateInit2 (&zs, -MAX_WBITS);
+
+ if (status < 0)
+ {
+ /* error initializing zlib stream */
+ return JAR_ERR_GENERAL;
+ }
+
+ zs.next_in = (Bytef *) inbuf;
+ zs.next_out = (Bytef *) outbuf;
+
+ zs.avail_in = insz;
+ zs.avail_out = outsz;
+
+ status = inflate (&zs, Z_FINISH);
+
+ if (status != Z_OK && status != Z_STREAM_END)
+ {
+ /* error during deflation */
+ return JAR_ERR_GENERAL;
+ }
+
+ status = inflateEnd (&zs);
+
+ if (status != Z_OK)
+ {
+ /* error during deflation */
+ return JAR_ERR_GENERAL;
+ }
+
+ PORT_Free (*data);
+
+ *data = outbuf;
+ *length = zs.total_out;
+
+ return 0;
+ }
+
+/*
+ * v e r i f y _ e x t r a c t
+ *
+ * Validate signature on the freshly extracted file.
+ *
+ */
+
+static int jar_verify_extract (JAR *jar, char *path, char *physical_path)
+ {
+ int status;
+ JAR_Digest dig;
+
+ PORT_Memset (&dig, 0, sizeof (JAR_Digest));
+ status = JAR_digest_file (physical_path, &dig);
+
+ if (!status)
+ status = JAR_verify_digest (jar, path, &dig);
+
+ return status;
+ }
+
+/*
+ * g e t _ p h y s i c a l
+ *
+ * Let's get physical.
+ * Obtains the offset and length of this file in the jar file.
+ *
+ */
+
+static JAR_Physical *jar_get_physical (JAR *jar, char *pathname)
+ {
+ JAR_Item *it;
+
+ JAR_Physical *phy;
+
+ ZZLink *link;
+ ZZList *list;
+
+ list = jar->phy;
+
+ if (ZZ_ListEmpty (list))
+ return NULL;
+
+ for (link = ZZ_ListHead (list);
+ !ZZ_ListIterDone (list, link);
+ link = link->next)
+ {
+ it = link->thing;
+ if (it->type == jarTypePhy
+ && it->pathname && !PORT_Strcmp (it->pathname, pathname))
+ {
+ phy = (JAR_Physical *) it->data;
+ return phy;
+ }
+ }
+
+ return NULL;
+ }
+
+/*
+ * j a r _ e x t r a c t _ m a n i f e s t s
+ *
+ * Extract the manifest files and parse them,
+ * from an open archive file whose contents are known.
+ *
+ */
+
+static int jar_extract_manifests (JAR *jar, jarArch format, JAR_FILE fp)
+ {
+ int status;
+
+ if (format != jarArchZip && format != jarArchTar)
+ return JAR_ERR_CORRUPT;
+
+ if ((status = jar_extract_mf (jar, format, fp, "mf")) < 0)
+ return status;
+
+ if ((status = jar_extract_mf (jar, format, fp, "sf")) < 0)
+ return status;
+
+ if ((status = jar_extract_mf (jar, format, fp, "rsa")) < 0)
+ return status;
+
+ if ((status = jar_extract_mf (jar, format, fp, "dsa")) < 0)
+ return status;
+
+ return 0;
+ }
+
+/*
+ * j a r _ e x t r a c t _ m f
+ *
+ * Extracts manifest files based on an extension, which
+ * should be .MF, .SF, .RSA, etc. Order of the files is now no
+ * longer important when zipping jar files.
+ *
+ */
+
+static int jar_extract_mf (JAR *jar, jarArch format, JAR_FILE fp, char *ext)
+ {
+ JAR_Item *it;
+
+ JAR_Physical *phy;
+
+ ZZLink *link;
+ ZZList *list;
+
+ char *fn, *e;
+ char ZHUGEP *manifest;
+
+ long length;
+ int status, ret = 0, num;
+
+ list = jar->phy;
+
+ if (ZZ_ListEmpty (list))
+ return JAR_ERR_PNF;
+
+ for (link = ZZ_ListHead (list);
+ !ZZ_ListIterDone (list, link);
+ link = link->next)
+ {
+ it = link->thing;
+ if (it->type == jarTypePhy
+ && !PORT_Strncmp (it->pathname, "META-INF", 8))
+ {
+ phy = (JAR_Physical *) it->data;
+
+ if (PORT_Strlen (it->pathname) < 8)
+ continue;
+
+ fn = it->pathname + 8;
+ if (*fn == '/' || *fn == '\\') fn++;
+
+ if (*fn == 0)
+ {
+ /* just a directory entry */
+ continue;
+ }
+
+ /* skip to extension */
+ for (e = fn; *e && *e != '.'; e++)
+ /* yip */ ;
+
+ /* and skip dot */
+ if (*e == '.') e++;
+
+ if (PORT_Strcasecmp (ext, e))
+ {
+ /* not the right extension */
+ continue;
+ }
+
+ if (phy->length == 0)
+ {
+ /* manifest files cannot be zero length! */
+ return JAR_ERR_CORRUPT;
+ }
+
+ /* Read in the manifest and parse it */
+ /* FIX? Does this break on win16 for very very large manifest files? */
+
+#ifdef XP_WIN16
+ PORT_Assert( phy->length+1 < 0xFFFF );
+#endif
+
+ manifest = (char ZHUGEP *) PORT_ZAlloc (phy->length + 1);
+ if (manifest)
+ {
+ JAR_FSEEK (fp, phy->offset, (PRSeekWhence)0);
+ num = JAR_FREAD (fp, manifest, phy->length);
+
+ if (num != phy->length)
+ {
+ /* corrupt archive file */
+ return JAR_ERR_CORRUPT;
+ }
+
+ if (phy->compression == 8)
+ {
+ length = phy->length;
+
+ status = jar_inflate_memory ((unsigned int) phy->compression, &length, phy->uncompressed_length, &manifest);
+
+ if (status < 0)
+ return status;
+ }
+ else if (phy->compression)
+ {
+ /* unsupported compression method */
+ return JAR_ERR_CORRUPT;
+ }
+ else
+ length = phy->length;
+
+ status = JAR_parse_manifest
+ (jar, manifest, length, it->pathname, "url");
+
+ PORT_Free (manifest);
+
+ if (status < 0 && ret == 0) ret = status;
+ }
+ else
+ return JAR_ERR_MEMORY;
+ }
+ else if (it->type == jarTypePhy)
+ {
+ /* ordinary file */
+ }
+ }
+
+ return ret;
+ }
+
+/*
+ * j a r _ g e n _ i n d e x
+ *
+ * Generate an index for the various types of
+ * known archive files. Right now .ZIP and .TAR
+ *
+ */
+
+static int jar_gen_index (JAR *jar, jarArch format, JAR_FILE fp)
+ {
+ int result = JAR_ERR_CORRUPT;
+ JAR_FSEEK (fp, 0, (PRSeekWhence)0);
+
+ switch (format)
+ {
+ case jarArchZip:
+ result = jar_listzip (jar, fp);
+ break;
+
+ case jarArchTar:
+ result = jar_listtar (jar, fp);
+ break;
+
+ case jarArchGuess:
+ case jarArchNone:
+ return JAR_ERR_GENERAL;
+ }
+
+ JAR_FSEEK (fp, 0, (PRSeekWhence)0);
+ return result;
+ }
+
+
+/*
+ * j a r _ l i s t z i p
+ *
+ * List the physical contents of a Phil Katz
+ * style .ZIP file into the JAR linked list.
+ *
+ */
+
+static int jar_listzip (JAR *jar, JAR_FILE fp)
+ {
+ int err = 0;
+
+ long pos = 0L;
+ char filename [JAR_SIZE];
+
+ char date [9], time [9];
+ char sig [4];
+
+ unsigned int compression;
+ unsigned int filename_len, extra_len;
+
+ struct ZipLocal *Local;
+ struct ZipCentral *Central;
+ struct ZipEnd *End;
+
+ /* phy things */
+
+ ZZLink *ent;
+ JAR_Item *it;
+ JAR_Physical *phy;
+
+ Local = (struct ZipLocal *) PORT_ZAlloc (30);
+ Central = (struct ZipCentral *) PORT_ZAlloc (46);
+ End = (struct ZipEnd *) PORT_ZAlloc (22);
+
+ if (!Local || !Central || !End)
+ {
+ /* out of memory */
+ err = JAR_ERR_MEMORY;
+ goto loser;
+ }
+
+ while (1)
+ {
+ JAR_FSEEK (fp, pos, (PRSeekWhence)0);
+
+ if (JAR_FREAD (fp, (char *) sig, 4) != 4)
+ {
+ /* zip file ends prematurely */
+ err = JAR_ERR_CORRUPT;
+ goto loser;
+ }
+
+ JAR_FSEEK (fp, pos, (PRSeekWhence)0);
+
+ if (xtolong ((unsigned char *)sig) == LSIG)
+ {
+ JAR_FREAD (fp, (char *) Local, 30);
+
+ filename_len = xtoint ((unsigned char *) Local->filename_len);
+ extra_len = xtoint ((unsigned char *) Local->extrafield_len);
+
+ if (filename_len >= JAR_SIZE)
+ {
+ /* corrupt zip file */
+ err = JAR_ERR_CORRUPT;
+ goto loser;
+ }
+
+ if (JAR_FREAD (fp, filename, filename_len) != filename_len)
+ {
+ /* truncated archive file */
+ err = JAR_ERR_CORRUPT;
+ goto loser;
+ }
+
+ filename [filename_len] = 0;
+
+ /* Add this to our jar chain */
+
+ phy = (JAR_Physical *) PORT_ZAlloc (sizeof (JAR_Physical));
+
+ if (phy == NULL)
+ {
+ err = JAR_ERR_MEMORY;
+ goto loser;
+ }
+
+ /* We will index any file that comes our way, but when it comes
+ to actually extraction, compression must be 0 or 8 */
+
+ compression = xtoint ((unsigned char *) Local->method);
+ phy->compression = compression >= 0 &&
+ compression <= 255 ? compression : 222;
+
+ phy->offset = pos + 30 + filename_len + extra_len;
+ phy->length = xtolong ((unsigned char *) Local->size);
+ phy->uncompressed_length = xtolong((unsigned char *) Local->orglen);
+
+ dosdate (date, Local->date);
+ dostime (time, Local->time);
+
+ it = (JAR_Item*)PORT_ZAlloc (sizeof (JAR_Item));
+ if (it == NULL)
+ {
+ err = JAR_ERR_MEMORY;
+ goto loser;
+ }
+
+ it->pathname = PORT_Strdup (filename);
+
+ it->type = jarTypePhy;
+
+ it->data = (unsigned char *) phy;
+ it->size = sizeof (JAR_Physical);
+
+ ent = ZZ_NewLink (it);
+
+ if (ent == NULL)
+ {
+ err = JAR_ERR_MEMORY;
+ goto loser;
+ }
+
+ ZZ_AppendLink (jar->phy, ent);
+
+ pos = phy->offset + phy->length;
+ }
+ else if (xtolong ( (unsigned char *)sig) == CSIG)
+ {
+ if (JAR_FREAD (fp, (char *) Central, 46) != 46)
+ {
+ /* apparently truncated archive */
+ err = JAR_ERR_CORRUPT;
+ goto loser;
+ }
+
+#if defined(XP_UNIX) || defined(XP_BEOS)
+ /* with unix we need to locate any bits from
+ the protection mask in the external attributes. */
+ {
+ unsigned int attr;
+
+ /* determined empirically */
+ attr = Central->external_attributes [2];
+
+ if (attr)
+ {
+ /* we have to read the filename, again */
+
+ filename_len = xtoint ((unsigned char *) Central->filename_len);
+
+ if (filename_len >= JAR_SIZE)
+ {
+ /* corrupt in central directory */
+ err = JAR_ERR_CORRUPT;
+ goto loser;
+ }
+
+ if (JAR_FREAD (fp, filename, filename_len) != filename_len)
+ {
+ /* truncated in central directory */
+ err = JAR_ERR_CORRUPT;
+ goto loser;
+ }
+
+ filename [filename_len] = 0;
+
+ /* look up this name again */
+ phy = jar_get_physical (jar, filename);
+
+ if (phy)
+ {
+ /* always allow access by self */
+ phy->mode = 0400 | attr;
+ }
+ }
+ }
+#endif
+
+ pos += 46 + xtoint ( (unsigned char *)Central->filename_len)
+ + xtoint ( (unsigned char *)Central->commentfield_len)
+ + xtoint ( (unsigned char *)Central->extrafield_len);
+ }
+ else if (xtolong ( (unsigned char *)sig) == ESIG)
+ {
+ if (JAR_FREAD (fp, (char *) End, 22) != 22)
+ {
+ err = JAR_ERR_CORRUPT;
+ goto loser;
+ }
+ else
+ break;
+ }
+ else
+ {
+ /* garbage in archive */
+ err = JAR_ERR_CORRUPT;
+ goto loser;
+ }
+ }
+
+loser:
+
+ if (Local) PORT_Free (Local);
+ if (Central) PORT_Free (Central);
+ if (End) PORT_Free (End);
+
+ return err;
+ }
+
+/*
+ * j a r _ l i s t t a r
+ *
+ * List the physical contents of a Unix
+ * .tar file into the JAR linked list.
+ *
+ */
+
+static int jar_listtar (JAR *jar, JAR_FILE fp)
+ {
+ long pos = 0L;
+
+ long sz, mode;
+ time_t when;
+ union TarEntry tarball;
+
+ char *s;
+
+ /* phy things */
+
+ JAR_Physical *phy;
+
+ while (1)
+ {
+ JAR_FSEEK (fp, pos, (PRSeekWhence)0);
+
+ if (JAR_FREAD (fp, (char *) &tarball, 512) < 512)
+ break;
+
+ if (!*tarball.val.filename)
+ break;
+
+ when = atoo (tarball.val.time);
+ sz = atoo (tarball.val.size);
+ mode = atoo (tarball.val.mode);
+
+
+ /* Tag the end of filename */
+
+ s = tarball.val.filename;
+ while (*s && *s != ' ') s++;
+ *s = 0;
+
+
+ /* Add to our linked list */
+
+ phy = (JAR_Physical *) PORT_ZAlloc (sizeof (JAR_Physical));
+
+ if (phy == NULL)
+ return JAR_ERR_MEMORY;
+
+ phy->compression = 0;
+ phy->offset = pos + 512;
+ phy->length = sz;
+
+ ADDITEM (jar->phy, jarTypePhy,
+ tarball.val.filename, phy, sizeof (JAR_Physical));
+
+
+ /* Advance to next file entry */
+
+ sz += 511;
+ sz = (sz / 512) * 512;
+
+ pos += sz + 512;
+ }
+
+ return 0;
+ }
+
+/*
+ * d o s d a t e
+ *
+ * Not used right now, but keep it in here because
+ * it will be needed.
+ *
+ */
+
+static int dosdate (char *date, char *s)
+ {
+ int num = xtoint ( (unsigned char *)s);
+
+ PR_snprintf (date, 9, "%02d-%02d-%02d",
+ ((num >> 5) & 0x0F), (num & 0x1F), ((num >> 9) + 80));
+
+ return 0;
+ }
+
+/*
+ * d o s t i m e
+ *
+ * Not used right now, but keep it in here because
+ * it will be needed.
+ *
+ */
+
+static int dostime (char *time, char *s)
+ {
+ int num = xtoint ( (unsigned char *)s);
+
+ PR_snprintf (time, 6, "%02d:%02d",
+ ((num >> 11) & 0x1F), ((num >> 5) & 0x3F));
+
+ return 0;
+ }
+
+/*
+ * x t o i n t
+ *
+ * Converts a two byte ugly endianed integer
+ * to our platform's integer.
+ *
+ */
+
+static unsigned int xtoint (unsigned char *ii)
+ {
+ return (int) (ii [0]) | ((int) ii [1] << 8);
+ }
+
+/*
+ * x t o l o n g
+ *
+ * Converts a four byte ugly endianed integer
+ * to our platform's integer.
+ *
+ */
+
+static unsigned long xtolong (unsigned char *ll)
+ {
+ unsigned long ret;
+
+ ret = (
+ (((unsigned long) ll [0]) << 0) |
+ (((unsigned long) ll [1]) << 8) |
+ (((unsigned long) ll [2]) << 16) |
+ (((unsigned long) ll [3]) << 24)
+ );
+
+ return ret;
+ }
+
+/*
+ * a t o o
+ *
+ * Ascii octal to decimal.
+ * Used for integer encoding inside tar files.
+ *
+ */
+
+static long atoo (char *s)
+ {
+ long num = 0L;
+
+ while (*s == ' ') s++;
+
+ while (*s >= '0' && *s <= '7')
+ {
+ num <<= 3;
+ num += *s++ - '0';
+ }
+
+ return num;
+ }
+
+/*
+ * g u e s s _ j a r
+ *
+ * Try to guess what kind of JAR file this is.
+ * Maybe tar, maybe zip. Look in the file for magic
+ * or at its filename.
+ *
+ */
+
+static int jar_guess_jar (char *filename, JAR_FILE fp)
+ {
+ char *ext;
+
+ ext = filename + PORT_Strlen (filename) - 4;
+
+ if (!PORT_Strcmp (ext, ".tar"))
+ return jarArchTar;
+
+ return jarArchZip;
+ }