security/nss/lib/jar/jarfile.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/jar/jarfile.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,965 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 + *  JARFILE
    1.10 + *
    1.11 + *  Parsing of a Jar file
    1.12 + */
    1.13 +#define JAR_SIZE 256
    1.14 +
    1.15 +#include "jar.h"
    1.16 +#include "jarint.h"
    1.17 +#include "jarfile.h"
    1.18 +
    1.19 +/* commercial compression */
    1.20 +#include "jzlib.h"
    1.21 +
    1.22 +#if defined(XP_UNIX) || defined(XP_BEOS)
    1.23 +#include "sys/stat.h"
    1.24 +#endif
    1.25 +
    1.26 +#include "sechash.h"	/* for HASH_GetHashObject() */
    1.27 +
    1.28 +PR_STATIC_ASSERT(46 == sizeof(struct ZipCentral));
    1.29 +PR_STATIC_ASSERT(30 == sizeof(struct ZipLocal));
    1.30 +PR_STATIC_ASSERT(22 == sizeof(struct ZipEnd));
    1.31 +PR_STATIC_ASSERT(512 == sizeof(union TarEntry));
    1.32 +
    1.33 +/* extracting */
    1.34 +static int 
    1.35 +jar_guess_jar(const char *filename, JAR_FILE fp);
    1.36 +
    1.37 +static int 
    1.38 +jar_inflate_memory(unsigned int method, long *length, long expected_out_len, 
    1.39 +                   char **data);
    1.40 +
    1.41 +static int 
    1.42 +jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length);
    1.43 +
    1.44 +static int 
    1.45 +jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, 
    1.46 +                     unsigned int method);
    1.47 +
    1.48 +static int 
    1.49 +jar_verify_extract(JAR *jar, char *path, char *physical_path);
    1.50 +
    1.51 +static JAR_Physical *
    1.52 +jar_get_physical(JAR *jar, char *pathname);
    1.53 +
    1.54 +static int 
    1.55 +jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp);
    1.56 +
    1.57 +static int 
    1.58 +jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext);
    1.59 +
    1.60 +
    1.61 +/* indexing */
    1.62 +static int 
    1.63 +jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp);
    1.64 +
    1.65 +static int 
    1.66 +jar_listtar(JAR *jar, JAR_FILE fp);
    1.67 +
    1.68 +static int 
    1.69 +jar_listzip(JAR *jar, JAR_FILE fp);
    1.70 +
    1.71 +
    1.72 +/* conversions */
    1.73 +static int 
    1.74 +dosdate(char *date, const char *s);
    1.75 +
    1.76 +static int 
    1.77 +dostime(char *time, const char *s);
    1.78 +
    1.79 +#ifdef NSS_X86_OR_X64
    1.80 +#define x86ShortToUint32(ii)   ((const PRUint32)*((const PRUint16 *)(ii)))
    1.81 +#define x86LongToUint32(ii)    (*(const PRUint32 *)(ii))
    1.82 +#else
    1.83 +static PRUint32
    1.84 +x86ShortToUint32(const void *ii);
    1.85 +
    1.86 +static PRUint32
    1.87 +x86LongToUint32(const void *ll);
    1.88 +#endif
    1.89 +
    1.90 +static long 
    1.91 +octalToLong(const char *s);
    1.92 +
    1.93 +/*
    1.94 + *  J A R _ p a s s _ a r c h i v e
    1.95 + *
    1.96 + *  For use by naive clients. Slam an entire archive file
    1.97 + *  into this function. We extract manifests, parse, index
    1.98 + *  the archive file, and do whatever nastiness.
    1.99 + *
   1.100 + */
   1.101 +int 
   1.102 +JAR_pass_archive(JAR *jar, jarArch format, char *filename, const char *url)
   1.103 +{
   1.104 +    JAR_FILE fp;
   1.105 +    int status = 0;
   1.106 +
   1.107 +    if (filename == NULL)
   1.108 +	return JAR_ERR_GENERAL;
   1.109 +
   1.110 +    if ((fp = JAR_FOPEN (filename, "rb")) != NULL) {
   1.111 +	if (format == jarArchGuess)
   1.112 +	    format = (jarArch)jar_guess_jar (filename, fp);
   1.113 +
   1.114 +	jar->format = format;
   1.115 +	jar->url = url ? PORT_Strdup (url) : NULL;
   1.116 +	jar->filename = PORT_Strdup (filename);
   1.117 +
   1.118 +	status = jar_gen_index (jar, format, fp);
   1.119 +	if (status == 0)
   1.120 +	    status = jar_extract_manifests (jar, format, fp);
   1.121 +
   1.122 +	JAR_FCLOSE (fp);
   1.123 +	if (status < 0)
   1.124 +	    return status;
   1.125 +
   1.126 +	/* people were expecting it this way */
   1.127 +	return jar->valid;
   1.128 +    }
   1.129 +    /* file not found */
   1.130 +    return JAR_ERR_FNF;
   1.131 +}
   1.132 +
   1.133 +/*
   1.134 + *  J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d
   1.135 + *
   1.136 + * Same as JAR_pass_archive, but doesn't parse signatures.
   1.137 + *
   1.138 + */
   1.139 +int 
   1.140 +JAR_pass_archive_unverified(JAR *jar, jarArch format, char *filename, 
   1.141 +                            const char *url)
   1.142 +{
   1.143 +    JAR_FILE fp;
   1.144 +    int status = 0;
   1.145 +
   1.146 +    if (filename == NULL) {
   1.147 +	return JAR_ERR_GENERAL;
   1.148 +    }
   1.149 +
   1.150 +    if ((fp = JAR_FOPEN (filename, "rb")) != NULL) {
   1.151 +	if (format == jarArchGuess) {
   1.152 +	    format = (jarArch)jar_guess_jar (filename, fp);
   1.153 +	}
   1.154 +
   1.155 +	jar->format = format;
   1.156 +	jar->url = url ? PORT_Strdup (url) : NULL;
   1.157 +	jar->filename = PORT_Strdup (filename);
   1.158 +
   1.159 +	status = jar_gen_index (jar, format, fp);
   1.160 +	if (status == 0) {
   1.161 +	    status = jar_extract_mf(jar, format, fp, "mf");
   1.162 +	}
   1.163 +
   1.164 +	JAR_FCLOSE (fp);
   1.165 +	if (status < 0) {
   1.166 +	    return status;
   1.167 +	}
   1.168 +
   1.169 +	/* people were expecting it this way */
   1.170 +	return jar->valid;
   1.171 +    }
   1.172 +    /* file not found */
   1.173 +    return JAR_ERR_FNF;
   1.174 +}
   1.175 +
   1.176 +/*
   1.177 + *  J A R _ v e r i f i e d _ e x t r a c t
   1.178 + *
   1.179 + *  Optimization: keep a file descriptor open
   1.180 + *  inside the JAR structure, so we don't have to
   1.181 + *  open the file 25 times to run java.
   1.182 + *
   1.183 + */
   1.184 +
   1.185 +int 
   1.186 +JAR_verified_extract(JAR *jar, char *path, char *outpath)
   1.187 +{
   1.188 +    int status = JAR_extract (jar, path, outpath);
   1.189 +
   1.190 +    if (status >= 0)
   1.191 +	return jar_verify_extract(jar, path, outpath);
   1.192 +    return status;
   1.193 +}
   1.194 +
   1.195 +int 
   1.196 +JAR_extract(JAR *jar, char *path, char *outpath)
   1.197 +{
   1.198 +    int result;
   1.199 +    JAR_Physical *phy;
   1.200 +
   1.201 +    if (jar->fp == NULL && jar->filename) {
   1.202 +	jar->fp = (FILE*)JAR_FOPEN (jar->filename, "rb");
   1.203 +    }
   1.204 +    if (jar->fp == NULL) {
   1.205 +	/* file not found */
   1.206 +	return JAR_ERR_FNF;
   1.207 +    }
   1.208 +
   1.209 +    phy = jar_get_physical (jar, path);
   1.210 +    if (phy) {
   1.211 +	if (phy->compression != 0 && phy->compression != 8) {
   1.212 +	    /* unsupported compression method */
   1.213 +	    result = JAR_ERR_CORRUPT;
   1.214 +	}
   1.215 +	if (phy->compression == 0) {
   1.216 +	    result = jar_physical_extraction
   1.217 +		((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length);
   1.218 +	} else {
   1.219 +	    result = jar_physical_inflate((PRFileDesc*)jar->fp, outpath, 
   1.220 +	                                  phy->offset, phy->length,
   1.221 +	                                  (unsigned int) phy->compression);
   1.222 +	}
   1.223 +
   1.224 +#if defined(XP_UNIX) || defined(XP_BEOS)
   1.225 +	if (phy->mode)
   1.226 +	    chmod (outpath, 0400 | (mode_t) phy->mode);
   1.227 +#endif
   1.228 +    } else {
   1.229 +	/* pathname not found in archive */
   1.230 +	result = JAR_ERR_PNF;
   1.231 +    }
   1.232 +    return result;
   1.233 +}
   1.234 +
   1.235 +/*
   1.236 + *  p h y s i c a l _ e x t r a c t i o n
   1.237 + *
   1.238 + *  This needs to be done in chunks of say 32k, instead of
   1.239 + *  in one bulk calloc. (Necessary under Win16 platform.)
   1.240 + *  This is done for uncompressed entries only.
   1.241 + *
   1.242 + */
   1.243 +
   1.244 +#define CHUNK 32768
   1.245 +
   1.246 +static int 
   1.247 +jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length)
   1.248 +{
   1.249 +    JAR_FILE out;
   1.250 +    char *buffer = (char *)PORT_ZAlloc(CHUNK);
   1.251 +    int status = 0;
   1.252 +
   1.253 +    if (buffer == NULL)
   1.254 +	return JAR_ERR_MEMORY;
   1.255 +
   1.256 +    if ((out = JAR_FOPEN (outpath, "wb")) != NULL) {
   1.257 +	long at = 0;
   1.258 +
   1.259 +	JAR_FSEEK (fp, offset, (PRSeekWhence)0);
   1.260 +	while (at < length) {
   1.261 +	    long chunk = (at + CHUNK <= length) ? CHUNK : length - at;
   1.262 +	    if (JAR_FREAD (fp, buffer, chunk) != chunk) {
   1.263 +		status = JAR_ERR_DISK;
   1.264 +		break;
   1.265 +	    }
   1.266 +	    at += chunk;
   1.267 +	    if (JAR_FWRITE (out, buffer, chunk) < chunk) {
   1.268 +		/* most likely a disk full error */
   1.269 +		status = JAR_ERR_DISK;
   1.270 +		break;
   1.271 +	    }
   1.272 +	}
   1.273 +	JAR_FCLOSE (out);
   1.274 +    } else {
   1.275 +	/* error opening output file */
   1.276 +	status = JAR_ERR_DISK;
   1.277 +    }
   1.278 +    PORT_Free (buffer);
   1.279 +    return status;
   1.280 +}
   1.281 +
   1.282 +/*
   1.283 + *  j a r _ p h y s i c a l _ i n f l a t e
   1.284 + *
   1.285 + *  Inflate a range of bytes in a file, writing the inflated
   1.286 + *  result to "outpath". Chunk based.
   1.287 + *
   1.288 + */
   1.289 +/* input and output chunks differ, assume 4x compression */
   1.290 +
   1.291 +#define ICHUNK 8192
   1.292 +#define OCHUNK 32768
   1.293 +
   1.294 +static int 
   1.295 +jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, 
   1.296 +                     unsigned int method)
   1.297 +{
   1.298 +    char *inbuf, *outbuf;
   1.299 +    int status = 0;
   1.300 +    z_stream zs;
   1.301 +    JAR_FILE out;
   1.302 +
   1.303 +    /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */
   1.304 +    if ((inbuf = (char *)PORT_ZAlloc(ICHUNK + 1)) == NULL)
   1.305 +	return JAR_ERR_MEMORY;
   1.306 +
   1.307 +    if ((outbuf = (char *)PORT_ZAlloc(OCHUNK)) == NULL) {
   1.308 +	PORT_Free (inbuf);
   1.309 +	return JAR_ERR_MEMORY;
   1.310 +    }
   1.311 +
   1.312 +    PORT_Memset (&zs, 0, sizeof (zs));
   1.313 +    status = inflateInit2 (&zs, -MAX_WBITS);
   1.314 +    if (status != Z_OK) {
   1.315 +	PORT_Free (inbuf);
   1.316 +	PORT_Free (outbuf);
   1.317 +	return JAR_ERR_GENERAL;
   1.318 +    }
   1.319 +
   1.320 +    if ((out = JAR_FOPEN (outpath, "wb")) != NULL) {
   1.321 +	long at = 0;
   1.322 +
   1.323 +	JAR_FSEEK (fp, offset, (PRSeekWhence)0);
   1.324 +	while (at < length) {
   1.325 +	    long chunk = (at + ICHUNK <= length) ? ICHUNK : length - at;
   1.326 +	    unsigned long tin;
   1.327 +
   1.328 +	    if (JAR_FREAD (fp, inbuf, chunk) != chunk) {
   1.329 +		/* incomplete read */
   1.330 +		JAR_FCLOSE (out);
   1.331 +		PORT_Free (inbuf);
   1.332 +		PORT_Free (outbuf);
   1.333 +		return JAR_ERR_CORRUPT;
   1.334 +	    }
   1.335 +	    at += chunk;
   1.336 +	    if (at == length) {
   1.337 +		/* add an extra dummy byte at the end */
   1.338 +		inbuf[chunk++] = 0xDD;
   1.339 +	    }
   1.340 +	    zs.next_in = (Bytef *) inbuf;
   1.341 +	    zs.avail_in = chunk;
   1.342 +	    zs.avail_out = OCHUNK;
   1.343 +	    tin = zs.total_in;
   1.344 +	    while ((zs.total_in - tin < chunk) || (zs.avail_out == 0)) {
   1.345 +		unsigned long prev_total = zs.total_out;
   1.346 +		unsigned long ochunk;
   1.347 +
   1.348 +		zs.next_out = (Bytef *) outbuf;
   1.349 +		zs.avail_out = OCHUNK;
   1.350 +		status = inflate (&zs, Z_NO_FLUSH);
   1.351 +		if (status != Z_OK && status != Z_STREAM_END) {
   1.352 +		    /* error during decompression */
   1.353 +		    JAR_FCLOSE (out);
   1.354 +		    PORT_Free (inbuf);
   1.355 +		    PORT_Free (outbuf);
   1.356 +		    return JAR_ERR_CORRUPT;
   1.357 +		}
   1.358 +		ochunk = zs.total_out - prev_total;
   1.359 +		if (JAR_FWRITE (out, outbuf, ochunk) < ochunk) {
   1.360 +		    /* most likely a disk full error */
   1.361 +		    status = JAR_ERR_DISK;
   1.362 +		    break;
   1.363 +		}
   1.364 +		if (status == Z_STREAM_END)
   1.365 +		    break;
   1.366 +	    }
   1.367 +	}
   1.368 +	JAR_FCLOSE (out);
   1.369 +	status = inflateEnd (&zs);
   1.370 +    } else {
   1.371 +	/* error opening output file */
   1.372 +	status = JAR_ERR_DISK;
   1.373 +    }
   1.374 +    PORT_Free (inbuf);
   1.375 +    PORT_Free (outbuf);
   1.376 +    return status;
   1.377 +}
   1.378 +
   1.379 +/*
   1.380 + *  j a r _ i n f l a t e _ m e m o r y
   1.381 + *
   1.382 + *  Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd,
   1.383 + *  and thus appears to operate inplace to the caller.
   1.384 + *
   1.385 + */
   1.386 +static int 
   1.387 +jar_inflate_memory(unsigned int method, long *length, long expected_out_len, 
   1.388 +                   char **data)
   1.389 +{
   1.390 +    char *inbuf  = *data;
   1.391 +    char *outbuf = (char*)PORT_ZAlloc(expected_out_len);
   1.392 +    long insz    = *length;
   1.393 +    int status;
   1.394 +    z_stream zs;
   1.395 +
   1.396 +    if (outbuf == NULL)
   1.397 +	return JAR_ERR_MEMORY;
   1.398 +
   1.399 +    PORT_Memset(&zs, 0, sizeof zs);
   1.400 +    status = inflateInit2 (&zs, -MAX_WBITS);
   1.401 +    if (status < 0) {
   1.402 +	/* error initializing zlib stream */
   1.403 +	PORT_Free (outbuf);
   1.404 +	return JAR_ERR_GENERAL;
   1.405 +    }
   1.406 +
   1.407 +    zs.next_in = (Bytef *) inbuf;
   1.408 +    zs.next_out = (Bytef *) outbuf;
   1.409 +    zs.avail_in = insz;
   1.410 +    zs.avail_out = expected_out_len;
   1.411 +
   1.412 +    status = inflate (&zs, Z_FINISH);
   1.413 +    if (status != Z_OK && status != Z_STREAM_END) {
   1.414 +	/* error during deflation */
   1.415 +	PORT_Free (outbuf);
   1.416 +	return JAR_ERR_GENERAL;
   1.417 +    }
   1.418 +
   1.419 +    status = inflateEnd (&zs);
   1.420 +    if (status != Z_OK) {
   1.421 +	/* error during deflation */
   1.422 +	PORT_Free (outbuf);
   1.423 +	return JAR_ERR_GENERAL;
   1.424 +    }
   1.425 +    PORT_Free(*data);
   1.426 +    *data = outbuf;
   1.427 +    *length = zs.total_out;
   1.428 +    return 0;
   1.429 +}
   1.430 +
   1.431 +/*
   1.432 + *  v e r i f y _ e x t r a c t
   1.433 + *
   1.434 + *  Validate signature on the freshly extracted file.
   1.435 + *
   1.436 + */
   1.437 +static int 
   1.438 +jar_verify_extract(JAR *jar, char *path, char *physical_path)
   1.439 +{
   1.440 +    int status;
   1.441 +    JAR_Digest dig;
   1.442 +
   1.443 +    PORT_Memset (&dig, 0, sizeof dig);
   1.444 +    status = JAR_digest_file (physical_path, &dig);
   1.445 +    if (!status)
   1.446 +	status = JAR_verify_digest (jar, path, &dig);
   1.447 +    return status;
   1.448 +}
   1.449 +
   1.450 +/*
   1.451 + *  g e t _ p h y s i c a l
   1.452 + *
   1.453 + *  Let's get physical.
   1.454 + *  Obtains the offset and length of this file in the jar file.
   1.455 + *
   1.456 + */
   1.457 +static JAR_Physical *
   1.458 +jar_get_physical(JAR *jar, char *pathname)
   1.459 +{
   1.460 +    ZZLink *link;
   1.461 +    ZZList *list = jar->phy;
   1.462 +
   1.463 +    if (ZZ_ListEmpty (list))
   1.464 +	return NULL;
   1.465 +
   1.466 +    for (link = ZZ_ListHead (list);
   1.467 +         !ZZ_ListIterDone (list, link);
   1.468 +         link = link->next) {
   1.469 +	JAR_Item *it = link->thing;
   1.470 +
   1.471 +	if (it->type == jarTypePhy && 
   1.472 +	    it->pathname && !PORT_Strcmp (it->pathname, pathname)) {
   1.473 +	    JAR_Physical *phy = (JAR_Physical *)it->data;
   1.474 +	    return phy;
   1.475 +	}
   1.476 +    }
   1.477 +    return NULL;
   1.478 +}
   1.479 +
   1.480 +/*
   1.481 + *  j a r _ e x t r a c t _ m a n i f e s t s
   1.482 + *
   1.483 + *  Extract the manifest files and parse them,
   1.484 + *  from an open archive file whose contents are known.
   1.485 + *
   1.486 + */
   1.487 +static int 
   1.488 +jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp)
   1.489 +{
   1.490 +    int status, signatures;
   1.491 +
   1.492 +    if (format != jarArchZip && format != jarArchTar)
   1.493 +	return JAR_ERR_CORRUPT;
   1.494 +
   1.495 +    if ((status = jar_extract_mf (jar, format, fp, "mf")) < 0)
   1.496 +	return status;
   1.497 +    if (!status)
   1.498 +	return JAR_ERR_ORDER;
   1.499 +    if ((status = jar_extract_mf (jar, format, fp, "sf")) < 0)
   1.500 +	return status;
   1.501 +    if (!status)
   1.502 +	return JAR_ERR_ORDER;
   1.503 +    if ((status = jar_extract_mf (jar, format, fp, "rsa")) < 0)
   1.504 +	return status;
   1.505 +    signatures = status;
   1.506 +    if ((status = jar_extract_mf (jar, format, fp, "dsa")) < 0)
   1.507 +	return status;
   1.508 +    if (!(signatures += status))
   1.509 +	return JAR_ERR_SIG;
   1.510 +    return 0;
   1.511 +}
   1.512 +
   1.513 +/*
   1.514 + *  j a r _ e x t r a c t _ m f
   1.515 + *
   1.516 + *  Extracts manifest files based on an extension, which
   1.517 + *  should be .MF, .SF, .RSA, etc. Order of the files is now no
   1.518 + *  longer important when zipping jar files.
   1.519 + *
   1.520 + */
   1.521 +static int 
   1.522 +jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext)
   1.523 +{
   1.524 +    ZZLink *link;
   1.525 +    ZZList *list = jar->phy;
   1.526 +    int ret = 0;
   1.527 +
   1.528 +    if (ZZ_ListEmpty (list))
   1.529 +	return JAR_ERR_PNF;
   1.530 +
   1.531 +    for (link = ZZ_ListHead (list);
   1.532 +         ret >= 0 && !ZZ_ListIterDone (list, link);
   1.533 +         link = link->next) {
   1.534 +	JAR_Item *it = link->thing;
   1.535 +
   1.536 +	if (it->type == jarTypePhy && 
   1.537 +	    !PORT_Strncmp (it->pathname, "META-INF", 8))
   1.538 +	{
   1.539 +	    JAR_Physical *phy = (JAR_Physical *) it->data;
   1.540 +	    char *fn = it->pathname + 8;
   1.541 +	    char *e;
   1.542 +	    char *manifest;
   1.543 +	    long length;
   1.544 +	    int num, status;
   1.545 +
   1.546 +	    if (PORT_Strlen (it->pathname) < 8)
   1.547 +		continue;
   1.548 +
   1.549 +	    if (*fn == '/' || *fn == '\\') 
   1.550 +	    	fn++;
   1.551 +	    if (*fn == 0) {
   1.552 +		/* just a directory entry */
   1.553 +		continue;
   1.554 +	    }
   1.555 +
   1.556 +	    /* skip to extension */
   1.557 +	    for (e = fn; *e && *e != '.'; e++)
   1.558 +		/* yip */ ;
   1.559 +
   1.560 +	    /* and skip dot */
   1.561 +	    if (*e == '.') 
   1.562 +	    	e++;
   1.563 +	    if (PORT_Strcasecmp (ext, e)) {
   1.564 +		/* not the right extension */
   1.565 +		continue;
   1.566 +	    }
   1.567 +	    if (phy->length == 0 || phy->length > 0xFFFF) {
   1.568 +		/* manifest files cannot be zero length or too big! */
   1.569 +		/* the 0xFFFF limit is per J2SE SDK */
   1.570 +		return JAR_ERR_CORRUPT;
   1.571 +	    }
   1.572 +
   1.573 +	    /* Read in the manifest and parse it */
   1.574 +	    /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */
   1.575 +	    manifest = (char *)PORT_ZAlloc(phy->length + 1);
   1.576 +	    if (!manifest) 
   1.577 +		return JAR_ERR_MEMORY;
   1.578 +
   1.579 +	    JAR_FSEEK (fp, phy->offset, (PRSeekWhence)0);
   1.580 +	    num = JAR_FREAD (fp, manifest, phy->length);
   1.581 +	    if (num != phy->length) {
   1.582 +		/* corrupt archive file */
   1.583 +		PORT_Free (manifest);
   1.584 +		return JAR_ERR_CORRUPT;
   1.585 +	    }
   1.586 +
   1.587 +	    if (phy->compression == 8) {
   1.588 +		length = phy->length;
   1.589 +		/* add an extra dummy byte at the end */
   1.590 +		manifest[length++] = 0xDD;
   1.591 +		status = jar_inflate_memory((unsigned int)phy->compression, 
   1.592 +					     &length,  
   1.593 +					     phy->uncompressed_length, 
   1.594 +					     &manifest);
   1.595 +		if (status < 0) {
   1.596 +		    PORT_Free (manifest);
   1.597 +		    return status;
   1.598 +		}
   1.599 +	    } else if (phy->compression) {
   1.600 +		/* unsupported compression method */
   1.601 +		PORT_Free (manifest);
   1.602 +		return JAR_ERR_CORRUPT;
   1.603 +	    } else
   1.604 +		length = phy->length;
   1.605 +
   1.606 +	    status = JAR_parse_manifest(jar, manifest, length, 
   1.607 +					it->pathname, "url");
   1.608 +	    PORT_Free (manifest);
   1.609 +	    if (status < 0)
   1.610 +		ret = status;
   1.611 +	    else
   1.612 +		++ret;
   1.613 +	} else if (it->type == jarTypePhy) {
   1.614 +	    /* ordinary file */
   1.615 +	}
   1.616 +    }
   1.617 +    return ret;
   1.618 +}
   1.619 +
   1.620 +/*
   1.621 + *  j a r _ g e n _ i n d e x
   1.622 + *
   1.623 + *  Generate an index for the various types of
   1.624 + *  known archive files. Right now .ZIP and .TAR
   1.625 + *
   1.626 + */
   1.627 +static int 
   1.628 +jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp)
   1.629 +{
   1.630 +    int result = JAR_ERR_CORRUPT;
   1.631 +
   1.632 +    JAR_FSEEK (fp, 0, (PRSeekWhence)0);
   1.633 +    switch (format) {
   1.634 +    case jarArchZip:
   1.635 +	result = jar_listzip (jar, fp);
   1.636 +	break;
   1.637 +
   1.638 +    case jarArchTar:
   1.639 +	result = jar_listtar (jar, fp);
   1.640 +	break;
   1.641 +
   1.642 +    case jarArchGuess:
   1.643 +    case jarArchNone:
   1.644 +	return JAR_ERR_GENERAL;
   1.645 +    }
   1.646 +    JAR_FSEEK (fp, 0, (PRSeekWhence)0);
   1.647 +    return result;
   1.648 +}
   1.649 +
   1.650 +/*
   1.651 + *  j a r _ l i s t z i p
   1.652 + *
   1.653 + *  List the physical contents of a Phil Katz
   1.654 + *  style .ZIP file into the JAR linked list.
   1.655 + *
   1.656 + */
   1.657 +static int 
   1.658 +jar_listzip(JAR *jar, JAR_FILE fp)
   1.659 +{
   1.660 +    ZZLink  *ent;
   1.661 +    JAR_Item *it;
   1.662 +    JAR_Physical *phy;
   1.663 +    struct ZipLocal *Local     = PORT_ZNew(struct ZipLocal);
   1.664 +    struct ZipCentral *Central = PORT_ZNew(struct ZipCentral);
   1.665 +    struct ZipEnd *End         = PORT_ZNew(struct ZipEnd);
   1.666 +
   1.667 +    int err = 0;
   1.668 +    long pos = 0L;
   1.669 +    unsigned int compression;
   1.670 +    unsigned int filename_len, extra_len;
   1.671 +
   1.672 +    char filename[JAR_SIZE];
   1.673 +    char date[9], time[9];
   1.674 +    char sig[4];
   1.675 +
   1.676 +    if (!Local || !Central || !End) {
   1.677 +	/* out of memory */
   1.678 +	err = JAR_ERR_MEMORY;
   1.679 +	goto loser;
   1.680 +    }
   1.681 +
   1.682 +    while (1) {
   1.683 +	PRUint32 sigVal;
   1.684 +	JAR_FSEEK (fp, pos, (PRSeekWhence)0);
   1.685 +
   1.686 +	if (JAR_FREAD(fp, sig, sizeof sig) != sizeof sig) {
   1.687 +	    /* zip file ends prematurely */
   1.688 +	    err = JAR_ERR_CORRUPT;
   1.689 +	    goto loser;
   1.690 +	}
   1.691 +
   1.692 +	JAR_FSEEK (fp, pos, (PRSeekWhence)0);
   1.693 +	sigVal = x86LongToUint32(sig);
   1.694 +	if (sigVal == LSIG) {
   1.695 +	    JAR_FREAD (fp, Local, sizeof *Local);
   1.696 +
   1.697 +	    filename_len = x86ShortToUint32(Local->filename_len);
   1.698 +	    extra_len    = x86ShortToUint32(Local->extrafield_len);
   1.699 +	    if (filename_len >= JAR_SIZE) {
   1.700 +		/* corrupt zip file */
   1.701 +		err = JAR_ERR_CORRUPT;
   1.702 +		goto loser;
   1.703 +	    }
   1.704 +
   1.705 +	    if (JAR_FREAD (fp, filename, filename_len) != filename_len) {
   1.706 +		/* truncated archive file */
   1.707 +		err = JAR_ERR_CORRUPT;
   1.708 +		goto loser;
   1.709 +	    }
   1.710 +	    filename [filename_len] = 0;
   1.711 +	    /* Add this to our jar chain */
   1.712 +	    phy = PORT_ZNew(JAR_Physical);
   1.713 +	    if (phy == NULL) {
   1.714 +		err = JAR_ERR_MEMORY;
   1.715 +		goto loser;
   1.716 +	    }
   1.717 +
   1.718 +	    /* We will index any file that comes our way, but when it comes
   1.719 +	       to actually extraction, compression must be 0 or 8 */
   1.720 +	    compression = x86ShortToUint32(Local->method);
   1.721 +	    phy->compression = (compression <= 255) ? compression : 222;
   1.722 +		/* XXX 222 is bad magic. */
   1.723 +
   1.724 +	    phy->offset = pos + (sizeof *Local) + filename_len + extra_len;
   1.725 +	    phy->length = x86LongToUint32(Local->size);
   1.726 +	    phy->uncompressed_length = x86LongToUint32(Local->orglen);
   1.727 +
   1.728 +	    dosdate (date, Local->date);
   1.729 +	    dostime (time, Local->time);
   1.730 +
   1.731 +	    it = PORT_ZNew(JAR_Item);
   1.732 +	    if (it == NULL) {
   1.733 +		err = JAR_ERR_MEMORY;
   1.734 +		goto loser;
   1.735 +	    }
   1.736 +
   1.737 +	    it->pathname = PORT_Strdup(filename);
   1.738 +	    it->type = jarTypePhy;
   1.739 +	    it->data = (unsigned char *) phy;
   1.740 +	    it->size = sizeof (JAR_Physical);
   1.741 +
   1.742 +	    ent = ZZ_NewLink (it);
   1.743 +	    if (ent == NULL) {
   1.744 +		err = JAR_ERR_MEMORY;
   1.745 +		goto loser;
   1.746 +	    }
   1.747 +
   1.748 +	    ZZ_AppendLink (jar->phy, ent);
   1.749 +	    pos = phy->offset + phy->length;
   1.750 +	} else if (sigVal == CSIG) {
   1.751 +	    unsigned int attr = 0;
   1.752 +	    if (JAR_FREAD(fp, Central, sizeof *Central) != sizeof *Central) {
   1.753 +		/* apparently truncated archive */
   1.754 +		err = JAR_ERR_CORRUPT;
   1.755 +		goto loser;
   1.756 +	    }
   1.757 +
   1.758 +#if defined(XP_UNIX) || defined(XP_BEOS)
   1.759 +	    /* with unix we need to locate any bits from
   1.760 +	       the protection mask in the external attributes. */
   1.761 +	    attr = Central->external_attributes [2]; /* magic */
   1.762 +	    if (attr) {
   1.763 +		/* we have to read the filename, again */
   1.764 +		filename_len = x86ShortToUint32(Central->filename_len);
   1.765 +		if (filename_len >= JAR_SIZE) {
   1.766 +		    /* corrupt in central directory */
   1.767 +		    err = JAR_ERR_CORRUPT;
   1.768 +		    goto loser;
   1.769 +		}
   1.770 +
   1.771 +		if (JAR_FREAD(fp, filename, filename_len) != filename_len) {
   1.772 +		    /* truncated in central directory */
   1.773 +		    err = JAR_ERR_CORRUPT;
   1.774 +		    goto loser;
   1.775 +		}
   1.776 +		filename [filename_len] = 0;
   1.777 +
   1.778 +		/* look up this name again */
   1.779 +		phy = jar_get_physical (jar, filename);
   1.780 +		if (phy) {
   1.781 +		    /* always allow access by self */
   1.782 +		    phy->mode = 0400 | attr;
   1.783 +		}
   1.784 +	    }
   1.785 +#endif
   1.786 +	    pos += sizeof(struct ZipCentral) 
   1.787 +	         + x86ShortToUint32(Central->filename_len)
   1.788 +		 + x86ShortToUint32(Central->commentfield_len)
   1.789 +		 + x86ShortToUint32(Central->extrafield_len);
   1.790 +	} else if (sigVal == ESIG) {
   1.791 +	    if (JAR_FREAD(fp, End, sizeof *End) != sizeof *End) {
   1.792 +		err = JAR_ERR_CORRUPT;
   1.793 +		goto loser;
   1.794 +	    }
   1.795 +	    break;
   1.796 +	} else {
   1.797 +	    /* garbage in archive */
   1.798 +	    err = JAR_ERR_CORRUPT;
   1.799 +	    goto loser;
   1.800 +	}
   1.801 +    }
   1.802 +
   1.803 +loser:
   1.804 +    if (Local) 
   1.805 +    	PORT_Free(Local);
   1.806 +    if (Central) 
   1.807 +    	PORT_Free(Central);
   1.808 +    if (End) 
   1.809 +    	PORT_Free(End);
   1.810 +    return err;
   1.811 +}
   1.812 +
   1.813 +/*
   1.814 + *  j a r _ l i s t t a r
   1.815 + *
   1.816 + *  List the physical contents of a Unix
   1.817 + *  .tar file into the JAR linked list.
   1.818 + *
   1.819 + */
   1.820 +static int 
   1.821 +jar_listtar(JAR *jar, JAR_FILE fp)
   1.822 +{
   1.823 +    char *s;
   1.824 +    JAR_Physical *phy;
   1.825 +    long pos = 0L;
   1.826 +    long sz, mode;
   1.827 +    time_t when;
   1.828 +    union TarEntry tarball;
   1.829 +
   1.830 +    while (1) {
   1.831 +	JAR_FSEEK (fp, pos, (PRSeekWhence)0);
   1.832 +
   1.833 +	if (JAR_FREAD (fp, &tarball, sizeof tarball) < sizeof tarball)
   1.834 +	    break;
   1.835 +
   1.836 +	if (!*tarball.val.filename)
   1.837 +	    break;
   1.838 +
   1.839 +	when = octalToLong (tarball.val.time);
   1.840 +	sz   = octalToLong (tarball.val.size);
   1.841 +	mode = octalToLong (tarball.val.mode);
   1.842 +
   1.843 +	/* Tag the end of filename */
   1.844 +	s = tarball.val.filename;
   1.845 +	while (*s && *s != ' ') 
   1.846 +	    s++;
   1.847 +	*s = 0;
   1.848 +
   1.849 +	/* Add to our linked list */
   1.850 +	phy = PORT_ZNew(JAR_Physical);
   1.851 +	if (phy == NULL)
   1.852 +	    return JAR_ERR_MEMORY;
   1.853 +
   1.854 +	phy->compression = 0;
   1.855 +	phy->offset = pos + sizeof tarball;
   1.856 +	phy->length = sz;
   1.857 +
   1.858 +	ADDITEM(jar->phy, jarTypePhy, tarball.val.filename, phy, 
   1.859 +	        sizeof *phy);
   1.860 +
   1.861 +	/* Advance to next file entry */
   1.862 +	sz = PR_ROUNDUP(sz,sizeof tarball);
   1.863 +	pos += sz + sizeof tarball;
   1.864 +    }
   1.865 +
   1.866 +    return 0;
   1.867 +}
   1.868 +
   1.869 +/*
   1.870 + *  d o s d a t e
   1.871 + *
   1.872 + *  Not used right now, but keep it in here because
   1.873 + *  it will be needed.
   1.874 + *
   1.875 + */
   1.876 +static int 
   1.877 +dosdate(char *date, const char *s)
   1.878 +{
   1.879 +    PRUint32 num = x86ShortToUint32(s);
   1.880 +
   1.881 +    PR_snprintf(date, 9, "%02d-%02d-%02d", ((num >> 5) & 0x0F), (num & 0x1F), 
   1.882 +                                           ((num >> 9) + 80));
   1.883 +    return 0;
   1.884 +}
   1.885 +
   1.886 +/*
   1.887 + *  d o s t i m e
   1.888 + *
   1.889 + *  Not used right now, but keep it in here because
   1.890 + *  it will be needed.
   1.891 + *
   1.892 + */
   1.893 +static int 
   1.894 +dostime (char *time, const char *s)
   1.895 +{
   1.896 +    PRUint32 num = x86ShortToUint32(s);
   1.897 +
   1.898 +    PR_snprintf (time, 6, "%02d:%02d", ((num >> 11) & 0x1F), 
   1.899 +                                       ((num >>  5) & 0x3F));
   1.900 +    return 0;
   1.901 +}
   1.902 +
   1.903 +#ifndef NSS_X86_OR_X64
   1.904 +/*
   1.905 + *  Simulates an x86 (little endian, unaligned) ushort fetch from any address.
   1.906 + */
   1.907 +static PRUint32
   1.908 +x86ShortToUint32(const void * v)
   1.909 +{
   1.910 +    const unsigned char *ii = (const unsigned char *)v;
   1.911 +    PRUint32 ret = (PRUint32)(ii[0]) | ((PRUint32)(ii[1]) << 8);
   1.912 +    return ret;
   1.913 +}
   1.914 +
   1.915 +/*
   1.916 + *  Simulates an x86 (little endian, unaligned) uint fetch from any address.
   1.917 + */
   1.918 +static PRUint32
   1.919 +x86LongToUint32(const void *v)
   1.920 +{
   1.921 +    const unsigned char *ll = (const unsigned char *)v;
   1.922 +    PRUint32 ret;
   1.923 +
   1.924 +    ret = ((((PRUint32)(ll[0])) <<  0) |
   1.925 +	   (((PRUint32)(ll[1])) <<  8) |
   1.926 +	   (((PRUint32)(ll[2])) << 16) |
   1.927 +	   (((PRUint32)(ll[3])) << 24));
   1.928 +    return ret;
   1.929 +}
   1.930 +#endif
   1.931 +
   1.932 +/*
   1.933 + *  ASCII octal to binary long.
   1.934 + *  Used for integer encoding inside tar files.
   1.935 + *
   1.936 + */
   1.937 +static long 
   1.938 +octalToLong(const char *s)
   1.939 +{
   1.940 +    long num = 0L;
   1.941 +
   1.942 +    while (*s == ' ') 
   1.943 +    	s++;
   1.944 +    while (*s >= '0' && *s <= '7') {
   1.945 +	num <<= 3;
   1.946 +	num += *s++ - '0';
   1.947 +    }
   1.948 +    return num;
   1.949 +}
   1.950 +
   1.951 +/*
   1.952 + *  g u e s s _ j a r
   1.953 + *
   1.954 + *  Try to guess what kind of JAR file this is.
   1.955 + *  Maybe tar, maybe zip. Look in the file for magic
   1.956 + *  or at its filename.
   1.957 + *
   1.958 + */
   1.959 +static int 
   1.960 +jar_guess_jar(const char *filename, JAR_FILE fp)
   1.961 +{
   1.962 +    PRInt32 len = PORT_Strlen(filename);
   1.963 +    const char *ext = filename + len - 4; /* 4 for ".tar" */
   1.964 +
   1.965 +    if (len >= 4 && !PL_strcasecmp(ext, ".tar"))
   1.966 +	return jarArchTar;
   1.967 +    return jarArchZip;
   1.968 +}

mercurial