/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* * JARFILE * * Parsing of a Jar file
*/ #define JAR_SIZE 256
/* * 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, constchar *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);
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;
} /* 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, constchar *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);
}
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;
} /* 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 = JAR_extract(jar, path, outpath);
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) {
result = jar_physical_extraction((PRFileDesc *)jar->fp, outpath, phy->offset, phy->length);
} else { /* compression methods other than 8 are unsupported, * but for historical reasons, jar_physical_inflate will be called for
* unsupported compression method constants too. */
result = jar_physical_inflate((PRFileDesc *)jar->fp, outpath,
phy->offset, phy->length,
(unsignedint)phy->compression);
}
#ifdefined(XP_UNIX) 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. *
*/
if ((out = JAR_FOPEN(outpath, "wb")) != NULL) { unsignedlong at = 0;
JAR_FSEEK(fp, offset, (PRSeekWhence)0); while (at < length) { long 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 */
if (JAR_FREAD(fp, inbuf, chunk) != chunk) { /* incomplete read */
JAR_FCLOSE(out);
status = JAR_ERR_CORRUPT; break;
}
at += chunk; if (at == length) { /* add an extra dummy byte at the end */
inbuf[chunk++] = 0xDD;
}
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)) { unsignedlong prev_total = zs.total_out; unsignedlong ochunk;
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 */
JAR_FCLOSE(out);
status = JAR_ERR_CORRUPT; break;
}
ochunk = zs.total_out - prev_total; if (JAR_FWRITE(out, outbuf, ochunk) < (long)ochunk) { /* most likely a disk full error */
status = JAR_ERR_DISK; break;
} if (status == Z_STREAM_END) break;
} if (status != Z_OK) { break;
}
}
JAR_FCLOSE(out);
status2 = inflateEnd(&zs); if (status == Z_OK) {
status = status2;
}
} else { /* error opening output file */
status = JAR_ERR_DISK;
}
loser: if (inbuf) {
PORT_Free(inbuf);
} if (outbuf) {
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. *
*/ staticint
jar_inflate_memory(unsignedint method, long *length, long expected_out_len, char **data)
{ char *inbuf = *data; char *outbuf = (char *)PORT_ZAlloc(expected_out_len); long insz = *length; int status;
z_stream zs;
status = inflate(&zs, Z_FINISH); if (status != Z_OK && status != Z_STREAM_END) { /* error during deflation */
PORT_Free(outbuf); return JAR_ERR_GENERAL;
}
status = inflateEnd(&zs); if (status != Z_OK) { /* error during deflation */
PORT_Free(outbuf); 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. *
*/ staticint
jar_verify_extract(JAR *jar, char *path, char *physical_path)
{ int status;
JAR_Digest dig;
PORT_Memset(&dig, 0, sizeof dig);
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)
{
ZZLink *link;
ZZList *list = jar->phy;
if (ZZ_ListEmpty(list)) return NULL;
for (link = ZZ_ListHead(list);
!ZZ_ListIterDone(list, link);
link = link->next) {
JAR_Item *it = link->thing;
/* * 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. *
*/ staticint
jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp)
{ int status, signatures;
if (format != jarArchZip && format != jarArchTar) return JAR_ERR_CORRUPT;
if ((status = jar_extract_mf(jar, format, fp, "mf")) < 0) return status; if (!status) return JAR_ERR_ORDER; if ((status = jar_extract_mf(jar, format, fp, "sf")) < 0) return status; if (!status) return JAR_ERR_ORDER; if ((status = jar_extract_mf(jar, format, fp, "rsa")) < 0) return status;
signatures = status; if ((status = jar_extract_mf(jar, format, fp, "dsa")) < 0) return status; if (!(signatures += status)) return JAR_ERR_SIG; 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. *
*/ staticint
jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext)
{
ZZLink *link;
ZZList *list = jar->phy; int ret = 0;
if (ZZ_ListEmpty(list)) return JAR_ERR_PNF;
for (link = ZZ_ListHead(list);
ret >= 0 && !ZZ_ListIterDone(list, link);
link = link->next) {
JAR_Item *it = link->thing;
if (it->type == jarTypePhy &&
!PORT_Strncmp(it->pathname, "META-INF", 8)) {
JAR_Physical *phy = (JAR_Physical *)it->data; char *fn = it->pathname + 8; char *e; char *manifest; long length; int num, status;
if (PORT_Strlen(it->pathname) < 8) continue;
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 || phy->length > 0xFFFF) { /* manifest files cannot be zero length or too big! */ /* the 0xFFFF limit is per J2SE SDK */ return JAR_ERR_CORRUPT;
}
/* Read in the manifest and parse it */ /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */
manifest = (char *)PORT_ZAlloc(phy->length + 1); if (!manifest) return JAR_ERR_MEMORY;
/* * 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 *
*/ staticint
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. *
*/ staticint
jar_listzip(JAR *jar, JAR_FILE fp)
{
ZZLink *ent;
JAR_Item *it = NULL;
JAR_Physical *phy = NULL; struct ZipLocal *Local = PORT_ZNew(struct ZipLocal); struct ZipCentral *Central = PORT_ZNew(struct ZipCentral); struct ZipEnd *End = PORT_ZNew(struct ZipEnd);
int err = 0; long pos = 0L; unsignedint compression; unsignedint filename_len, extra_len;
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 = PORT_ZNew(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 = x86ShortToUint32(Local->method);
phy->compression = (compression <= 255) ? compression : 222; /* XXX 222 is bad magic. */
#ifdefined(XP_UNIX) /* with unix we need to locate any bits from
the protection mask in the external attributes. */
attr = Central->external_attributes[2]; /* magic */ if (attr) { /* we have to read the filename, again */
filename_len = x86ShortToUint32(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 += sizeof(struct ZipCentral) +
x86ShortToUint32(Central->filename_len) +
x86ShortToUint32(Central->commentfield_len) +
x86ShortToUint32(Central->extrafield_len);
} elseif (sigVal == ESIG) { if (JAR_FREAD(fp, End, sizeof *End) != sizeof *End) {
err = JAR_ERR_CORRUPT; goto loser;
} break;
} else { /* garbage in archive */
err = JAR_ERR_CORRUPT; goto loser;
}
}
loser: if (Local)
PORT_Free(Local); if (phy && it == NULL)
PORT_Free(phy); 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. *
*/ staticint
jar_listtar(JAR *jar, JAR_FILE fp)
{ char *s;
JAR_Physical *phy; long pos = 0L; long sz; union TarEntry tarball;
while (1) {
JAR_FSEEK(fp, pos, (PRSeekWhence)0);
if (JAR_FREAD(fp, &tarball, sizeof tarball) < sizeof tarball) break;
if (!*tarball.val.filename) break;
sz = octalToLong(tarball.val.size);
/* Tag the end of filename */
s = tarball.val.filename; while (*s && *s != ' ')
s++;
*s = 0;
/* Add to our linked list */
phy = PORT_ZNew(JAR_Physical); if (phy == NULL) return JAR_ERR_MEMORY;
/* Advance to next file entry */
sz = PR_ROUNDUP(sz, sizeof tarball);
pos += sz + sizeof tarball;
}
return 0;
}
/* * d o s d a t e * * Not used right now, but keep it in here because * it will be needed. *
*/ staticint
dosdate(char *date, constchar *s)
{
PRUint32 num = x86ShortToUint32(s);
/* * d o s t i m e * * Not used right now, but keep it in here because * it will be needed. *
*/ staticint
dostime(char *time, constchar *s)
{
PRUint32 num = x86ShortToUint32(s);
/* * ASCII octal to binary long. * Used for integer encoding inside tar files. *
*/ staticlong
octalToLong(constchar *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. *
*/ staticint
jar_guess_jar(constchar *filename, JAR_FILE fp)
{
PRInt32 len = PORT_Strlen(filename); constchar *ext = filename + len - 4; /* 4 for ".tar" */
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.