Blame nss/lib/jar/jarfile.c

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