/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Mozilla Communicator client code, * released March 31, 1998. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Daniel Veditz * Samir Gehani * Mitch Stoltz */ /* * This module implements a simple archive extractor for the PKZIP format. * * The underlying nsZipArchive is NOT thread-safe. Do not pass references * or pointers to it across thread boundaries. */ #ifndef STANDALONE #include "nscore.h" #include "prmem.h" #include "prio.h" #include "plstr.h" #include "xp_regexp.h" #define ZFILE_CREATE PR_WRONLY | PR_CREATE_FILE #define READTYPE PRInt32 #include "zlib.h" #include "nsISupportsUtils.h" #else #ifdef WIN32 #include "windows.h" #endif #undef MOZILLA_CLIENT // undoes prtypes damage in zlib.h #define ZFILE_CREATE "wb" #define READTYPE PRUint32 #include "zlib.h" #undef PR_PUBLIC_API #include "zipstub.h" #endif /* STANDALONE */ #ifdef XP_UNIX #include #elif defined(XP_PC) #include #endif #include "zipfile.h" #include "zipstruct.h" #include "nsZipArchive.h" static PRUint16 xtoint(unsigned char *ii); static PRUint32 xtolong(unsigned char *ll); static PRUint16 ExtractMode(PRUint32 ext_attr); /*--------------------------------------------- * C API wrapper for nsZipArchive *--------------------------------------------*/ #ifdef STANDALONE /** * ZIP_OpenArchive * * opens the named zip/jar archive and returns a handle that * represents the archive in other ZIP_ calls. * * @param zipname archive filename * @param hZip receives handle if archive opened OK * @return status code */ PR_PUBLIC_API(PRInt32) ZIP_OpenArchive( const char * zipname, void** hZip ) { PRInt32 status; /*--- error check args ---*/ if ( hZip == 0 ) return ZIP_ERR_PARAM; /*--- NULL output to prevent use by bozos who don't check errors ---*/ *hZip = 0; /*--- create and open the archive ---*/ nsZipArchive* zip = new nsZipArchive(); if ( zip == 0 ) return ZIP_ERR_MEMORY; status = zip->OpenArchive(zipname); if ( status == ZIP_OK ) *hZip = NS_STATIC_CAST(void*,zip); else delete zip; return status; } /** * ZIP_CloseArchive * * closes zip archive and frees memory * @param hZip handle obtained from ZIP_OpenArchive * @return status code */ PR_PUBLIC_API(PRInt32) ZIP_CloseArchive( void** hZip ) { /*--- error check args ---*/ if ( hZip == 0 || *hZip == 0 ) return ZIP_ERR_PARAM; nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,*hZip); if ( zip->kMagic != ZIP_MAGIC ) return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */ /*--- close the archive ---*/ *hZip = 0; delete zip; return ZIP_OK; } /** * ZIP_ExtractFile * * extracts named file from an opened archive * * @param hZip handle obtained from ZIP_OpenArchive * @param filename name of file in archive * @param outname filename to extract to */ PR_PUBLIC_API(PRInt32) ZIP_ExtractFile( void* hZip, const char * filename, const char * outname ) { /*--- error check args ---*/ if ( hZip == 0 ) return ZIP_ERR_PARAM; nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,hZip); if ( zip->kMagic != ZIP_MAGIC ) return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */ /*--- extract the file ---*/ return zip->ExtractFile( filename, outname ); } /** * ZIP_FindInit * * Initializes an enumeration of files in the archive * * @param hZip handle obtained from ZIP_OpenArchive * @param pattern regexp to match files in archive, the usual shell expressions. * NULL pattern also matches all files, faster than "*" */ PR_PUBLIC_API(void*) ZIP_FindInit( void* hZip, const char * pattern ) { /*--- error check args ---*/ if ( hZip == 0 ) return 0; nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,hZip); if ( zip->kMagic != ZIP_MAGIC ) return 0; /* whatever it is isn't one of ours! */ /*--- initialize the pattern search ---*/ return zip->FindInit( pattern ); } /** * ZIP_FindNext * * Puts the next name in the passed buffer. Returns ZIP_ERR_SMALLBUF when * the name is too large for the buffer, and ZIP_ERR_FNF when there are no * more files that match the pattern * * @param hFind handle obtained from ZIP_FindInit * @param outbuf buffer to receive next filename * @param bufsize size of allocated buffer */ PR_PUBLIC_API(PRInt32) ZIP_FindNext( void* hFind, char * outbuf, PRUint16 bufsize ) { PRInt32 status; /*--- error check args ---*/ if ( hFind == 0 ) return ZIP_ERR_PARAM; nsZipFind* find = NS_STATIC_CAST(nsZipFind*,hFind); if ( find->kMagic != ZIPFIND_MAGIC ) return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */ /*--- return next filename file ---*/ nsZipItem* item; status = find->mArchive->FindNext( find, &item ); if ( status == ZIP_OK ) { if ( bufsize > item->namelen ) { PL_strcpy( outbuf, item->name ); } else status = ZIP_ERR_SMALLBUF; } return status; } /** * ZIP_FindFree * * Releases allocated memory associated with the find token * * @param hFind handle obtained from ZIP_FindInit */ PR_PUBLIC_API(PRInt32) ZIP_FindFree( void* hFind ) { /*--- error check args ---*/ if ( hFind == 0 ) return ZIP_ERR_PARAM; nsZipFind* find = NS_STATIC_CAST(nsZipFind*,hFind); if ( find->kMagic != ZIPFIND_MAGIC ) return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */ /* free the find structure */ return find->mArchive->FindFree( find ); } #endif /* STANDALONE */ //*********************************************************** // nsZipArchive -- public methods //*********************************************************** //--------------------------------------------- // nsZipArchive::OpenArchive //--------------------------------------------- PRInt32 nsZipArchive::OpenArchive( const char * aArchiveName ) { //-- validate arguments if ( aArchiveName == 0 || *aArchiveName == '\0') return ZIP_ERR_PARAM; //-- not allowed to do two opens on the same object! if ( mFd != 0 ) return ZIP_ERR_GENERAL; //-- open the physical file mFd = PR_Open( aArchiveName, PR_RDONLY, 0 ); if ( mFd == 0 ) return ZIP_ERR_DISK; //-- get table of contents for archive return BuildFileList(); } //--------------------------------------------- // nsZipArchive::CloseArchive //--------------------------------------------- PRInt32 nsZipArchive::CloseArchive() { // close the file if open if ( mFd != 0 ) { PR_Close(mFd); } // delete nsZipItems in table nsZipItem* pItem; for ( int i = 0; i < ZIP_TABSIZE; ++i) { pItem = mFiles[i]; while ( pItem != 0 ) { mFiles[i] = pItem->next; delete pItem; pItem = mFiles[i]; } } return ZIP_OK; } //--------------------------------------------- // nsZipArchive::ItemSize //--------------------------------------------- PRInt32 nsZipArchive::GetItem( const char * aFilename, nsZipItem *result) { //-- Parameter validity check if (aFilename == 0) return ZIP_ERR_PARAM; //PRInt32 result; nsZipItem* aItem; //-- find file information aItem = GetFileItem( aFilename ); if ( aItem == 0 ) { return ZIP_ERR_FNF; } return result->Init(aItem); } //--------------------------------------------- // nsZipArchive::ReadInit //--------------------------------------------- PRInt32 nsZipArchive::ReadInit(const char* aFilename, nsZipRead** aRead) { //-- Parameter validity check if (aFilename == 0 || aRead == 0) return ZIP_ERR_PARAM; PRInt32 result; //-- find item and seek to file position nsZipItem* item; result = ReadInitImpl(aFilename, &item); if (result != ZIP_OK) return result; //-- Create nsZipRead object *aRead = new nsZipRead(this, item); if (aRead == 0) return ZIP_ERR_MEMORY; //-- initialize crc (*aRead)->mCRC32 = crc32(0L, Z_NULL, 0); //-- If file is deflated, do the inflation now. // We save the complete inflated item in mInflatedFileBuffer // to be copied later. if (item->compression == DEFLATED) { char* buf = (char*)PR_Malloc(item->realsize); result = InflateItem(item, 0, buf); if (result != ZIP_OK) return result; (*aRead)->mInflatedFileBuffer = buf; } return ZIP_OK; } //--------------------------------------------- // nsZipArchive::Read //--------------------------------------------- PRInt32 nsZipArchive::Read(nsZipRead* aRead, char* aBuf, PRUint32 aCount, PRUint32* aBytesRead) { //-- Parameter validity check /* * Note the nsZipRead passed in must have been created * by this nsZipArchive. */ if (aBytesRead == 0 || aRead == 0 || aBuf == 0 || aRead->mArchive != this) return ZIP_ERR_PARAM; //-- extract the file using appropriate method switch( aRead->mItem->compression ) { case STORED: //-- Read from the zip file directly into the caller's buffer return ReadItem( aRead, aBuf, aCount, aBytesRead ); case DEFLATED: //-- We've already done the inflation; copy from mInflatedFileBuffer // into the caller's buffer return ReadInflatedItem( aRead->mItem, aRead->mInflatedFileBuffer, aBuf, &(aRead->mCurPos), aCount, aBytesRead); default: //-- unsupported compression type return ZIP_ERR_UNSUPPORTED; } } //--------------------------------------------- // nsZipArchive::Available //--------------------------------------------- PRUint32 nsZipArchive::Available(nsZipRead* aRead) { if (aRead == 0) return 0; nsZipItem* item = aRead->mItem; if (item->compression == DEFLATED) return item->realsize - aRead->mCurPos; else return item->size - aRead->mCurPos; } //--------------------------------------------- // nsZipArchive::ExtractFile //--------------------------------------------- PRInt32 nsZipArchive::ExtractFile(const char* aFilename, const char* aOutname) { //-- sanity check arguments if ( aFilename == 0 || aOutname == 0 ) return ZIP_ERR_PARAM; PRInt32 status; nsZipItem* item; //-- Find item in archive and seek to it status = ReadInitImpl( aFilename, &item ); if (status != ZIP_OK) return status; //-- extract the file using the appropriate method switch( item->compression ) { case STORED: status = CopyItemToDisk( item, aOutname ); break; case DEFLATED: status = InflateItem( item, aOutname, 0 ); break; default: //-- unsupported compression type return ZIP_ERR_UNSUPPORTED; } if (status != ZIP_OK) PR_Delete(aOutname); #if defined(XP_UNIX) || defined(XP_PC) else { //-- set extracted file permissions chmod(aOutname, item->mode); } #endif return status; } //--------------------------------------------- // nsZipArchive::FindInit //--------------------------------------------- nsZipFind* nsZipArchive::FindInit( const char * aPattern ) { PRBool regExp; char* pattern = 0; // validate the pattern if ( aPattern ) { switch (XP_RegExpValid( (char*)aPattern )) { case INVALID_SXP: return 0; case NON_SXP: regExp = PR_FALSE; break; case VALID_SXP: regExp = PR_TRUE; break; default: // undocumented return value from RegExpValid! PR_ASSERT( PR_FALSE ); return 0; } pattern = PL_strdup( aPattern ); if ( !pattern ) return 0; } return new nsZipFind( this, pattern, regExp ); } //--------------------------------------------- // nsZipArchive::FindNext //--------------------------------------------- PRInt32 nsZipArchive::FindNext( nsZipFind* aFind, nsZipItem** aResult) { PRInt32 status; PRBool found = PR_FALSE; PRUint16 slot = aFind->mSlot; nsZipItem* item = aFind->mItem; if ( aFind->mArchive != this ) return ZIP_ERR_PARAM; // we start from last match, look for next while ( slot < ZIP_TABSIZE && !found ) { if ( item != 0 ) item = item->next; // move to next in current chain else item = mFiles[slot]; // starting a new slot if ( item == 0 ) { // no more in this chain, move to next slot ++slot; continue; } else if ( aFind->mPattern == 0 ) found = PR_TRUE; // always match else if ( aFind->mRegExp ) found = (XP_RegExpMatch( item->name, aFind->mPattern, PR_FALSE ) == MATCH); else #if defined(STANDALONE) && defined(XP_MAC) // simulate * matches found = ( strncmp( item->name, aFind->mPattern, strlen(aFind->mPattern) ) == 0 ); #else found = ( PL_strcmp( item->name, aFind->mPattern ) == 0 ); #endif } if ( found ) { *aResult = item; aFind->mSlot = slot; aFind->mItem = item; status = ZIP_OK; } else status = ZIP_ERR_FNF; return status; } //--------------------------------------------- // nsZipArchive::FindFree //--------------------------------------------- PRInt32 nsZipArchive::FindFree( nsZipFind* aFind ) { if ( aFind->mArchive != this ) return ZIP_ERR_PARAM; delete aFind; return ZIP_OK; } //*********************************************************** // nsZipArchive -- private implementation //*********************************************************** //--------------------------------------------- // nsZipArchive::BuildFileList //--------------------------------------------- PRInt32 nsZipArchive::BuildFileList() { PRInt32 status = ZIP_OK; PRUint32 sig = 0L; PRUint32 namelen, extralen, commentlen; PRUint32 hash; PRUint32 size; ZipLocal Local; ZipCentral Central; nsZipItem* item; //----------------------------------------------------------------------- // skip to the central directory //----------------------------------------------------------------------- PRInt32 pos = 0L; while ( status == ZIP_OK ) { #ifndef STANDALONE if ( PR_Seek( mFd, pos, PR_SEEK_SET ) != (PRInt32)pos ) #else // For standalone, PR_Seek() is stubbed with fseek(), which returns 0 // if successfull, otherwise a non-zero. if ( PR_Seek( mFd, pos, PR_SEEK_SET ) != 0 ) #endif { //-- couldn't seek to next position status = ZIP_ERR_CORRUPT; break; } if ( PR_Read( mFd, (char*)&Local, sizeof(ZipLocal) ) != sizeof(ZipLocal) ) { //-- file ends prematurely status = ZIP_ERR_CORRUPT; break; } //-- check if we hit the central directory sig = xtolong( Local.signature ); if ( sig == LOCALSIG ) { //-- check length of this file and its metadata so we can skip over it namelen = xtoint( Local.filename_len ); extralen = xtoint( Local.extrafield_len ); size = xtolong( Local.size ); //-- reposition file mark to next expected local header pos += sizeof(ZipLocal) + namelen + extralen + size; } else if ( sig == CENTRALSIG ) { // file mark set to start of central directory break; } else { //-- otherwise expected to find a local header status = ZIP_ERR_CORRUPT; break; } } /* while reading local headers */ //------------------------------------------------------- // read the central directory headers //------------------------------------------------------- while ( status == ZIP_OK ) { #ifndef STANDALONE if ( PR_Seek( mFd, pos, PR_SEEK_SET ) != (PRInt32)pos ) #else if ( PR_Seek( mFd, pos, PR_SEEK_SET ) != 0 ) #endif { //-- couldn't seek to next position status = ZIP_ERR_CORRUPT; break; } if ( PR_Read( mFd, (char*)&Central, sizeof(Central) ) != sizeof(ZipCentral) ) { //-- central dir end record is smaller than a central dir header sig = xtolong( Central.signature ); if ( sig == ENDSIG ) { //-- we've reached the end of the central dir break; } else { status = ZIP_ERR_CORRUPT; break; } } sig = xtolong( Central.signature ); if ( sig == ENDSIG ) { //-- we've reached the end of the central dir break; } else if ( sig != CENTRALSIG ) { //-- otherwise expected to find a central dir header status = ZIP_ERR_CORRUPT; break; } namelen = xtoint( Central.filename_len ); extralen = xtoint( Central.extrafield_len ); commentlen = xtoint( Central.commentfield_len ); item = new nsZipItem(); if (item != 0) { item->name = new char[namelen+1]; if ( item->name != 0 ) { if ( PR_Read( mFd, item->name, namelen ) == (READTYPE)namelen ) { item->name[namelen] = 0; item->namelen = namelen; item->headerloc = xtolong( Central.localhdr_offset ); //-- seek to local header #ifndef STANDALONE if ( PR_Seek( mFd, item->headerloc, PR_SEEK_SET ) != (PRInt32)item->headerloc ) #else if ( PR_Seek( mFd, item->headerloc, PR_SEEK_SET ) != 0 ) #endif { //-- couldn't seek to next position status = ZIP_ERR_CORRUPT; break; } //-- read local header to extract local extralen //-- NOTE: extralen is different in central header and local header //-- for archives created using the Unix "zip" utility. To set //-- the offset accurately we need the local extralen. if ( PR_Read( mFd, (char*)&Local, sizeof(ZipLocal) ) != (READTYPE)sizeof(ZipLocal) ) { //-- expected a complete local header status = ZIP_ERR_CORRUPT; break; } item->offset = item->headerloc + sizeof(ZipLocal) + namelen + xtoint( Local.extrafield_len ); item->compression = xtoint( Central.method ); item->size = xtolong( Central.size ); item->realsize = xtolong( Central.orglen ); item->crc32 = xtolong( Central.crc32 ); item->mode = ExtractMode(xtolong( Central.external_attributes )); //-- add item to file table hash = HashName( item->name ); item->next = mFiles[hash]; mFiles[hash] = item; //-- set pos to next expected central dir header pos += sizeof(ZipCentral) + namelen + extralen + commentlen; } else { //-- file is truncated status = ZIP_ERR_CORRUPT; delete item; } } else { //-- couldn't allocate for the filename status = ZIP_ERR_MEMORY; delete item; } } else { //-- couldn't create an nsZipItem status = ZIP_ERR_MEMORY; } } /* while reading central directory records */ return status; } //--------------------------------------------- // nsZipArchive::GetFileItem //--------------------------------------------- nsZipItem* nsZipArchive::GetFileItem( const char * aFilename ) { PR_ASSERT( aFilename != 0 ); nsZipItem* item = mFiles[ HashName(aFilename) ]; for ( ; item != 0; item = item->next ) { if ( 0 == PL_strcmp( aFilename, item->name ) ) break; //-- found it } return item; } //--------------------------------------------- // nsZipArchive::HashName //--------------------------------------------- PRUint32 nsZipArchive::HashName( const char* aName ) { PRUint32 val = 0; PRUint8* c; PR_ASSERT( aName != 0 ); for ( c = (PRUint8*)aName; *c != 0; c++ ) { val = val*37 + *c; } return (val % ZIP_TABSIZE); } //--------------------------------------------- // nsZipArchive::ReadInitImpl //--------------------------------------------- PRInt32 nsZipArchive::ReadInitImpl(const char* aFilename, nsZipItem** aItem) { PR_ASSERT (aFilename != 0); //-- find file information *aItem = GetFileItem( aFilename ); if ( *aItem == 0 ) return ZIP_ERR_FNF; //-- find start of file in archive #ifndef STANDALONE if ( PR_Seek( mFd, (*aItem)->offset, PR_SEEK_SET ) != (PRInt32)(*aItem)->offset ) #else // For standalone, PR_Seek() is stubbed with fseek(), which returns 0 // if successfull, otherwise a non-zero. if ( PR_Seek( mFd, (*aItem)->offset, PR_SEEK_SET ) != 0) #endif return ZIP_ERR_CORRUPT; return ZIP_OK; } //--------------------------------------------- // nsZipArchive::ReadItem //--------------------------------------------- PRInt32 nsZipArchive::ReadItem( nsZipRead* aRead, char* aBuf, PRUint32 aCount, PRUint32* aActual) { PRUint32 chunk; nsZipItem *item = aRead->mItem; PRUint32 *curpos = &(aRead->mCurPos); PRUint32 *crc = &(aRead->mCRC32); PR_ASSERT(aBuf != 0 && item != 0); // Seek to position in file if ( PR_Seek( mFd, (PROffset32)(item->offset + *curpos), PR_SEEK_SET ) != (PROffset32)(item->offset + *curpos) ) return ZIP_ERR_CORRUPT; //-- Read from file chunk = (aCount <= ((item->size) - *curpos) ) ? aCount : ((item->size) - *curpos); *aActual = PR_Read( mFd, aBuf, chunk ); *curpos += *aActual; //-- incrementally update crc32 *crc = crc32(*crc,(const unsigned char*)aBuf, chunk); //-- verify crc32 if (0 == (item->size - *curpos)) // eof { if (*crc != item->crc32) return ZIP_ERR_CORRUPT; } return ZIP_OK; } //--------------------------------------------- // nsZipArchive::CopyItemToDisk //--------------------------------------------- PRInt32 nsZipArchive::CopyItemToDisk( const nsZipItem* aItem, const char* aOutname ) { PRInt32 status = ZIP_OK; PRUint32 chunk, pos, size, crc; PRFileDesc* fOut = 0; PR_ASSERT( aItem != 0 && aOutname != 0 ); char* buf = (char*)PR_Malloc(ZIP_BUFLEN); if ( buf == 0 ) return ZIP_ERR_MEMORY; //-- We should already be at the correct spot in the archive. //-- ReadInitImpl did the seek(). //-- initialize crc crc = crc32(0L, Z_NULL, 0); //-- open output file fOut = PR_Open( aOutname, ZFILE_CREATE , 0644); if ( fOut == 0 ) { status = ZIP_ERR_DISK; goto cleanup; } //-- copy chunks until file is done size = aItem->size; for ( pos=0; pos < size; pos += chunk ) { chunk = (pos+ZIP_BUFLEN <= size) ? ZIP_BUFLEN : size - pos; if ( PR_Read( mFd, buf, chunk ) != (READTYPE)chunk ) { //-- unexpected end of data in archive status = ZIP_ERR_CORRUPT; break; } //-- incrementally update crc32 crc = crc32(crc, (const unsigned char*)buf, chunk); if ( PR_Write( fOut, buf, chunk ) < (READTYPE)chunk ) { //-- Couldn't write all the data (disk full?) status = ZIP_ERR_DISK; break; } } //-- verify crc32 if ( (status == ZIP_OK) && (crc != aItem->crc32) ) status = ZIP_ERR_CORRUPT; cleanup: if ( fOut != 0 ) PR_Close( fOut ); PR_FREEIF( buf ); return status; } //--------------------------------------------- // nsZipArchive::InflateItem //--------------------------------------------- PRInt32 nsZipArchive::InflateItem( const nsZipItem* aItem, const char* aOutname, char* bigBuf ) /* * This function either inflates an archive item to disk, to the * file specified by aOutname, or into a buffer specified by * bigBuf. bigBuf then gets copied into the "real" output * buffer a chunk at a time by ReadInflatedItem(). Memory-wise, * this is inefficient, since it stores an entire copy of the * decompressed item in memory, then copies it to the caller's * buffer. A more memory-efficient implementation is possible, * in which the outbuf in this function could be returned * directly, but implementing it would be complex. */ { PRInt32 status = ZIP_OK; PRUint32 chunk, inpos, outpos, size, crc; PRUint32 bigBufSize; PRFileDesc* fOut = 0; z_stream zs; int zerr; PRBool bInflating = PR_FALSE; PRBool bRead; PRBool bWrote; PRBool bToFile; Bytef* old_next_out; #if defined STANDALONE && defined WIN32 MSG msg; #endif /* STANDALONE */ // -- if aOutname is null, we'll be writing to a buffer instead of a file if (aOutname != 0) { PR_ASSERT( aItem != 0 ); bToFile = PR_TRUE; } else { // -- Writing to a buffer, so bigBuf must not be null. PR_ASSERT( aItem != 0 && bigBuf != 0 ); bToFile = PR_FALSE; bigBufSize = aItem->realsize; } //-- allocate deflation buffers Bytef *inbuf = (Bytef*)PR_Malloc(ZIP_BUFLEN); Bytef *outbuf = (Bytef*)PR_Malloc(ZIP_BUFLEN); if ( inbuf == 0 || outbuf == 0 ) { status = ZIP_ERR_MEMORY; goto cleanup; } //-- We should already be at the correct spot in the archive. //-- ReadInitImpl did the seek(). if (bToFile) { //-- open output file fOut = PR_Open( aOutname, ZFILE_CREATE, 0644); if ( fOut == 0 ) { status = ZIP_ERR_DISK; goto cleanup; } } //-- set up the inflate memset( &zs, 0, sizeof(zs) ); zerr = inflateInit2( &zs, -MAX_WBITS ); if ( zerr != Z_OK ) { status = ZIP_ERR_GENERAL; goto cleanup; } bInflating = PR_TRUE; //-- inflate loop size = aItem->size; outpos = inpos = 0; zs.next_out = outbuf; zs.avail_out = ZIP_BUFLEN; crc = crc32(0L, Z_NULL, 0); while ( zerr == Z_OK ) { bRead = PR_FALSE; bWrote = PR_FALSE; if ( zs.avail_in == 0 && zs.total_in < size ) { //-- no data to inflate yet still more in file: //-- read another chunk of compressed data inpos = zs.total_in; // input position chunk = ( inpos + ZIP_BUFLEN <= size ) ? ZIP_BUFLEN : size - inpos; if ( PR_Read( mFd, inbuf, chunk ) != (READTYPE)chunk ) { //-- unexpected end of data status = ZIP_ERR_CORRUPT; break; } zs.next_in = inbuf; zs.avail_in = chunk; bRead = PR_TRUE; } if ( zs.avail_out == 0 ) { if (bToFile) { //-- write inflated buffer to disk and make space if ( PR_Write( fOut, outbuf, ZIP_BUFLEN ) < ZIP_BUFLEN ) { //-- Couldn't write all the data (disk full?) status = ZIP_ERR_DISK; break; } } else { //-- copy inflated buffer to our big buffer // Assertion makes sure we don't overflow bigBuf PR_ASSERT( outpos + ZIP_BUFLEN <= bigBufSize); char* copyStart = bigBuf + outpos; memcpy(copyStart, outbuf, ZIP_BUFLEN); } outpos = zs.total_out; zs.next_out = outbuf; zs.avail_out = ZIP_BUFLEN; bWrote = PR_TRUE; } if(bRead || bWrote) { old_next_out = zs.next_out; zerr = inflate( &zs, Z_PARTIAL_FLUSH ); //-- incrementally update crc32 crc = crc32(crc, (const unsigned char*)old_next_out, zs.next_out - old_next_out); } else zerr = Z_STREAM_END; #if defined STANDALONE && defined WIN32 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } #endif /* STANDALONE */ } // while //-- verify crc32 if ( (status == ZIP_OK) && (crc != aItem->crc32) ) { status = ZIP_ERR_CORRUPT; goto cleanup; } //-- write last inflated bit to disk if ( zerr == Z_STREAM_END && outpos < zs.total_out ) { chunk = zs.total_out - outpos; if (bToFile) { if ( PR_Write( fOut, outbuf, chunk ) < (READTYPE)chunk ) status = ZIP_ERR_DISK; } else { PR_ASSERT( (outpos + chunk) <= bigBufSize ); char* copyStart = bigBuf + outpos; memcpy(copyStart, outbuf, chunk); } } //-- convert zlib error to return value if ( status == ZIP_OK && zerr != Z_OK && zerr != Z_STREAM_END ) { status = (zerr == Z_MEM_ERROR) ? ZIP_ERR_MEMORY : ZIP_ERR_CORRUPT; } //-- if found no errors make sure we've converted the whole thing PR_ASSERT( status != ZIP_OK || zs.total_in == aItem->size ); PR_ASSERT( status != ZIP_OK || zs.total_out == aItem->realsize ); cleanup: if ( bInflating ) { //-- free zlib internal state inflateEnd( &zs ); } if (fOut != 0 ) PR_Close( fOut ); PR_FREEIF( inbuf ); PR_FREEIF( outbuf ); return status; } //------------------------------------------ // nsZipArchive::ReadInflatedItem //------------------------------------------ PRInt32 nsZipArchive::ReadInflatedItem( const nsZipItem* aItem, char* inflatedBuf, char* outbuf, PRUint32* aCurPos, PRUint32 count, PRUint32* actual) { //-- Parameter check PR_ASSERT(inflatedBuf != 0 && outbuf != 0 && aItem != 0); //-- Set up the copy PRUint32 bigBufSize = aItem->realsize; PRUint32 c = ((*aCurPos + count) < bigBufSize) ? count : bigBufSize - *aCurPos; char* src = inflatedBuf + (*aCurPos); //-- Do the copy and record number of bytes copied memcpy(outbuf, src, c); *actual = c; *aCurPos += *actual; return ZIP_OK; } //------------------------------------------ // nsZipArchive constructor and destructor //------------------------------------------ #ifndef STANDALONE MOZ_DECL_CTOR_COUNTER(nsZipArchive); #endif nsZipArchive::nsZipArchive() : kMagic(ZIP_MAGIC), mFd(0) { #ifndef STANDALONE MOZ_COUNT_CTOR(nsZipArchive); #endif // initialize the table to NULL for ( int i = 0; i < ZIP_TABSIZE; ++i) { mFiles[i] = 0; } } nsZipArchive::~nsZipArchive() { (void)CloseArchive(); #ifndef STANDALONE MOZ_COUNT_DTOR(nsZipArchive); #endif } //------------------------------------------ // nsZipItem constructor and destructor //------------------------------------------ #ifndef STANDALONE MOZ_DECL_CTOR_COUNTER(nsZipItem); #endif nsZipItem::nsZipItem() : name(0),next(0) { #ifndef STANDALONE MOZ_COUNT_CTOR(nsZipItem); #endif } nsZipItem::~nsZipItem() { if (name != 0 ) delete [] name; #ifndef STANDALONE MOZ_COUNT_DTOR(nsZipItem); #endif } PRInt32 nsZipItem::Init(nsZipItem* other) { if (name) delete[] name; name = (char*)PR_Malloc(other->namelen); if (name == 0) return ZIP_ERR_MEMORY; memcpy(name, other->name, other->namelen); namelen = other->namelen; offset = other->offset; headerloc = other->headerloc; compression = other->compression; size = other->size; realsize = other->realsize; crc32 = other->crc32; mode = other->mode; next = 0; // don't copy next return ZIP_OK; } //------------------------------------------ // nsZipRead constructor and destructor //------------------------------------------ #ifndef STANDALONE MOZ_DECL_CTOR_COUNTER(nsZipRead); #endif nsZipRead::nsZipRead( nsZipArchive* aZipArchive, nsZipItem* aZipItem ) : mArchive(aZipArchive), mItem(aZipItem), mCurPos(0), mInflatedFileBuffer(0) { #ifndef STANDALONE MOZ_COUNT_CTOR(nsZipRead); #endif } nsZipRead::~nsZipRead() { PR_FREEIF(mInflatedFileBuffer); #ifndef STANDALONE MOZ_COUNT_DTOR(nsZipRead); #endif } //------------------------------------------ // nsZipFind constructor and destructor //------------------------------------------ #ifndef STANDALONE MOZ_DECL_CTOR_COUNTER(nsZipFind); #endif nsZipFind::nsZipFind( nsZipArchive* aZip, char* aPattern, PRBool aRegExp ) : kMagic(ZIPFIND_MAGIC), mArchive(aZip), mPattern(aPattern), mSlot(0), mItem(0), mRegExp(aRegExp) { #ifndef STANDALONE MOZ_COUNT_CTOR(nsZipFind); #endif } nsZipFind::~nsZipFind() { if (mPattern != 0 ) PL_strfree( mPattern ); #ifndef STANDALONE MOZ_COUNT_DTOR(nsZipFind); #endif } //------------------------------------------ // nsZipFind::GetArchive //------------------------------------------ nsZipArchive* nsZipFind::GetArchive() { if (!mArchive) return NULL; return mArchive; } //------------------------------------------ // helper functions //------------------------------------------ /* * x t o i n t * * Converts a two byte ugly endianed integer * to our platform's integer. */ static PRUint16 xtoint (unsigned char *ii) { return (PRUint16) ((ii [0]) | (ii [1] << 8)); } /* * x t o l o n g * * Converts a four byte ugly endianed integer * to our platform's integer. */ static PRUint32 xtolong (unsigned char *ll) { PRUint32 ret; ret = ( (((PRUint32) ll [0]) << 0) | (((PRUint32) ll [1]) << 8) | (((PRUint32) ll [2]) << 16) | (((PRUint32) ll [3]) << 24) ); return ret; } /* * ExtractMode * * Extracts bits 17-24 from a 32-bit unsigned long * representation of the external attributes field. * Subsequently it tacks on the implicit user-read * bit. */ static PRUint16 ExtractMode(PRUint32 ext_attr) { ext_attr &= 0x00FF0000; ext_attr >>= 16; ext_attr |= 0x00000100; return (PRUint16) ext_attr; }