Back to index

lightning-sunbird  0.9+nobinonly
jarfile.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  *  JARFILE
00039  *
00040  *  Parsing of a Jar file
00041  */
00042 
00043 #define JAR_SIZE 256
00044 
00045 #include "jar.h"
00046 
00047 #include "jarint.h"
00048 #include "jarfile.h"
00049 
00050 /* commercial compression */
00051 #include "jzlib.h"
00052 
00053 #if defined(XP_UNIX) || defined(XP_BEOS)
00054 #include "sys/stat.h"
00055 #endif
00056 
00057 #include "sechash.h" /* for HASH_GetHashObject() */
00058 
00059 /* extracting */
00060 
00061 static int jar_guess_jar (char *filename, JAR_FILE fp);
00062 
00063 static int jar_inflate_memory 
00064    (unsigned int method, long *length, long expected_out_len, char ZHUGEP **data);
00065 
00066 static int jar_physical_extraction 
00067    (JAR_FILE fp, char *outpath, long offset, long length);
00068 
00069 static int jar_physical_inflate
00070    (JAR_FILE fp, char *outpath, long offset, 
00071          long length, unsigned int method);
00072 
00073 static int jar_verify_extract 
00074    (JAR *jar, char *path, char *physical_path);
00075 
00076 static JAR_Physical *jar_get_physical (JAR *jar, char *pathname);
00077 
00078 static int jar_extract_manifests (JAR *jar, jarArch format, JAR_FILE fp);
00079 
00080 static int jar_extract_mf (JAR *jar, jarArch format, JAR_FILE fp, char *ext);
00081 
00082 
00083 /* indexing */
00084 
00085 static int jar_gen_index (JAR *jar, jarArch format, JAR_FILE fp);
00086 
00087 static int jar_listtar (JAR *jar, JAR_FILE fp);
00088 
00089 static int jar_listzip (JAR *jar, JAR_FILE fp);
00090 
00091 
00092 /* conversions */
00093 
00094 static int dosdate (char *date, char *s);
00095 
00096 static int dostime (char *time, char *s);
00097 
00098 static unsigned int xtoint (unsigned char *ii);
00099 
00100 static unsigned long xtolong (unsigned char *ll);
00101 
00102 static long atoo (char *s);
00103 
00104 /*
00105  *  J A R _ p a s s _ a r c h i v e
00106  *
00107  *  For use by naive clients. Slam an entire archive file
00108  *  into this function. We extract manifests, parse, index
00109  *  the archive file, and do whatever nastiness.
00110  *
00111  */
00112 
00113 int JAR_pass_archive
00114     (JAR *jar, jarArch format, char *filename, const char *url)
00115   {
00116   JAR_FILE fp;
00117   int status = 0;
00118 
00119   if (filename == NULL)
00120     return JAR_ERR_GENERAL;
00121 
00122   if ((fp = JAR_FOPEN (filename, "rb")) != NULL)
00123     {
00124     if (format == jarArchGuess)
00125       format = (jarArch)jar_guess_jar (filename, fp);
00126 
00127     jar->format = format;
00128     jar->url = url ? PORT_Strdup (url) : NULL;
00129     jar->filename = PORT_Strdup (filename);
00130 
00131     status = jar_gen_index (jar, format, fp);
00132 
00133     if (status == 0)
00134       status = jar_extract_manifests (jar, format, fp);
00135 
00136     JAR_FCLOSE (fp);
00137 
00138     if (status < 0)
00139       return status;
00140 
00141     /* people were expecting it this way */
00142     return jar->valid;
00143     }
00144   else
00145     {
00146     /* file not found */
00147     return JAR_ERR_FNF;
00148     }
00149   }
00150 
00151 /*
00152  *  J A R _ p a s s _ a r c h i v e _ u n v e r i f i e d
00153  *
00154  * Same as JAR_pass_archive, but doesn't parse signatures.
00155  *
00156  */
00157 int JAR_pass_archive_unverified
00158         (JAR *jar, jarArch format, char *filename, const char *url)
00159 {
00160         JAR_FILE fp;
00161         int status = 0;
00162 
00163         if (filename == NULL) {
00164                 return JAR_ERR_GENERAL;
00165         }
00166 
00167         if ((fp = JAR_FOPEN (filename, "rb")) != NULL) {
00168                 if (format == jarArchGuess) {
00169                         format = (jarArch)jar_guess_jar (filename, fp);
00170                 }
00171 
00172                 jar->format = format;
00173                 jar->url = url ? PORT_Strdup (url) : NULL;
00174                 jar->filename = PORT_Strdup (filename);
00175 
00176                 status = jar_gen_index (jar, format, fp);
00177 
00178                 if (status == 0) {
00179                         status = jar_extract_mf(jar, format, fp, "mf");
00180                 }
00181 
00182                 JAR_FCLOSE (fp);
00183 
00184                 if (status < 0) {
00185                         return status;
00186                 }
00187 
00188                 /* people were expecting it this way */
00189                 return jar->valid;
00190         } else {
00191                 /* file not found */
00192                 return JAR_ERR_FNF;
00193         }
00194 }
00195 
00196 /*
00197  *  J A R _ v e r i f i e d _ e x t r a c t
00198  *
00199  *  Optimization: keep a file descriptor open
00200  *  inside the JAR structure, so we don't have to
00201  *  open the file 25 times to run java. 
00202  *
00203  */
00204 
00205 int JAR_verified_extract
00206     (JAR *jar, char *path, char *outpath)
00207   {
00208   int status;
00209 
00210   status = JAR_extract (jar, path, outpath);
00211 
00212   if (status >= 0)
00213     return jar_verify_extract (jar, path, outpath);
00214   else
00215     return status;
00216   }
00217 
00218 int JAR_extract
00219     (JAR *jar, char *path, char *outpath)
00220   {
00221   int result;
00222 
00223   JAR_Physical *phy;
00224 
00225   if (jar->fp == NULL && jar->filename)
00226     {
00227     jar->fp = (FILE*)JAR_FOPEN (jar->filename, "rb");
00228     }
00229 
00230   if (jar->fp == NULL)
00231     {
00232     /* file not found */
00233     return JAR_ERR_FNF;
00234     }
00235 
00236   phy = jar_get_physical (jar, path);
00237 
00238   if (phy)
00239     {
00240     if (phy->compression != 0 && phy->compression != 8)
00241       {
00242       /* unsupported compression method */
00243       result = JAR_ERR_CORRUPT;
00244       }
00245 
00246     if (phy->compression == 0)
00247       {
00248       result = jar_physical_extraction 
00249          ((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length);
00250       }
00251     else
00252       {
00253       result = jar_physical_inflate 
00254          ((PRFileDesc*)jar->fp, outpath, phy->offset, phy->length, 
00255             (unsigned int) phy->compression);
00256       }
00257 
00258 #if defined(XP_UNIX) || defined(XP_BEOS)
00259     if (phy->mode)
00260       chmod (outpath, 0400 | (mode_t) phy->mode);
00261 #endif
00262     }
00263   else
00264     {
00265     /* pathname not found in archive */
00266     result = JAR_ERR_PNF;
00267     }
00268 
00269   return result;
00270   }
00271 
00272 /* 
00273  *  p h y s i c a l _ e x t r a c t i o n
00274  *
00275  *  This needs to be done in chunks of say 32k, instead of
00276  *  in one bulk calloc. (Necessary under Win16 platform.)
00277  *  This is done for uncompressed entries only.
00278  *
00279  */
00280 
00281 #define CHUNK 32768
00282 
00283 static int jar_physical_extraction 
00284      (JAR_FILE fp, char *outpath, long offset, long length)
00285   {
00286   JAR_FILE out;
00287 
00288   char *buffer;
00289   long at, chunk;
00290 
00291   int status = 0;
00292 
00293   buffer = (char *) PORT_ZAlloc (CHUNK);
00294 
00295   if (buffer == NULL)
00296     return JAR_ERR_MEMORY;
00297 
00298   if ((out = JAR_FOPEN (outpath, "wb")) != NULL)
00299     {
00300     at = 0;
00301 
00302     JAR_FSEEK (fp, offset, (PRSeekWhence)0);
00303 
00304     while (at < length)
00305       {
00306       chunk = (at + CHUNK <= length) ? CHUNK : length - at;
00307 
00308       if (JAR_FREAD (fp, buffer, chunk) != chunk)
00309         {
00310         status = JAR_ERR_DISK;
00311         break;
00312         }
00313 
00314       at += chunk;
00315 
00316       if (JAR_FWRITE (out, buffer, chunk) < chunk)
00317         {
00318         /* most likely a disk full error */
00319         status = JAR_ERR_DISK;
00320         break;
00321         }
00322       }
00323     JAR_FCLOSE (out);
00324     }
00325   else
00326     {
00327     /* error opening output file */
00328     status = JAR_ERR_DISK;
00329     }
00330 
00331   PORT_Free (buffer);
00332   return status;
00333   }
00334 
00335 /*
00336  *  j a r _ p h y s i c a l _ i n f l a t e
00337  *
00338  *  Inflate a range of bytes in a file, writing the inflated
00339  *  result to "outpath". Chunk based.
00340  *
00341  */
00342 
00343 /* input and output chunks differ, assume 4x compression */
00344 
00345 #define ICHUNK 8192
00346 #define OCHUNK 32768
00347 
00348 static int jar_physical_inflate
00349      (JAR_FILE fp, char *outpath, long offset, 
00350           long length, unsigned int method)
00351   {
00352   z_stream zs;
00353 
00354   JAR_FILE out;
00355 
00356   long at, chunk;
00357   char *inbuf, *outbuf;
00358 
00359   int status = 0;
00360 
00361   unsigned long prev_total, ochunk, tin;
00362 
00363   if ((inbuf = (char *) PORT_ZAlloc (ICHUNK)) == NULL)
00364     return JAR_ERR_MEMORY;
00365 
00366   if ((outbuf = (char *) PORT_ZAlloc (OCHUNK)) == NULL)
00367     {
00368     PORT_Free (inbuf);
00369     return JAR_ERR_MEMORY;
00370     }
00371 
00372   PORT_Memset (&zs, 0, sizeof (zs));
00373   status = inflateInit2 (&zs, -MAX_WBITS);
00374 
00375   if (status != Z_OK)
00376     return JAR_ERR_GENERAL;
00377 
00378   if ((out = JAR_FOPEN (outpath, "wb")) != NULL)
00379     {
00380     at = 0;
00381 
00382     JAR_FSEEK (fp, offset, (PRSeekWhence)0);
00383 
00384     while (at < length)
00385       {
00386       chunk = (at + ICHUNK <= length) ? ICHUNK : length - at;
00387 
00388       if (JAR_FREAD (fp, inbuf, chunk) != chunk)
00389         {
00390         /* incomplete read */
00391         return JAR_ERR_CORRUPT;
00392         }
00393 
00394       at += chunk;
00395 
00396       zs.next_in = (Bytef *) inbuf;
00397       zs.avail_in = chunk;
00398       zs.avail_out = OCHUNK;
00399 
00400       tin = zs.total_in;
00401 
00402       while ((zs.total_in - tin < chunk) || (zs.avail_out == 0))
00403         {
00404         prev_total = zs.total_out;
00405 
00406         zs.next_out = (Bytef *) outbuf;
00407         zs.avail_out = OCHUNK;
00408 
00409         status = inflate (&zs, Z_NO_FLUSH);
00410 
00411         if (status != Z_OK && status != Z_STREAM_END)
00412           {
00413           /* error during decompression */
00414           return JAR_ERR_CORRUPT;
00415           }
00416 
00417        ochunk = zs.total_out - prev_total;
00418 
00419         if (JAR_FWRITE (out, outbuf, ochunk) < ochunk)
00420           {
00421           /* most likely a disk full error */
00422           status = JAR_ERR_DISK;
00423           break;
00424           }
00425 
00426         if (status == Z_STREAM_END)
00427           break;
00428         }
00429       }
00430 
00431     JAR_FCLOSE (out);
00432     status = inflateEnd (&zs);
00433     }
00434   else
00435     {
00436     /* error opening output file */
00437     status = JAR_ERR_DISK;
00438     }
00439 
00440   PORT_Free (inbuf);
00441   PORT_Free (outbuf);
00442 
00443   return status;
00444   }
00445 
00446 /*
00447  *  j a r _ i n f l a t e _ m e m o r y
00448  *
00449  *  Call zlib to inflate the given memory chunk. It is re-XP_ALLOC'd, 
00450  *  and thus appears to operate inplace to the caller.
00451  *
00452  */
00453 
00454 static int jar_inflate_memory 
00455      (unsigned int method, long *length, long expected_out_len, char ZHUGEP **data)
00456   {
00457   int status;
00458   z_stream zs;
00459 
00460   long insz, outsz;
00461 
00462   char *inbuf, *outbuf;
00463 
00464   inbuf =  *data;
00465   insz = *length;
00466 
00467   outsz = expected_out_len;
00468   outbuf = (char*)PORT_ZAlloc (outsz);
00469 
00470   if (outbuf == NULL)
00471     return JAR_ERR_MEMORY;
00472 
00473   PORT_Memset (&zs, 0, sizeof (zs));
00474 
00475   status = inflateInit2 (&zs, -MAX_WBITS);
00476 
00477   if (status < 0)
00478     {
00479     /* error initializing zlib stream */
00480     return JAR_ERR_GENERAL;
00481     }
00482 
00483   zs.next_in = (Bytef *) inbuf;
00484   zs.next_out = (Bytef *) outbuf;
00485 
00486   zs.avail_in = insz;
00487   zs.avail_out = outsz;
00488 
00489   status = inflate (&zs, Z_FINISH);
00490 
00491   if (status != Z_OK && status != Z_STREAM_END)
00492     {
00493     /* error during deflation */
00494     return JAR_ERR_GENERAL; 
00495     }
00496 
00497   status = inflateEnd (&zs);
00498 
00499   if (status != Z_OK)
00500     {
00501     /* error during deflation */
00502     return JAR_ERR_GENERAL;
00503     }
00504 
00505   PORT_Free (*data);
00506 
00507   *data = outbuf;
00508   *length = zs.total_out;
00509 
00510   return 0;
00511   }
00512 
00513 /*
00514  *  v e r i f y _ e x t r a c t
00515  *
00516  *  Validate signature on the freshly extracted file.
00517  *
00518  */
00519 
00520 static int jar_verify_extract (JAR *jar, char *path, char *physical_path)
00521   {
00522   int status;
00523   JAR_Digest dig;
00524 
00525   PORT_Memset (&dig, 0, sizeof (JAR_Digest));
00526   status = JAR_digest_file (physical_path, &dig);
00527 
00528   if (!status)
00529     status = JAR_verify_digest (jar, path, &dig);
00530 
00531   return status;
00532   }
00533 
00534 /*
00535  *  g e t _ p h y s i c a l
00536  *
00537  *  Let's get physical.
00538  *  Obtains the offset and length of this file in the jar file.
00539  *
00540  */
00541 
00542 static JAR_Physical *jar_get_physical (JAR *jar, char *pathname)
00543   {
00544   JAR_Item *it;
00545 
00546   JAR_Physical *phy;
00547 
00548   ZZLink *link;
00549   ZZList *list;
00550 
00551   list = jar->phy;
00552 
00553   if (ZZ_ListEmpty (list))
00554     return NULL;
00555 
00556   for (link = ZZ_ListHead (list);
00557        !ZZ_ListIterDone (list, link);
00558        link = link->next)
00559     {
00560     it = link->thing;
00561     if (it->type == jarTypePhy 
00562           && it->pathname && !PORT_Strcmp (it->pathname, pathname))
00563       {
00564       phy = (JAR_Physical *) it->data;
00565       return phy;
00566       }
00567     }
00568 
00569   return NULL;
00570   }
00571 
00572 /*
00573  *  j a r _ e x t r a c t _ m a n i f e s t s
00574  *
00575  *  Extract the manifest files and parse them,
00576  *  from an open archive file whose contents are known.
00577  *
00578  */
00579 
00580 static int jar_extract_manifests (JAR *jar, jarArch format, JAR_FILE fp)
00581   {
00582   int status;
00583 
00584   if (format != jarArchZip && format != jarArchTar)
00585     return JAR_ERR_CORRUPT;
00586 
00587   if ((status = jar_extract_mf (jar, format, fp, "mf")) < 0)
00588     return status;
00589 
00590   if ((status = jar_extract_mf (jar, format, fp, "sf")) < 0)
00591     return status;
00592 
00593   if ((status = jar_extract_mf (jar, format, fp, "rsa")) < 0)
00594     return status;
00595 
00596   if ((status = jar_extract_mf (jar, format, fp, "dsa")) < 0)
00597     return status;
00598 
00599   return 0;
00600   }
00601 
00602 /*
00603  *  j a r _ e x t r a c t _ m f
00604  *
00605  *  Extracts manifest files based on an extension, which 
00606  *  should be .MF, .SF, .RSA, etc. Order of the files is now no 
00607  *  longer important when zipping jar files.
00608  *
00609  */
00610 
00611 static int jar_extract_mf (JAR *jar, jarArch format, JAR_FILE fp, char *ext)
00612   {
00613   JAR_Item *it;
00614 
00615   JAR_Physical *phy;
00616 
00617   ZZLink *link;
00618   ZZList *list;
00619 
00620   char *fn, *e;
00621   char ZHUGEP *manifest;
00622 
00623   long length;
00624   int status, ret = 0, num;
00625 
00626   list = jar->phy;
00627 
00628   if (ZZ_ListEmpty (list))
00629     return JAR_ERR_PNF;
00630 
00631   for (link = ZZ_ListHead (list);
00632        !ZZ_ListIterDone (list, link);
00633        link = link->next)
00634     {
00635     it = link->thing;
00636     if (it->type == jarTypePhy 
00637           && !PORT_Strncmp (it->pathname, "META-INF", 8))
00638       {
00639       phy = (JAR_Physical *) it->data;
00640 
00641       if (PORT_Strlen (it->pathname) < 8)
00642         continue;
00643 
00644       fn = it->pathname + 8;
00645       if (*fn == '/' || *fn == '\\') fn++;
00646 
00647       if (*fn == 0)
00648         {
00649         /* just a directory entry */
00650         continue;
00651         }
00652 
00653       /* skip to extension */
00654       for (e = fn; *e && *e != '.'; e++)
00655         /* yip */ ;
00656 
00657       /* and skip dot */
00658       if (*e == '.') e++;
00659 
00660       if (PORT_Strcasecmp (ext, e))
00661         {
00662         /* not the right extension */
00663         continue;
00664         }
00665 
00666       if (phy->length == 0)
00667         {
00668         /* manifest files cannot be zero length! */
00669         return JAR_ERR_CORRUPT;
00670         }
00671 
00672       /* Read in the manifest and parse it */
00673       /* FIX? Does this break on win16 for very very large manifest files? */
00674 
00675 #ifdef XP_WIN16
00676       PORT_Assert( phy->length+1 < 0xFFFF );
00677 #endif
00678 
00679       manifest = (char ZHUGEP *) PORT_ZAlloc (phy->length + 1);
00680       if (manifest)
00681         {
00682         JAR_FSEEK (fp, phy->offset, (PRSeekWhence)0);
00683         num = JAR_FREAD (fp, manifest, phy->length);
00684 
00685         if (num != phy->length)
00686           {
00687           /* corrupt archive file */
00688           return JAR_ERR_CORRUPT;
00689           }
00690 
00691         if (phy->compression == 8)
00692           {
00693           length = phy->length;
00694 
00695           status = jar_inflate_memory ((unsigned int) phy->compression, &length,  phy->uncompressed_length, &manifest);
00696 
00697           if (status < 0)
00698             return status;
00699           }
00700         else if (phy->compression)
00701           {
00702           /* unsupported compression method */
00703           return JAR_ERR_CORRUPT;
00704           }
00705         else
00706           length = phy->length;
00707 
00708         status = JAR_parse_manifest 
00709            (jar, manifest, length, it->pathname, "url");
00710 
00711         PORT_Free (manifest);
00712 
00713         if (status < 0 && ret == 0) ret = status;
00714         }
00715       else
00716         return JAR_ERR_MEMORY;
00717       }
00718     else if (it->type == jarTypePhy)
00719       {
00720       /* ordinary file */
00721       }
00722     }
00723 
00724   return ret;
00725   }
00726 
00727 /*
00728  *  j a r _ g e n _ i n d e x
00729  *
00730  *  Generate an index for the various types of
00731  *  known archive files. Right now .ZIP and .TAR
00732  *
00733  */
00734 
00735 static int jar_gen_index (JAR *jar, jarArch format, JAR_FILE fp)
00736   {
00737   int result = JAR_ERR_CORRUPT;
00738   JAR_FSEEK (fp, 0, (PRSeekWhence)0);
00739 
00740   switch (format)
00741     {
00742     case jarArchZip:
00743       result = jar_listzip (jar, fp);
00744       break;
00745 
00746     case jarArchTar:
00747       result = jar_listtar (jar, fp);
00748       break;
00749 
00750     case jarArchGuess:
00751     case jarArchNone:
00752       return JAR_ERR_GENERAL;
00753     }
00754 
00755   JAR_FSEEK (fp, 0, (PRSeekWhence)0);
00756   return result;
00757   }
00758 
00759 
00760 /*
00761  *  j a r _ l i s t z i p
00762  *
00763  *  List the physical contents of a Phil Katz 
00764  *  style .ZIP file into the JAR linked list.
00765  *
00766  */
00767 
00768 static int jar_listzip (JAR *jar, JAR_FILE fp)
00769   {
00770   int err = 0;
00771 
00772   long pos = 0L;
00773   char filename [JAR_SIZE];
00774 
00775   char date [9], time [9];
00776   char sig [4];
00777 
00778   unsigned int compression;
00779   unsigned int filename_len, extra_len;
00780 
00781   struct ZipLocal *Local;
00782   struct ZipCentral *Central;
00783   struct ZipEnd *End;
00784 
00785   /* phy things */
00786 
00787   ZZLink  *ent;
00788   JAR_Item *it;
00789   JAR_Physical *phy;
00790 
00791   Local = (struct ZipLocal *) PORT_ZAlloc (30);
00792   Central = (struct ZipCentral *) PORT_ZAlloc (46);
00793   End = (struct ZipEnd *) PORT_ZAlloc (22);
00794 
00795   if (!Local || !Central || !End)
00796     {
00797     /* out of memory */
00798     err = JAR_ERR_MEMORY;
00799     goto loser;
00800     }
00801 
00802   while (1)
00803     {
00804     JAR_FSEEK (fp, pos, (PRSeekWhence)0);
00805 
00806     if (JAR_FREAD (fp, (char *) sig, 4) != 4)
00807       {
00808       /* zip file ends prematurely */
00809       err = JAR_ERR_CORRUPT;
00810       goto loser;
00811       }
00812 
00813     JAR_FSEEK (fp, pos, (PRSeekWhence)0);
00814 
00815     if (xtolong ((unsigned char *)sig) == LSIG)
00816       {
00817       JAR_FREAD (fp, (char *) Local, 30);
00818 
00819       filename_len = xtoint ((unsigned char *) Local->filename_len);
00820       extra_len = xtoint ((unsigned char *) Local->extrafield_len);
00821 
00822       if (filename_len >= JAR_SIZE)
00823         {
00824         /* corrupt zip file */
00825         err = JAR_ERR_CORRUPT;
00826         goto loser;
00827         }
00828 
00829       if (JAR_FREAD (fp, filename, filename_len) != filename_len)
00830         {
00831         /* truncated archive file */
00832         err = JAR_ERR_CORRUPT;
00833         goto loser;
00834         }
00835 
00836       filename [filename_len] = 0;
00837 
00838       /* Add this to our jar chain */
00839 
00840       phy = (JAR_Physical *) PORT_ZAlloc (sizeof (JAR_Physical));
00841 
00842       if (phy == NULL)
00843         {
00844         err = JAR_ERR_MEMORY;
00845         goto loser;
00846         }
00847 
00848       /* We will index any file that comes our way, but when it comes
00849          to actually extraction, compression must be 0 or 8 */
00850 
00851       compression = xtoint ((unsigned char *) Local->method);
00852       phy->compression = compression >= 0 && 
00853               compression <= 255 ? compression : 222;
00854 
00855       phy->offset = pos + 30 + filename_len + extra_len;
00856       phy->length = xtolong ((unsigned char *) Local->size);
00857       phy->uncompressed_length = xtolong((unsigned char *) Local->orglen);
00858 
00859       dosdate (date, Local->date);
00860       dostime (time, Local->time);
00861 
00862       it = (JAR_Item*)PORT_ZAlloc (sizeof (JAR_Item));
00863       if (it == NULL)
00864         {
00865         err = JAR_ERR_MEMORY;
00866         goto loser;
00867         }
00868 
00869       it->pathname = PORT_Strdup (filename);
00870 
00871       it->type = jarTypePhy;
00872 
00873       it->data = (unsigned char *) phy;
00874       it->size = sizeof (JAR_Physical);
00875 
00876       ent = ZZ_NewLink (it);
00877 
00878       if (ent == NULL)
00879         {
00880         err = JAR_ERR_MEMORY;
00881         goto loser;
00882         }
00883 
00884       ZZ_AppendLink (jar->phy, ent);
00885  
00886       pos = phy->offset + phy->length;
00887       }
00888     else if (xtolong ( (unsigned char *)sig) == CSIG)
00889       {
00890       if (JAR_FREAD (fp, (char *) Central, 46) != 46)
00891         {
00892         /* apparently truncated archive */
00893         err = JAR_ERR_CORRUPT;
00894         goto loser;
00895         }
00896 
00897 #if defined(XP_UNIX) || defined(XP_BEOS)
00898       /* with unix we need to locate any bits from 
00899          the protection mask in the external attributes. */
00900         {
00901         unsigned int attr;
00902 
00903         /* determined empirically */
00904         attr = Central->external_attributes [2];
00905 
00906         if (attr)
00907           {
00908           /* we have to read the filename, again */
00909 
00910           filename_len = xtoint ((unsigned char *) Central->filename_len);
00911 
00912           if (filename_len >= JAR_SIZE)
00913             {
00914             /* corrupt in central directory */
00915             err = JAR_ERR_CORRUPT;
00916             goto loser;
00917             }
00918 
00919           if (JAR_FREAD (fp, filename, filename_len) != filename_len)
00920             {
00921             /* truncated in central directory */
00922             err = JAR_ERR_CORRUPT;
00923             goto loser;
00924             }
00925 
00926           filename [filename_len] = 0;
00927 
00928           /* look up this name again */
00929           phy = jar_get_physical (jar, filename);
00930 
00931           if (phy)
00932             {
00933             /* always allow access by self */
00934             phy->mode = 0400 | attr;
00935             }
00936           }
00937         }
00938 #endif
00939 
00940       pos += 46 + xtoint ( (unsigned char *)Central->filename_len)
00941                 + xtoint ( (unsigned char *)Central->commentfield_len)
00942                 + xtoint ( (unsigned char *)Central->extrafield_len);
00943       }
00944     else if (xtolong ( (unsigned char *)sig) == ESIG)
00945       {
00946       if (JAR_FREAD (fp, (char *) End, 22) != 22)
00947         {
00948         err = JAR_ERR_CORRUPT;
00949         goto loser;
00950         }
00951       else
00952         break;
00953       }
00954     else
00955       {
00956       /* garbage in archive */
00957       err = JAR_ERR_CORRUPT;
00958       goto loser;
00959       }
00960     }
00961 
00962 loser:
00963 
00964   if (Local) PORT_Free (Local);
00965   if (Central) PORT_Free (Central);
00966   if (End) PORT_Free (End);
00967 
00968   return err;
00969   }
00970 
00971 /*
00972  *  j a r _ l i s t t a r
00973  *
00974  *  List the physical contents of a Unix 
00975  *  .tar file into the JAR linked list.
00976  *
00977  */
00978 
00979 static int jar_listtar (JAR *jar, JAR_FILE fp)
00980   {
00981   long pos = 0L;
00982 
00983   long sz, mode;
00984   time_t when;
00985   union TarEntry tarball;
00986 
00987   char *s;
00988 
00989   /* phy things */
00990 
00991   JAR_Physical *phy;
00992 
00993   while (1)
00994     {
00995     JAR_FSEEK (fp, pos, (PRSeekWhence)0);
00996 
00997     if (JAR_FREAD (fp, (char *) &tarball, 512) < 512)
00998       break;
00999 
01000     if (!*tarball.val.filename)
01001       break;
01002 
01003     when = atoo (tarball.val.time);
01004     sz = atoo (tarball.val.size);
01005     mode = atoo (tarball.val.mode);
01006 
01007 
01008     /* Tag the end of filename */
01009 
01010     s = tarball.val.filename;
01011     while (*s && *s != ' ') s++;
01012     *s = 0;
01013 
01014 
01015     /* Add to our linked list */
01016 
01017     phy = (JAR_Physical *) PORT_ZAlloc (sizeof (JAR_Physical));
01018 
01019     if (phy == NULL)
01020       return JAR_ERR_MEMORY;
01021 
01022     phy->compression = 0;
01023     phy->offset = pos + 512;
01024     phy->length = sz;
01025 
01026     ADDITEM (jar->phy, jarTypePhy, 
01027        tarball.val.filename, phy, sizeof (JAR_Physical));
01028 
01029 
01030     /* Advance to next file entry */
01031 
01032     sz += 511;
01033     sz = (sz / 512) * 512;
01034 
01035     pos += sz + 512;
01036     }
01037 
01038   return 0;
01039   }
01040 
01041 /*
01042  *  d o s d a t e
01043  *
01044  *  Not used right now, but keep it in here because
01045  *  it will be needed. 
01046  *
01047  */
01048 
01049 static int dosdate (char *date, char *s)
01050   {
01051   int num = xtoint ( (unsigned char *)s);
01052 
01053   PR_snprintf (date, 9, "%02d-%02d-%02d",
01054      ((num >> 5) & 0x0F), (num & 0x1F), ((num >> 9) + 80));
01055 
01056   return 0;
01057   }
01058 
01059 /*
01060  *  d o s t i m e
01061  *
01062  *  Not used right now, but keep it in here because
01063  *  it will be needed. 
01064  *
01065  */
01066 
01067 static int dostime (char *time, char *s)
01068   {
01069   int num = xtoint ( (unsigned char *)s);
01070 
01071   PR_snprintf (time, 6, "%02d:%02d",
01072      ((num >> 11) & 0x1F), ((num >> 5) & 0x3F));
01073 
01074   return 0;
01075   }
01076 
01077 /*
01078  *  x t o i n t
01079  *
01080  *  Converts a two byte ugly endianed integer
01081  *  to our platform's integer.
01082  *
01083  */
01084 
01085 static unsigned int xtoint (unsigned char *ii)
01086   {
01087   return (int) (ii [0]) | ((int) ii [1] << 8);
01088   }
01089 
01090 /*
01091  *  x t o l o n g
01092  *
01093  *  Converts a four byte ugly endianed integer
01094  *  to our platform's integer.
01095  *
01096  */
01097 
01098 static unsigned long xtolong (unsigned char *ll)
01099   {
01100   unsigned long ret;
01101 
01102   ret =  (
01103          (((unsigned long) ll [0]) <<  0) |
01104          (((unsigned long) ll [1]) <<  8) |
01105          (((unsigned long) ll [2]) << 16) |
01106          (((unsigned long) ll [3]) << 24)
01107          );
01108 
01109   return ret;
01110   }
01111 
01112 /*
01113  *  a t o o
01114  *
01115  *  Ascii octal to decimal.
01116  *  Used for integer encoding inside tar files.
01117  *
01118  */
01119 
01120 static long atoo (char *s)
01121   {
01122   long num = 0L;
01123 
01124   while (*s == ' ') s++;
01125 
01126   while (*s >= '0' && *s <= '7')
01127     {
01128     num <<= 3;
01129     num += *s++ - '0';
01130     }
01131 
01132   return num;
01133   }
01134 
01135 /*
01136  *  g u e s s _ j a r
01137  *
01138  *  Try to guess what kind of JAR file this is.
01139  *  Maybe tar, maybe zip. Look in the file for magic
01140  *  or at its filename.
01141  *
01142  */
01143 
01144 static int jar_guess_jar (char *filename, JAR_FILE fp)
01145   {
01146   char *ext;
01147 
01148   ext = filename + PORT_Strlen (filename) - 4;
01149 
01150   if (!PORT_Strcmp (ext, ".tar"))
01151     return jarArchTar;
01152 
01153   return jarArchZip;
01154   }