Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
michael@0 | 6 | * JARFILE |
michael@0 | 7 | * |
michael@0 | 8 | * Parsing of a Jar file |
michael@0 | 9 | */ |
michael@0 | 10 | #define JAR_SIZE 256 |
michael@0 | 11 | |
michael@0 | 12 | #include "jar.h" |
michael@0 | 13 | #include "jarint.h" |
michael@0 | 14 | #include "jarfile.h" |
michael@0 | 15 | |
michael@0 | 16 | /* commercial compression */ |
michael@0 | 17 | #include "jzlib.h" |
michael@0 | 18 | |
michael@0 | 19 | #if defined(XP_UNIX) || defined(XP_BEOS) |
michael@0 | 20 | #include "sys/stat.h" |
michael@0 | 21 | #endif |
michael@0 | 22 | |
michael@0 | 23 | #include "sechash.h" /* for HASH_GetHashObject() */ |
michael@0 | 24 | |
michael@0 | 25 | PR_STATIC_ASSERT(46 == sizeof(struct ZipCentral)); |
michael@0 | 26 | PR_STATIC_ASSERT(30 == sizeof(struct ZipLocal)); |
michael@0 | 27 | PR_STATIC_ASSERT(22 == sizeof(struct ZipEnd)); |
michael@0 | 28 | PR_STATIC_ASSERT(512 == sizeof(union TarEntry)); |
michael@0 | 29 | |
michael@0 | 30 | /* extracting */ |
michael@0 | 31 | static int |
michael@0 | 32 | jar_guess_jar(const char *filename, JAR_FILE fp); |
michael@0 | 33 | |
michael@0 | 34 | static int |
michael@0 | 35 | jar_inflate_memory(unsigned int method, long *length, long expected_out_len, |
michael@0 | 36 | char **data); |
michael@0 | 37 | |
michael@0 | 38 | static int |
michael@0 | 39 | jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length); |
michael@0 | 40 | |
michael@0 | 41 | static int |
michael@0 | 42 | jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, |
michael@0 | 43 | unsigned int method); |
michael@0 | 44 | |
michael@0 | 45 | static int |
michael@0 | 46 | jar_verify_extract(JAR *jar, char *path, char *physical_path); |
michael@0 | 47 | |
michael@0 | 48 | static JAR_Physical * |
michael@0 | 49 | jar_get_physical(JAR *jar, char *pathname); |
michael@0 | 50 | |
michael@0 | 51 | static int |
michael@0 | 52 | jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp); |
michael@0 | 53 | |
michael@0 | 54 | static int |
michael@0 | 55 | jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext); |
michael@0 | 56 | |
michael@0 | 57 | |
michael@0 | 58 | /* indexing */ |
michael@0 | 59 | static int |
michael@0 | 60 | jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp); |
michael@0 | 61 | |
michael@0 | 62 | static int |
michael@0 | 63 | jar_listtar(JAR *jar, JAR_FILE fp); |
michael@0 | 64 | |
michael@0 | 65 | static int |
michael@0 | 66 | jar_listzip(JAR *jar, JAR_FILE fp); |
michael@0 | 67 | |
michael@0 | 68 | |
michael@0 | 69 | /* conversions */ |
michael@0 | 70 | static int |
michael@0 | 71 | dosdate(char *date, const char *s); |
michael@0 | 72 | |
michael@0 | 73 | static int |
michael@0 | 74 | dostime(char *time, const char *s); |
michael@0 | 75 | |
michael@0 | 76 | #ifdef NSS_X86_OR_X64 |
michael@0 | 77 | #define x86ShortToUint32(ii) ((const PRUint32)*((const PRUint16 *)(ii))) |
michael@0 | 78 | #define x86LongToUint32(ii) (*(const PRUint32 *)(ii)) |
michael@0 | 79 | #else |
michael@0 | 80 | static PRUint32 |
michael@0 | 81 | x86ShortToUint32(const void *ii); |
michael@0 | 82 | |
michael@0 | 83 | static PRUint32 |
michael@0 | 84 | x86LongToUint32(const void *ll); |
michael@0 | 85 | #endif |
michael@0 | 86 | |
michael@0 | 87 | static long |
michael@0 | 88 | octalToLong(const char *s); |
michael@0 | 89 | |
michael@0 | 90 | /* |
michael@0 | 91 | * J A R _ p a s s _ a r c h i v e |
michael@0 | 92 | * |
michael@0 | 93 | * For use by naive clients. Slam an entire archive file |
michael@0 | 94 | * into this function. We extract manifests, parse, index |
michael@0 | 95 | * the archive file, and do whatever nastiness. |
michael@0 | 96 | * |
michael@0 | 97 | */ |
michael@0 | 98 | int |
michael@0 | 99 | JAR_pass_archive(JAR *jar, jarArch format, char *filename, const char *url) |
michael@0 | 100 | { |
michael@0 | 101 | JAR_FILE fp; |
michael@0 | 102 | int status = 0; |
michael@0 | 103 | |
michael@0 | 104 | if (filename == NULL) |
michael@0 | 105 | return JAR_ERR_GENERAL; |
michael@0 | 106 | |
michael@0 | 107 | if ((fp = JAR_FOPEN (filename, "rb")) != NULL) { |
michael@0 | 108 | if (format == jarArchGuess) |
michael@0 | 109 | format = (jarArch)jar_guess_jar (filename, fp); |
michael@0 | 110 | |
michael@0 | 111 | jar->format = format; |
michael@0 | 112 | jar->url = url ? PORT_Strdup (url) : NULL; |
michael@0 | 113 | jar->filename = PORT_Strdup (filename); |
michael@0 | 114 | |
michael@0 | 115 | status = jar_gen_index (jar, format, fp); |
michael@0 | 116 | if (status == 0) |
michael@0 | 117 | status = jar_extract_manifests (jar, format, fp); |
michael@0 | 118 | |
michael@0 | 119 | JAR_FCLOSE (fp); |
michael@0 | 120 | if (status < 0) |
michael@0 | 121 | return status; |
michael@0 | 122 | |
michael@0 | 123 | /* people were expecting it this way */ |
michael@0 | 124 | return jar->valid; |
michael@0 | 125 | } |
michael@0 | 126 | /* file not found */ |
michael@0 | 127 | return JAR_ERR_FNF; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | /* |
michael@0 | 131 | * J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d |
michael@0 | 132 | * |
michael@0 | 133 | * Same as JAR_pass_archive, but doesn't parse signatures. |
michael@0 | 134 | * |
michael@0 | 135 | */ |
michael@0 | 136 | int |
michael@0 | 137 | JAR_pass_archive_unverified(JAR *jar, jarArch format, char *filename, |
michael@0 | 138 | const char *url) |
michael@0 | 139 | { |
michael@0 | 140 | JAR_FILE fp; |
michael@0 | 141 | int status = 0; |
michael@0 | 142 | |
michael@0 | 143 | if (filename == NULL) { |
michael@0 | 144 | return JAR_ERR_GENERAL; |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | if ((fp = JAR_FOPEN (filename, "rb")) != NULL) { |
michael@0 | 148 | if (format == jarArchGuess) { |
michael@0 | 149 | format = (jarArch)jar_guess_jar (filename, fp); |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | jar->format = format; |
michael@0 | 153 | jar->url = url ? PORT_Strdup (url) : NULL; |
michael@0 | 154 | jar->filename = PORT_Strdup (filename); |
michael@0 | 155 | |
michael@0 | 156 | status = jar_gen_index (jar, format, fp); |
michael@0 | 157 | if (status == 0) { |
michael@0 | 158 | status = jar_extract_mf(jar, format, fp, "mf"); |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | JAR_FCLOSE (fp); |
michael@0 | 162 | if (status < 0) { |
michael@0 | 163 | return status; |
michael@0 | 164 | } |
michael@0 | 165 | |
michael@0 | 166 | /* people were expecting it this way */ |
michael@0 | 167 | return jar->valid; |
michael@0 | 168 | } |
michael@0 | 169 | /* file not found */ |
michael@0 | 170 | return JAR_ERR_FNF; |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | /* |
michael@0 | 174 | * J A R _ v e r i f i e d _ e x t r a c t |
michael@0 | 175 | * |
michael@0 | 176 | * Optimization: keep a file descriptor open |
michael@0 | 177 | * inside the JAR structure, so we don't have to |
michael@0 | 178 | * open the file 25 times to run java. |
michael@0 | 179 | * |
michael@0 | 180 | */ |
michael@0 | 181 | |
michael@0 | 182 | int |
michael@0 | 183 | JAR_verified_extract(JAR *jar, char *path, char *outpath) |
michael@0 | 184 | { |
michael@0 | 185 | int status = JAR_extract (jar, path, outpath); |
michael@0 | 186 | |
michael@0 | 187 | if (status >= 0) |
michael@0 | 188 | return jar_verify_extract(jar, path, outpath); |
michael@0 | 189 | return status; |
michael@0 | 190 | } |
michael@0 | 191 | |
michael@0 | 192 | int |
michael@0 | 193 | JAR_extract(JAR *jar, char *path, char *outpath) |
michael@0 | 194 | { |
michael@0 | 195 | int result; |
michael@0 | 196 | JAR_Physical *phy; |
michael@0 | 197 | |
michael@0 | 198 | if (jar->fp == NULL && jar->filename) { |
michael@0 | 199 | jar->fp = (FILE*)JAR_FOPEN (jar->filename, "rb"); |
michael@0 | 200 | } |
michael@0 | 201 | if (jar->fp == NULL) { |
michael@0 | 202 | /* file not found */ |
michael@0 | 203 | return JAR_ERR_FNF; |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | phy = jar_get_physical (jar, path); |
michael@0 | 207 | if (phy) { |
michael@0 | 208 | if (phy->compression != 0 && phy->compression != 8) { |
michael@0 | 209 | /* unsupported compression method */ |
michael@0 | 210 | result = JAR_ERR_CORRUPT; |
michael@0 | 211 | } |
michael@0 | 212 | if (phy->compression == 0) { |
michael@0 | 213 | result = jar_physical_extraction |
michael@0 | 214 | ((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length); |
michael@0 | 215 | } else { |
michael@0 | 216 | result = jar_physical_inflate((PRFileDesc*)jar->fp, outpath, |
michael@0 | 217 | phy->offset, phy->length, |
michael@0 | 218 | (unsigned int) phy->compression); |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | #if defined(XP_UNIX) || defined(XP_BEOS) |
michael@0 | 222 | if (phy->mode) |
michael@0 | 223 | chmod (outpath, 0400 | (mode_t) phy->mode); |
michael@0 | 224 | #endif |
michael@0 | 225 | } else { |
michael@0 | 226 | /* pathname not found in archive */ |
michael@0 | 227 | result = JAR_ERR_PNF; |
michael@0 | 228 | } |
michael@0 | 229 | return result; |
michael@0 | 230 | } |
michael@0 | 231 | |
michael@0 | 232 | /* |
michael@0 | 233 | * p h y s i c a l _ e x t r a c t i o n |
michael@0 | 234 | * |
michael@0 | 235 | * This needs to be done in chunks of say 32k, instead of |
michael@0 | 236 | * in one bulk calloc. (Necessary under Win16 platform.) |
michael@0 | 237 | * This is done for uncompressed entries only. |
michael@0 | 238 | * |
michael@0 | 239 | */ |
michael@0 | 240 | |
michael@0 | 241 | #define CHUNK 32768 |
michael@0 | 242 | |
michael@0 | 243 | static int |
michael@0 | 244 | jar_physical_extraction(JAR_FILE fp, char *outpath, long offset, long length) |
michael@0 | 245 | { |
michael@0 | 246 | JAR_FILE out; |
michael@0 | 247 | char *buffer = (char *)PORT_ZAlloc(CHUNK); |
michael@0 | 248 | int status = 0; |
michael@0 | 249 | |
michael@0 | 250 | if (buffer == NULL) |
michael@0 | 251 | return JAR_ERR_MEMORY; |
michael@0 | 252 | |
michael@0 | 253 | if ((out = JAR_FOPEN (outpath, "wb")) != NULL) { |
michael@0 | 254 | long at = 0; |
michael@0 | 255 | |
michael@0 | 256 | JAR_FSEEK (fp, offset, (PRSeekWhence)0); |
michael@0 | 257 | while (at < length) { |
michael@0 | 258 | long chunk = (at + CHUNK <= length) ? CHUNK : length - at; |
michael@0 | 259 | if (JAR_FREAD (fp, buffer, chunk) != chunk) { |
michael@0 | 260 | status = JAR_ERR_DISK; |
michael@0 | 261 | break; |
michael@0 | 262 | } |
michael@0 | 263 | at += chunk; |
michael@0 | 264 | if (JAR_FWRITE (out, buffer, chunk) < chunk) { |
michael@0 | 265 | /* most likely a disk full error */ |
michael@0 | 266 | status = JAR_ERR_DISK; |
michael@0 | 267 | break; |
michael@0 | 268 | } |
michael@0 | 269 | } |
michael@0 | 270 | JAR_FCLOSE (out); |
michael@0 | 271 | } else { |
michael@0 | 272 | /* error opening output file */ |
michael@0 | 273 | status = JAR_ERR_DISK; |
michael@0 | 274 | } |
michael@0 | 275 | PORT_Free (buffer); |
michael@0 | 276 | return status; |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | /* |
michael@0 | 280 | * j a r _ p h y s i c a l _ i n f l a t e |
michael@0 | 281 | * |
michael@0 | 282 | * Inflate a range of bytes in a file, writing the inflated |
michael@0 | 283 | * result to "outpath". Chunk based. |
michael@0 | 284 | * |
michael@0 | 285 | */ |
michael@0 | 286 | /* input and output chunks differ, assume 4x compression */ |
michael@0 | 287 | |
michael@0 | 288 | #define ICHUNK 8192 |
michael@0 | 289 | #define OCHUNK 32768 |
michael@0 | 290 | |
michael@0 | 291 | static int |
michael@0 | 292 | jar_physical_inflate(JAR_FILE fp, char *outpath, long offset, long length, |
michael@0 | 293 | unsigned int method) |
michael@0 | 294 | { |
michael@0 | 295 | char *inbuf, *outbuf; |
michael@0 | 296 | int status = 0; |
michael@0 | 297 | z_stream zs; |
michael@0 | 298 | JAR_FILE out; |
michael@0 | 299 | |
michael@0 | 300 | /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ |
michael@0 | 301 | if ((inbuf = (char *)PORT_ZAlloc(ICHUNK + 1)) == NULL) |
michael@0 | 302 | return JAR_ERR_MEMORY; |
michael@0 | 303 | |
michael@0 | 304 | if ((outbuf = (char *)PORT_ZAlloc(OCHUNK)) == NULL) { |
michael@0 | 305 | PORT_Free (inbuf); |
michael@0 | 306 | return JAR_ERR_MEMORY; |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | PORT_Memset (&zs, 0, sizeof (zs)); |
michael@0 | 310 | status = inflateInit2 (&zs, -MAX_WBITS); |
michael@0 | 311 | if (status != Z_OK) { |
michael@0 | 312 | PORT_Free (inbuf); |
michael@0 | 313 | PORT_Free (outbuf); |
michael@0 | 314 | return JAR_ERR_GENERAL; |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | if ((out = JAR_FOPEN (outpath, "wb")) != NULL) { |
michael@0 | 318 | long at = 0; |
michael@0 | 319 | |
michael@0 | 320 | JAR_FSEEK (fp, offset, (PRSeekWhence)0); |
michael@0 | 321 | while (at < length) { |
michael@0 | 322 | long chunk = (at + ICHUNK <= length) ? ICHUNK : length - at; |
michael@0 | 323 | unsigned long tin; |
michael@0 | 324 | |
michael@0 | 325 | if (JAR_FREAD (fp, inbuf, chunk) != chunk) { |
michael@0 | 326 | /* incomplete read */ |
michael@0 | 327 | JAR_FCLOSE (out); |
michael@0 | 328 | PORT_Free (inbuf); |
michael@0 | 329 | PORT_Free (outbuf); |
michael@0 | 330 | return JAR_ERR_CORRUPT; |
michael@0 | 331 | } |
michael@0 | 332 | at += chunk; |
michael@0 | 333 | if (at == length) { |
michael@0 | 334 | /* add an extra dummy byte at the end */ |
michael@0 | 335 | inbuf[chunk++] = 0xDD; |
michael@0 | 336 | } |
michael@0 | 337 | zs.next_in = (Bytef *) inbuf; |
michael@0 | 338 | zs.avail_in = chunk; |
michael@0 | 339 | zs.avail_out = OCHUNK; |
michael@0 | 340 | tin = zs.total_in; |
michael@0 | 341 | while ((zs.total_in - tin < chunk) || (zs.avail_out == 0)) { |
michael@0 | 342 | unsigned long prev_total = zs.total_out; |
michael@0 | 343 | unsigned long ochunk; |
michael@0 | 344 | |
michael@0 | 345 | zs.next_out = (Bytef *) outbuf; |
michael@0 | 346 | zs.avail_out = OCHUNK; |
michael@0 | 347 | status = inflate (&zs, Z_NO_FLUSH); |
michael@0 | 348 | if (status != Z_OK && status != Z_STREAM_END) { |
michael@0 | 349 | /* error during decompression */ |
michael@0 | 350 | JAR_FCLOSE (out); |
michael@0 | 351 | PORT_Free (inbuf); |
michael@0 | 352 | PORT_Free (outbuf); |
michael@0 | 353 | return JAR_ERR_CORRUPT; |
michael@0 | 354 | } |
michael@0 | 355 | ochunk = zs.total_out - prev_total; |
michael@0 | 356 | if (JAR_FWRITE (out, outbuf, ochunk) < ochunk) { |
michael@0 | 357 | /* most likely a disk full error */ |
michael@0 | 358 | status = JAR_ERR_DISK; |
michael@0 | 359 | break; |
michael@0 | 360 | } |
michael@0 | 361 | if (status == Z_STREAM_END) |
michael@0 | 362 | break; |
michael@0 | 363 | } |
michael@0 | 364 | } |
michael@0 | 365 | JAR_FCLOSE (out); |
michael@0 | 366 | status = inflateEnd (&zs); |
michael@0 | 367 | } else { |
michael@0 | 368 | /* error opening output file */ |
michael@0 | 369 | status = JAR_ERR_DISK; |
michael@0 | 370 | } |
michael@0 | 371 | PORT_Free (inbuf); |
michael@0 | 372 | PORT_Free (outbuf); |
michael@0 | 373 | return status; |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | /* |
michael@0 | 377 | * j a r _ i n f l a t e _ m e m o r y |
michael@0 | 378 | * |
michael@0 | 379 | * Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd, |
michael@0 | 380 | * and thus appears to operate inplace to the caller. |
michael@0 | 381 | * |
michael@0 | 382 | */ |
michael@0 | 383 | static int |
michael@0 | 384 | jar_inflate_memory(unsigned int method, long *length, long expected_out_len, |
michael@0 | 385 | char **data) |
michael@0 | 386 | { |
michael@0 | 387 | char *inbuf = *data; |
michael@0 | 388 | char *outbuf = (char*)PORT_ZAlloc(expected_out_len); |
michael@0 | 389 | long insz = *length; |
michael@0 | 390 | int status; |
michael@0 | 391 | z_stream zs; |
michael@0 | 392 | |
michael@0 | 393 | if (outbuf == NULL) |
michael@0 | 394 | return JAR_ERR_MEMORY; |
michael@0 | 395 | |
michael@0 | 396 | PORT_Memset(&zs, 0, sizeof zs); |
michael@0 | 397 | status = inflateInit2 (&zs, -MAX_WBITS); |
michael@0 | 398 | if (status < 0) { |
michael@0 | 399 | /* error initializing zlib stream */ |
michael@0 | 400 | PORT_Free (outbuf); |
michael@0 | 401 | return JAR_ERR_GENERAL; |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | zs.next_in = (Bytef *) inbuf; |
michael@0 | 405 | zs.next_out = (Bytef *) outbuf; |
michael@0 | 406 | zs.avail_in = insz; |
michael@0 | 407 | zs.avail_out = expected_out_len; |
michael@0 | 408 | |
michael@0 | 409 | status = inflate (&zs, Z_FINISH); |
michael@0 | 410 | if (status != Z_OK && status != Z_STREAM_END) { |
michael@0 | 411 | /* error during deflation */ |
michael@0 | 412 | PORT_Free (outbuf); |
michael@0 | 413 | return JAR_ERR_GENERAL; |
michael@0 | 414 | } |
michael@0 | 415 | |
michael@0 | 416 | status = inflateEnd (&zs); |
michael@0 | 417 | if (status != Z_OK) { |
michael@0 | 418 | /* error during deflation */ |
michael@0 | 419 | PORT_Free (outbuf); |
michael@0 | 420 | return JAR_ERR_GENERAL; |
michael@0 | 421 | } |
michael@0 | 422 | PORT_Free(*data); |
michael@0 | 423 | *data = outbuf; |
michael@0 | 424 | *length = zs.total_out; |
michael@0 | 425 | return 0; |
michael@0 | 426 | } |
michael@0 | 427 | |
michael@0 | 428 | /* |
michael@0 | 429 | * v e r i f y _ e x t r a c t |
michael@0 | 430 | * |
michael@0 | 431 | * Validate signature on the freshly extracted file. |
michael@0 | 432 | * |
michael@0 | 433 | */ |
michael@0 | 434 | static int |
michael@0 | 435 | jar_verify_extract(JAR *jar, char *path, char *physical_path) |
michael@0 | 436 | { |
michael@0 | 437 | int status; |
michael@0 | 438 | JAR_Digest dig; |
michael@0 | 439 | |
michael@0 | 440 | PORT_Memset (&dig, 0, sizeof dig); |
michael@0 | 441 | status = JAR_digest_file (physical_path, &dig); |
michael@0 | 442 | if (!status) |
michael@0 | 443 | status = JAR_verify_digest (jar, path, &dig); |
michael@0 | 444 | return status; |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | /* |
michael@0 | 448 | * g e t _ p h y s i c a l |
michael@0 | 449 | * |
michael@0 | 450 | * Let's get physical. |
michael@0 | 451 | * Obtains the offset and length of this file in the jar file. |
michael@0 | 452 | * |
michael@0 | 453 | */ |
michael@0 | 454 | static JAR_Physical * |
michael@0 | 455 | jar_get_physical(JAR *jar, char *pathname) |
michael@0 | 456 | { |
michael@0 | 457 | ZZLink *link; |
michael@0 | 458 | ZZList *list = jar->phy; |
michael@0 | 459 | |
michael@0 | 460 | if (ZZ_ListEmpty (list)) |
michael@0 | 461 | return NULL; |
michael@0 | 462 | |
michael@0 | 463 | for (link = ZZ_ListHead (list); |
michael@0 | 464 | !ZZ_ListIterDone (list, link); |
michael@0 | 465 | link = link->next) { |
michael@0 | 466 | JAR_Item *it = link->thing; |
michael@0 | 467 | |
michael@0 | 468 | if (it->type == jarTypePhy && |
michael@0 | 469 | it->pathname && !PORT_Strcmp (it->pathname, pathname)) { |
michael@0 | 470 | JAR_Physical *phy = (JAR_Physical *)it->data; |
michael@0 | 471 | return phy; |
michael@0 | 472 | } |
michael@0 | 473 | } |
michael@0 | 474 | return NULL; |
michael@0 | 475 | } |
michael@0 | 476 | |
michael@0 | 477 | /* |
michael@0 | 478 | * j a r _ e x t r a c t _ m a n i f e s t s |
michael@0 | 479 | * |
michael@0 | 480 | * Extract the manifest files and parse them, |
michael@0 | 481 | * from an open archive file whose contents are known. |
michael@0 | 482 | * |
michael@0 | 483 | */ |
michael@0 | 484 | static int |
michael@0 | 485 | jar_extract_manifests(JAR *jar, jarArch format, JAR_FILE fp) |
michael@0 | 486 | { |
michael@0 | 487 | int status, signatures; |
michael@0 | 488 | |
michael@0 | 489 | if (format != jarArchZip && format != jarArchTar) |
michael@0 | 490 | return JAR_ERR_CORRUPT; |
michael@0 | 491 | |
michael@0 | 492 | if ((status = jar_extract_mf (jar, format, fp, "mf")) < 0) |
michael@0 | 493 | return status; |
michael@0 | 494 | if (!status) |
michael@0 | 495 | return JAR_ERR_ORDER; |
michael@0 | 496 | if ((status = jar_extract_mf (jar, format, fp, "sf")) < 0) |
michael@0 | 497 | return status; |
michael@0 | 498 | if (!status) |
michael@0 | 499 | return JAR_ERR_ORDER; |
michael@0 | 500 | if ((status = jar_extract_mf (jar, format, fp, "rsa")) < 0) |
michael@0 | 501 | return status; |
michael@0 | 502 | signatures = status; |
michael@0 | 503 | if ((status = jar_extract_mf (jar, format, fp, "dsa")) < 0) |
michael@0 | 504 | return status; |
michael@0 | 505 | if (!(signatures += status)) |
michael@0 | 506 | return JAR_ERR_SIG; |
michael@0 | 507 | return 0; |
michael@0 | 508 | } |
michael@0 | 509 | |
michael@0 | 510 | /* |
michael@0 | 511 | * j a r _ e x t r a c t _ m f |
michael@0 | 512 | * |
michael@0 | 513 | * Extracts manifest files based on an extension, which |
michael@0 | 514 | * should be .MF, .SF, .RSA, etc. Order of the files is now no |
michael@0 | 515 | * longer important when zipping jar files. |
michael@0 | 516 | * |
michael@0 | 517 | */ |
michael@0 | 518 | static int |
michael@0 | 519 | jar_extract_mf(JAR *jar, jarArch format, JAR_FILE fp, char *ext) |
michael@0 | 520 | { |
michael@0 | 521 | ZZLink *link; |
michael@0 | 522 | ZZList *list = jar->phy; |
michael@0 | 523 | int ret = 0; |
michael@0 | 524 | |
michael@0 | 525 | if (ZZ_ListEmpty (list)) |
michael@0 | 526 | return JAR_ERR_PNF; |
michael@0 | 527 | |
michael@0 | 528 | for (link = ZZ_ListHead (list); |
michael@0 | 529 | ret >= 0 && !ZZ_ListIterDone (list, link); |
michael@0 | 530 | link = link->next) { |
michael@0 | 531 | JAR_Item *it = link->thing; |
michael@0 | 532 | |
michael@0 | 533 | if (it->type == jarTypePhy && |
michael@0 | 534 | !PORT_Strncmp (it->pathname, "META-INF", 8)) |
michael@0 | 535 | { |
michael@0 | 536 | JAR_Physical *phy = (JAR_Physical *) it->data; |
michael@0 | 537 | char *fn = it->pathname + 8; |
michael@0 | 538 | char *e; |
michael@0 | 539 | char *manifest; |
michael@0 | 540 | long length; |
michael@0 | 541 | int num, status; |
michael@0 | 542 | |
michael@0 | 543 | if (PORT_Strlen (it->pathname) < 8) |
michael@0 | 544 | continue; |
michael@0 | 545 | |
michael@0 | 546 | if (*fn == '/' || *fn == '\\') |
michael@0 | 547 | fn++; |
michael@0 | 548 | if (*fn == 0) { |
michael@0 | 549 | /* just a directory entry */ |
michael@0 | 550 | continue; |
michael@0 | 551 | } |
michael@0 | 552 | |
michael@0 | 553 | /* skip to extension */ |
michael@0 | 554 | for (e = fn; *e && *e != '.'; e++) |
michael@0 | 555 | /* yip */ ; |
michael@0 | 556 | |
michael@0 | 557 | /* and skip dot */ |
michael@0 | 558 | if (*e == '.') |
michael@0 | 559 | e++; |
michael@0 | 560 | if (PORT_Strcasecmp (ext, e)) { |
michael@0 | 561 | /* not the right extension */ |
michael@0 | 562 | continue; |
michael@0 | 563 | } |
michael@0 | 564 | if (phy->length == 0 || phy->length > 0xFFFF) { |
michael@0 | 565 | /* manifest files cannot be zero length or too big! */ |
michael@0 | 566 | /* the 0xFFFF limit is per J2SE SDK */ |
michael@0 | 567 | return JAR_ERR_CORRUPT; |
michael@0 | 568 | } |
michael@0 | 569 | |
michael@0 | 570 | /* Read in the manifest and parse it */ |
michael@0 | 571 | /* Raw inflate in zlib 1.1.4 needs an extra dummy byte at the end */ |
michael@0 | 572 | manifest = (char *)PORT_ZAlloc(phy->length + 1); |
michael@0 | 573 | if (!manifest) |
michael@0 | 574 | return JAR_ERR_MEMORY; |
michael@0 | 575 | |
michael@0 | 576 | JAR_FSEEK (fp, phy->offset, (PRSeekWhence)0); |
michael@0 | 577 | num = JAR_FREAD (fp, manifest, phy->length); |
michael@0 | 578 | if (num != phy->length) { |
michael@0 | 579 | /* corrupt archive file */ |
michael@0 | 580 | PORT_Free (manifest); |
michael@0 | 581 | return JAR_ERR_CORRUPT; |
michael@0 | 582 | } |
michael@0 | 583 | |
michael@0 | 584 | if (phy->compression == 8) { |
michael@0 | 585 | length = phy->length; |
michael@0 | 586 | /* add an extra dummy byte at the end */ |
michael@0 | 587 | manifest[length++] = 0xDD; |
michael@0 | 588 | status = jar_inflate_memory((unsigned int)phy->compression, |
michael@0 | 589 | &length, |
michael@0 | 590 | phy->uncompressed_length, |
michael@0 | 591 | &manifest); |
michael@0 | 592 | if (status < 0) { |
michael@0 | 593 | PORT_Free (manifest); |
michael@0 | 594 | return status; |
michael@0 | 595 | } |
michael@0 | 596 | } else if (phy->compression) { |
michael@0 | 597 | /* unsupported compression method */ |
michael@0 | 598 | PORT_Free (manifest); |
michael@0 | 599 | return JAR_ERR_CORRUPT; |
michael@0 | 600 | } else |
michael@0 | 601 | length = phy->length; |
michael@0 | 602 | |
michael@0 | 603 | status = JAR_parse_manifest(jar, manifest, length, |
michael@0 | 604 | it->pathname, "url"); |
michael@0 | 605 | PORT_Free (manifest); |
michael@0 | 606 | if (status < 0) |
michael@0 | 607 | ret = status; |
michael@0 | 608 | else |
michael@0 | 609 | ++ret; |
michael@0 | 610 | } else if (it->type == jarTypePhy) { |
michael@0 | 611 | /* ordinary file */ |
michael@0 | 612 | } |
michael@0 | 613 | } |
michael@0 | 614 | return ret; |
michael@0 | 615 | } |
michael@0 | 616 | |
michael@0 | 617 | /* |
michael@0 | 618 | * j a r _ g e n _ i n d e x |
michael@0 | 619 | * |
michael@0 | 620 | * Generate an index for the various types of |
michael@0 | 621 | * known archive files. Right now .ZIP and .TAR |
michael@0 | 622 | * |
michael@0 | 623 | */ |
michael@0 | 624 | static int |
michael@0 | 625 | jar_gen_index(JAR *jar, jarArch format, JAR_FILE fp) |
michael@0 | 626 | { |
michael@0 | 627 | int result = JAR_ERR_CORRUPT; |
michael@0 | 628 | |
michael@0 | 629 | JAR_FSEEK (fp, 0, (PRSeekWhence)0); |
michael@0 | 630 | switch (format) { |
michael@0 | 631 | case jarArchZip: |
michael@0 | 632 | result = jar_listzip (jar, fp); |
michael@0 | 633 | break; |
michael@0 | 634 | |
michael@0 | 635 | case jarArchTar: |
michael@0 | 636 | result = jar_listtar (jar, fp); |
michael@0 | 637 | break; |
michael@0 | 638 | |
michael@0 | 639 | case jarArchGuess: |
michael@0 | 640 | case jarArchNone: |
michael@0 | 641 | return JAR_ERR_GENERAL; |
michael@0 | 642 | } |
michael@0 | 643 | JAR_FSEEK (fp, 0, (PRSeekWhence)0); |
michael@0 | 644 | return result; |
michael@0 | 645 | } |
michael@0 | 646 | |
michael@0 | 647 | /* |
michael@0 | 648 | * j a r _ l i s t z i p |
michael@0 | 649 | * |
michael@0 | 650 | * List the physical contents of a Phil Katz |
michael@0 | 651 | * style .ZIP file into the JAR linked list. |
michael@0 | 652 | * |
michael@0 | 653 | */ |
michael@0 | 654 | static int |
michael@0 | 655 | jar_listzip(JAR *jar, JAR_FILE fp) |
michael@0 | 656 | { |
michael@0 | 657 | ZZLink *ent; |
michael@0 | 658 | JAR_Item *it; |
michael@0 | 659 | JAR_Physical *phy; |
michael@0 | 660 | struct ZipLocal *Local = PORT_ZNew(struct ZipLocal); |
michael@0 | 661 | struct ZipCentral *Central = PORT_ZNew(struct ZipCentral); |
michael@0 | 662 | struct ZipEnd *End = PORT_ZNew(struct ZipEnd); |
michael@0 | 663 | |
michael@0 | 664 | int err = 0; |
michael@0 | 665 | long pos = 0L; |
michael@0 | 666 | unsigned int compression; |
michael@0 | 667 | unsigned int filename_len, extra_len; |
michael@0 | 668 | |
michael@0 | 669 | char filename[JAR_SIZE]; |
michael@0 | 670 | char date[9], time[9]; |
michael@0 | 671 | char sig[4]; |
michael@0 | 672 | |
michael@0 | 673 | if (!Local || !Central || !End) { |
michael@0 | 674 | /* out of memory */ |
michael@0 | 675 | err = JAR_ERR_MEMORY; |
michael@0 | 676 | goto loser; |
michael@0 | 677 | } |
michael@0 | 678 | |
michael@0 | 679 | while (1) { |
michael@0 | 680 | PRUint32 sigVal; |
michael@0 | 681 | JAR_FSEEK (fp, pos, (PRSeekWhence)0); |
michael@0 | 682 | |
michael@0 | 683 | if (JAR_FREAD(fp, sig, sizeof sig) != sizeof sig) { |
michael@0 | 684 | /* zip file ends prematurely */ |
michael@0 | 685 | err = JAR_ERR_CORRUPT; |
michael@0 | 686 | goto loser; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | JAR_FSEEK (fp, pos, (PRSeekWhence)0); |
michael@0 | 690 | sigVal = x86LongToUint32(sig); |
michael@0 | 691 | if (sigVal == LSIG) { |
michael@0 | 692 | JAR_FREAD (fp, Local, sizeof *Local); |
michael@0 | 693 | |
michael@0 | 694 | filename_len = x86ShortToUint32(Local->filename_len); |
michael@0 | 695 | extra_len = x86ShortToUint32(Local->extrafield_len); |
michael@0 | 696 | if (filename_len >= JAR_SIZE) { |
michael@0 | 697 | /* corrupt zip file */ |
michael@0 | 698 | err = JAR_ERR_CORRUPT; |
michael@0 | 699 | goto loser; |
michael@0 | 700 | } |
michael@0 | 701 | |
michael@0 | 702 | if (JAR_FREAD (fp, filename, filename_len) != filename_len) { |
michael@0 | 703 | /* truncated archive file */ |
michael@0 | 704 | err = JAR_ERR_CORRUPT; |
michael@0 | 705 | goto loser; |
michael@0 | 706 | } |
michael@0 | 707 | filename [filename_len] = 0; |
michael@0 | 708 | /* Add this to our jar chain */ |
michael@0 | 709 | phy = PORT_ZNew(JAR_Physical); |
michael@0 | 710 | if (phy == NULL) { |
michael@0 | 711 | err = JAR_ERR_MEMORY; |
michael@0 | 712 | goto loser; |
michael@0 | 713 | } |
michael@0 | 714 | |
michael@0 | 715 | /* We will index any file that comes our way, but when it comes |
michael@0 | 716 | to actually extraction, compression must be 0 or 8 */ |
michael@0 | 717 | compression = x86ShortToUint32(Local->method); |
michael@0 | 718 | phy->compression = (compression <= 255) ? compression : 222; |
michael@0 | 719 | /* XXX 222 is bad magic. */ |
michael@0 | 720 | |
michael@0 | 721 | phy->offset = pos + (sizeof *Local) + filename_len + extra_len; |
michael@0 | 722 | phy->length = x86LongToUint32(Local->size); |
michael@0 | 723 | phy->uncompressed_length = x86LongToUint32(Local->orglen); |
michael@0 | 724 | |
michael@0 | 725 | dosdate (date, Local->date); |
michael@0 | 726 | dostime (time, Local->time); |
michael@0 | 727 | |
michael@0 | 728 | it = PORT_ZNew(JAR_Item); |
michael@0 | 729 | if (it == NULL) { |
michael@0 | 730 | err = JAR_ERR_MEMORY; |
michael@0 | 731 | goto loser; |
michael@0 | 732 | } |
michael@0 | 733 | |
michael@0 | 734 | it->pathname = PORT_Strdup(filename); |
michael@0 | 735 | it->type = jarTypePhy; |
michael@0 | 736 | it->data = (unsigned char *) phy; |
michael@0 | 737 | it->size = sizeof (JAR_Physical); |
michael@0 | 738 | |
michael@0 | 739 | ent = ZZ_NewLink (it); |
michael@0 | 740 | if (ent == NULL) { |
michael@0 | 741 | err = JAR_ERR_MEMORY; |
michael@0 | 742 | goto loser; |
michael@0 | 743 | } |
michael@0 | 744 | |
michael@0 | 745 | ZZ_AppendLink (jar->phy, ent); |
michael@0 | 746 | pos = phy->offset + phy->length; |
michael@0 | 747 | } else if (sigVal == CSIG) { |
michael@0 | 748 | unsigned int attr = 0; |
michael@0 | 749 | if (JAR_FREAD(fp, Central, sizeof *Central) != sizeof *Central) { |
michael@0 | 750 | /* apparently truncated archive */ |
michael@0 | 751 | err = JAR_ERR_CORRUPT; |
michael@0 | 752 | goto loser; |
michael@0 | 753 | } |
michael@0 | 754 | |
michael@0 | 755 | #if defined(XP_UNIX) || defined(XP_BEOS) |
michael@0 | 756 | /* with unix we need to locate any bits from |
michael@0 | 757 | the protection mask in the external attributes. */ |
michael@0 | 758 | attr = Central->external_attributes [2]; /* magic */ |
michael@0 | 759 | if (attr) { |
michael@0 | 760 | /* we have to read the filename, again */ |
michael@0 | 761 | filename_len = x86ShortToUint32(Central->filename_len); |
michael@0 | 762 | if (filename_len >= JAR_SIZE) { |
michael@0 | 763 | /* corrupt in central directory */ |
michael@0 | 764 | err = JAR_ERR_CORRUPT; |
michael@0 | 765 | goto loser; |
michael@0 | 766 | } |
michael@0 | 767 | |
michael@0 | 768 | if (JAR_FREAD(fp, filename, filename_len) != filename_len) { |
michael@0 | 769 | /* truncated in central directory */ |
michael@0 | 770 | err = JAR_ERR_CORRUPT; |
michael@0 | 771 | goto loser; |
michael@0 | 772 | } |
michael@0 | 773 | filename [filename_len] = 0; |
michael@0 | 774 | |
michael@0 | 775 | /* look up this name again */ |
michael@0 | 776 | phy = jar_get_physical (jar, filename); |
michael@0 | 777 | if (phy) { |
michael@0 | 778 | /* always allow access by self */ |
michael@0 | 779 | phy->mode = 0400 | attr; |
michael@0 | 780 | } |
michael@0 | 781 | } |
michael@0 | 782 | #endif |
michael@0 | 783 | pos += sizeof(struct ZipCentral) |
michael@0 | 784 | + x86ShortToUint32(Central->filename_len) |
michael@0 | 785 | + x86ShortToUint32(Central->commentfield_len) |
michael@0 | 786 | + x86ShortToUint32(Central->extrafield_len); |
michael@0 | 787 | } else if (sigVal == ESIG) { |
michael@0 | 788 | if (JAR_FREAD(fp, End, sizeof *End) != sizeof *End) { |
michael@0 | 789 | err = JAR_ERR_CORRUPT; |
michael@0 | 790 | goto loser; |
michael@0 | 791 | } |
michael@0 | 792 | break; |
michael@0 | 793 | } else { |
michael@0 | 794 | /* garbage in archive */ |
michael@0 | 795 | err = JAR_ERR_CORRUPT; |
michael@0 | 796 | goto loser; |
michael@0 | 797 | } |
michael@0 | 798 | } |
michael@0 | 799 | |
michael@0 | 800 | loser: |
michael@0 | 801 | if (Local) |
michael@0 | 802 | PORT_Free(Local); |
michael@0 | 803 | if (Central) |
michael@0 | 804 | PORT_Free(Central); |
michael@0 | 805 | if (End) |
michael@0 | 806 | PORT_Free(End); |
michael@0 | 807 | return err; |
michael@0 | 808 | } |
michael@0 | 809 | |
michael@0 | 810 | /* |
michael@0 | 811 | * j a r _ l i s t t a r |
michael@0 | 812 | * |
michael@0 | 813 | * List the physical contents of a Unix |
michael@0 | 814 | * .tar file into the JAR linked list. |
michael@0 | 815 | * |
michael@0 | 816 | */ |
michael@0 | 817 | static int |
michael@0 | 818 | jar_listtar(JAR *jar, JAR_FILE fp) |
michael@0 | 819 | { |
michael@0 | 820 | char *s; |
michael@0 | 821 | JAR_Physical *phy; |
michael@0 | 822 | long pos = 0L; |
michael@0 | 823 | long sz, mode; |
michael@0 | 824 | time_t when; |
michael@0 | 825 | union TarEntry tarball; |
michael@0 | 826 | |
michael@0 | 827 | while (1) { |
michael@0 | 828 | JAR_FSEEK (fp, pos, (PRSeekWhence)0); |
michael@0 | 829 | |
michael@0 | 830 | if (JAR_FREAD (fp, &tarball, sizeof tarball) < sizeof tarball) |
michael@0 | 831 | break; |
michael@0 | 832 | |
michael@0 | 833 | if (!*tarball.val.filename) |
michael@0 | 834 | break; |
michael@0 | 835 | |
michael@0 | 836 | when = octalToLong (tarball.val.time); |
michael@0 | 837 | sz = octalToLong (tarball.val.size); |
michael@0 | 838 | mode = octalToLong (tarball.val.mode); |
michael@0 | 839 | |
michael@0 | 840 | /* Tag the end of filename */ |
michael@0 | 841 | s = tarball.val.filename; |
michael@0 | 842 | while (*s && *s != ' ') |
michael@0 | 843 | s++; |
michael@0 | 844 | *s = 0; |
michael@0 | 845 | |
michael@0 | 846 | /* Add to our linked list */ |
michael@0 | 847 | phy = PORT_ZNew(JAR_Physical); |
michael@0 | 848 | if (phy == NULL) |
michael@0 | 849 | return JAR_ERR_MEMORY; |
michael@0 | 850 | |
michael@0 | 851 | phy->compression = 0; |
michael@0 | 852 | phy->offset = pos + sizeof tarball; |
michael@0 | 853 | phy->length = sz; |
michael@0 | 854 | |
michael@0 | 855 | ADDITEM(jar->phy, jarTypePhy, tarball.val.filename, phy, |
michael@0 | 856 | sizeof *phy); |
michael@0 | 857 | |
michael@0 | 858 | /* Advance to next file entry */ |
michael@0 | 859 | sz = PR_ROUNDUP(sz,sizeof tarball); |
michael@0 | 860 | pos += sz + sizeof tarball; |
michael@0 | 861 | } |
michael@0 | 862 | |
michael@0 | 863 | return 0; |
michael@0 | 864 | } |
michael@0 | 865 | |
michael@0 | 866 | /* |
michael@0 | 867 | * d o s d a t e |
michael@0 | 868 | * |
michael@0 | 869 | * Not used right now, but keep it in here because |
michael@0 | 870 | * it will be needed. |
michael@0 | 871 | * |
michael@0 | 872 | */ |
michael@0 | 873 | static int |
michael@0 | 874 | dosdate(char *date, const char *s) |
michael@0 | 875 | { |
michael@0 | 876 | PRUint32 num = x86ShortToUint32(s); |
michael@0 | 877 | |
michael@0 | 878 | PR_snprintf(date, 9, "%02d-%02d-%02d", ((num >> 5) & 0x0F), (num & 0x1F), |
michael@0 | 879 | ((num >> 9) + 80)); |
michael@0 | 880 | return 0; |
michael@0 | 881 | } |
michael@0 | 882 | |
michael@0 | 883 | /* |
michael@0 | 884 | * d o s t i m e |
michael@0 | 885 | * |
michael@0 | 886 | * Not used right now, but keep it in here because |
michael@0 | 887 | * it will be needed. |
michael@0 | 888 | * |
michael@0 | 889 | */ |
michael@0 | 890 | static int |
michael@0 | 891 | dostime (char *time, const char *s) |
michael@0 | 892 | { |
michael@0 | 893 | PRUint32 num = x86ShortToUint32(s); |
michael@0 | 894 | |
michael@0 | 895 | PR_snprintf (time, 6, "%02d:%02d", ((num >> 11) & 0x1F), |
michael@0 | 896 | ((num >> 5) & 0x3F)); |
michael@0 | 897 | return 0; |
michael@0 | 898 | } |
michael@0 | 899 | |
michael@0 | 900 | #ifndef NSS_X86_OR_X64 |
michael@0 | 901 | /* |
michael@0 | 902 | * Simulates an x86 (little endian, unaligned) ushort fetch from any address. |
michael@0 | 903 | */ |
michael@0 | 904 | static PRUint32 |
michael@0 | 905 | x86ShortToUint32(const void * v) |
michael@0 | 906 | { |
michael@0 | 907 | const unsigned char *ii = (const unsigned char *)v; |
michael@0 | 908 | PRUint32 ret = (PRUint32)(ii[0]) | ((PRUint32)(ii[1]) << 8); |
michael@0 | 909 | return ret; |
michael@0 | 910 | } |
michael@0 | 911 | |
michael@0 | 912 | /* |
michael@0 | 913 | * Simulates an x86 (little endian, unaligned) uint fetch from any address. |
michael@0 | 914 | */ |
michael@0 | 915 | static PRUint32 |
michael@0 | 916 | x86LongToUint32(const void *v) |
michael@0 | 917 | { |
michael@0 | 918 | const unsigned char *ll = (const unsigned char *)v; |
michael@0 | 919 | PRUint32 ret; |
michael@0 | 920 | |
michael@0 | 921 | ret = ((((PRUint32)(ll[0])) << 0) | |
michael@0 | 922 | (((PRUint32)(ll[1])) << 8) | |
michael@0 | 923 | (((PRUint32)(ll[2])) << 16) | |
michael@0 | 924 | (((PRUint32)(ll[3])) << 24)); |
michael@0 | 925 | return ret; |
michael@0 | 926 | } |
michael@0 | 927 | #endif |
michael@0 | 928 | |
michael@0 | 929 | /* |
michael@0 | 930 | * ASCII octal to binary long. |
michael@0 | 931 | * Used for integer encoding inside tar files. |
michael@0 | 932 | * |
michael@0 | 933 | */ |
michael@0 | 934 | static long |
michael@0 | 935 | octalToLong(const char *s) |
michael@0 | 936 | { |
michael@0 | 937 | long num = 0L; |
michael@0 | 938 | |
michael@0 | 939 | while (*s == ' ') |
michael@0 | 940 | s++; |
michael@0 | 941 | while (*s >= '0' && *s <= '7') { |
michael@0 | 942 | num <<= 3; |
michael@0 | 943 | num += *s++ - '0'; |
michael@0 | 944 | } |
michael@0 | 945 | return num; |
michael@0 | 946 | } |
michael@0 | 947 | |
michael@0 | 948 | /* |
michael@0 | 949 | * g u e s s _ j a r |
michael@0 | 950 | * |
michael@0 | 951 | * Try to guess what kind of JAR file this is. |
michael@0 | 952 | * Maybe tar, maybe zip. Look in the file for magic |
michael@0 | 953 | * or at its filename. |
michael@0 | 954 | * |
michael@0 | 955 | */ |
michael@0 | 956 | static int |
michael@0 | 957 | jar_guess_jar(const char *filename, JAR_FILE fp) |
michael@0 | 958 | { |
michael@0 | 959 | PRInt32 len = PORT_Strlen(filename); |
michael@0 | 960 | const char *ext = filename + len - 4; /* 4 for ".tar" */ |
michael@0 | 961 | |
michael@0 | 962 | if (len >= 4 && !PL_strcasecmp(ext, ".tar")) |
michael@0 | 963 | return jarArchTar; |
michael@0 | 964 | return jarArchZip; |
michael@0 | 965 | } |