From 61231d808086b0533a05e6fdc321e357dca460a8 Mon Sep 17 00:00:00 2001 From: "gavin%gavinsharp.com" Date: Sun, 24 Sep 2006 15:13:57 +0000 Subject: [PATCH] Bug 337561: Merge nsJARDirectoryInputStream into nsJARInputStream, patch by Alfred Kayser , r+sr=darin --- modules/libjar/nsIZipReader.idl | 12 +- modules/libjar/nsJAR.cpp | 49 +-- modules/libjar/nsJAR.h | 4 +- modules/libjar/nsJARChannel.cpp | 14 +- modules/libjar/nsJARDirectoryInputStream.cpp | 309 ------------------- modules/libjar/nsJARInputStream.cpp | 224 ++++++++++++-- modules/libjar/nsJARInputStream.h | 25 +- modules/libjar/objs.mk | 1 - 8 files changed, 268 insertions(+), 370 deletions(-) diff --git a/modules/libjar/nsIZipReader.idl b/modules/libjar/nsIZipReader.idl index c7afd76e350..45c61b56eba 100644 --- a/modules/libjar/nsIZipReader.idl +++ b/modules/libjar/nsIZipReader.idl @@ -90,7 +90,7 @@ interface nsIZipEntry : nsISupports readonly attribute boolean isSynthetic; }; -[scriptable, uuid(08fa7e4b-4eec-4cae-b7fe-a3c1d1e9aed2)] +[scriptable, uuid(5cce7f53-23b3-47f8-be05-122c0ba703fd)] interface nsIZipReader : nsISupports { /** @@ -186,8 +186,18 @@ interface nsIZipReader : nsISupports /** * Returns an input stream containing the contents of the specified zip * entry. + * @param zipEntry the name of the entry to open the stream from */ nsIInputStream getInputStream(in string zipEntry); + + /** + * Returns an input stream containing the contents of the specified zip + * entry. If the entry refers to a directory (ends with '/'), a directory stream + * is opened, otherwise the contents of the file entry is returned. + * @param aJarSpec the Spec of the URI for the JAR (only used for directory streams) + * @param zipEntry the name of the entry to open the stream from + */ + nsIInputStream getInputStreamWithSpec(in AUTF8String aJarSpec, in string zipEntry); }; //////////////////////////////////////////////////////////////////////////////// diff --git a/modules/libjar/nsJAR.cpp b/modules/libjar/nsJAR.cpp index 80e5c471c37..897f2213c0a 100644 --- a/modules/libjar/nsJAR.cpp +++ b/modules/libjar/nsJAR.cpp @@ -54,8 +54,6 @@ #include #endif -static PRTime GetModTime(PRUint16 aDate, PRUint16 aTime); - //---------------------------------------------- // nsJARManifestItem declaration //---------------------------------------------- @@ -314,29 +312,42 @@ nsJAR::FindEntries(const char *aPattern, nsIUTF8StringEnumerator **result) NS_IMETHODIMP nsJAR::GetInputStream(const char* aFilename, nsIInputStream** result) { + return GetInputStreamWithSpec(EmptyCString(), aFilename, result); +} + +NS_IMETHODIMP +nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec, + const char* aEntryName, nsIInputStream** result) +{ + NS_ENSURE_ARG_POINTER(aEntryName); NS_ENSURE_ARG_POINTER(result); - // First check if item exists in jar - nsZipItem *item = mZip.GetItem(aFilename); - if (!item) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST; - - // Open jarfile, to get its own filedescriptor for the stream - PRFileDesc *fd = OpenFile(); - if (!fd) return NS_ERROR_FAILURE; - + // Watch out for the jar:foo.zip!/ (aDir is empty) top-level special case! + PRFileDesc *fd = nsnull; + nsZipItem *item = nsnull; + if (*aEntryName) { + // First check if item exists in jar + item = mZip.GetItem(aEntryName); + if (!item) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST; + } nsJARInputStream* jis = new nsJARInputStream(); - if (!jis) return NS_ERROR_OUT_OF_MEMORY; + // addref now so we can call InitFile/InitDirectory() + NS_ENSURE_TRUE(jis, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(*result = jis); - // addref now so we can call Init() - *result = jis; - NS_ADDREF(*result); - - nsresult rv = jis->Init(this, item, fd); + nsresult rv = NS_OK; + if (!item || item->isDirectory) { + rv = jis->InitDirectory(&mZip, aJarDirSpec, aEntryName); + } else { + // Open jarfile, to get its own filedescriptor for the stream + fd = OpenFile(); + rv = fd ? jis->InitFile(&mZip, item, fd) : NS_ERROR_FAILURE; + } if (NS_FAILED(rv)) { NS_RELEASE(*result); - return rv; + if (fd) PR_Close(fd); } - return NS_OK; + return rv; } //---------------------------------------------- @@ -1269,7 +1280,7 @@ nsZipReaderCache::Observe(nsISupports *aSubject, return NS_OK; } -static PRTime GetModTime(PRUint16 aDate, PRUint16 aTime) +PRTime GetModTime(PRUint16 aDate, PRUint16 aTime) { char buffer[17]; diff --git a/modules/libjar/nsJAR.h b/modules/libjar/nsJAR.h index a3caec936aa..96502412903 100644 --- a/modules/libjar/nsJAR.h +++ b/modules/libjar/nsJAR.h @@ -86,6 +86,8 @@ typedef enum JAR_NOT_SIGNED = 7 } JARManifestStatusType; +PRTime GetModTime(PRUint16 aDate, PRUint16 aTime); + /*------------------------------------------------------------------------- * Class nsJAR declaration. * nsJAR serves as an XPCOM wrapper for nsZipArchive with the addition of @@ -182,8 +184,8 @@ private: PRUint32 mSize; /* size in original file */ PRUint32 mRealsize; /* inflated size */ PRUint32 mCrc32; - PRUint16 mTime; PRUint16 mDate; + PRUint16 mTime; PRUint8 mCompression; PRPackedBool mIsDirectory; PRPackedBool mIsSynthetic; diff --git a/modules/libjar/nsJARChannel.cpp b/modules/libjar/nsJARChannel.cpp index 9c54464f33f..87b698b4ac9 100644 --- a/modules/libjar/nsJARChannel.cpp +++ b/modules/libjar/nsJARChannel.cpp @@ -40,7 +40,6 @@ #include "nsJAR.h" #include "nsJARChannel.h" #include "nsJARProtocolHandler.h" -#include "nsJARDirectoryInputStream.h" #include "nsMimeTypes.h" #include "nsNetUtil.h" #include "nsInt64.h" @@ -145,19 +144,16 @@ nsJARInputThunk::EnsureJarStream() if (NS_FAILED(rv)) return rv; if (ENTRY_IS_DIRECTORY(mJarEntry)) { - // This isn't simply part of nsJAR::GetInputStream because it shouldn't - // be possible to get an input stream for a directory in a zip via that - // path, just as it isn't possible to get a directory stream via an - // nsIFileInputStream + // A directory stream also needs the Spec of the FullJarURI + // because is included in the stream data itself. nsCAutoString jarDirSpec; rv = mFullJarURI->GetAsciiSpec(jarDirSpec); if (NS_FAILED(rv)) return rv; - rv = nsJARDirectoryInputStream::Create(mJarReader, - jarDirSpec, - mJarEntry.get(), - getter_AddRefs(mJarStream)); + rv = mJarReader->GetInputStreamWithSpec(jarDirSpec, + mJarEntry.get(), + getter_AddRefs(mJarStream)); } else { rv = mJarReader->GetInputStream(mJarEntry.get(), diff --git a/modules/libjar/nsJARDirectoryInputStream.cpp b/modules/libjar/nsJARDirectoryInputStream.cpp index 3f96f541d1f..e69de29bb2d 100644 --- a/modules/libjar/nsJARDirectoryInputStream.cpp +++ b/modules/libjar/nsJARDirectoryInputStream.cpp @@ -1,309 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* nsJARDirectoryInputStream.cpp - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla 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/MPL/ - * - * 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 libjar code. - * - * The Initial Developer of the Original Code is - * Jeff Walden . - * Portions created by the Initial Developer are Copyright (C) 2006 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nsJARDirectoryInputStream.h" -#include "nsNetUtil.h" -#include "nsEscape.h" -#include "nsIFile.h" - - -/*--------------------------------------------- - * nsISupports implementation - *--------------------------------------------*/ - -NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARDirectoryInputStream, nsIInputStream) - -/*---------------------------------------------------------- - * nsJARDirectoryInputStream implementation - *--------------------------------------------------------*/ - -NS_IMETHODIMP -nsJARDirectoryInputStream::Available(PRUint32 *_retval) -{ - if (NS_FAILED(mStatus)) - return mStatus; - - *_retval = mBuffer.Length(); - return NS_OK; -} - -NS_IMETHODIMP -nsJARDirectoryInputStream::Read(char* buf, PRUint32 count, PRUint32 *bytesRead) -{ - if (mStatus == NS_BASE_STREAM_CLOSED) { - *bytesRead = 0; - return NS_OK; - } - - if (NS_FAILED(mStatus)) - return mStatus; - - nsresult rv; - - // If the buffer contains data, copy what's there up to the desired amount - PRUint32 numRead = CopyDataToBuffer(buf, count); - - if (count > 0) { - // empty the buffer and start writing directory entry lines to it - mBuffer.Truncate(); - mBufPos = 0; - PRUint32 arrayLen = mArray.Count(); - for ( ;count > mBuffer.Length(); mArrPos++) { - // have we consumed all the directory contents? - if (arrayLen <= mArrPos) - break; - - // Name - const char * entryName = mArray[mArrPos]->get(); - PRUint32 entryNameLen = mArray[mArrPos]->Length(); - const char * relativeName = entryName + mDirNameLen; - PRUint32 relativeNameLen = entryNameLen - mDirNameLen; - - nsCOMPtr ze; - rv = mZip->GetEntry(entryName, getter_AddRefs(ze)); - if (NS_FAILED(rv)) return rv; - - // Type - PRBool isDir = PR_FALSE; - rv = ze->GetIsDirectory(&isDir); - if (NS_FAILED(rv)) return rv; - - // Size (real, not compressed) - PRUint32 itemRealSize = 0; - rv = ze->GetRealSize(&itemRealSize); - if (NS_FAILED(rv)) return rv; - - // Last Modified Time - PRTime lmt = LL_Zero(); - rv = ze->GetLastModifiedTime(&lmt); - if (NS_FAILED(rv)) return rv; - - PRExplodedTime tm; - PR_ExplodeTime(lmt, PR_GMTParameters, &tm); - char itemLastModTime[65]; - PR_FormatTimeUSEnglish(itemLastModTime, - sizeof(itemLastModTime), - " %a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", - &tm); - - // write a 201: line to the buffer for this item - // 200: filename content-length last-modified file-type - mBuffer.AppendLiteral("201: "); - - // Names must be escaped and relative, so use the pre-calculated length - // of the directory name as the offset into the string - // NS_EscapeURL adds the escaped URL to the give string buffer - NS_EscapeURL(relativeName, relativeNameLen, - esc_Minimal | esc_AlwaysCopy, mBuffer); - - mBuffer.AppendLiteral(" "); - mBuffer.AppendInt(itemRealSize, 10); - mBuffer.Append(itemLastModTime); // starts/ends with ' ' - mBuffer.Append(isDir ? "DIRECTORY\n" : "FILE\n"); - } - - // Copy up to the desired amount of data to buffer - numRead += CopyDataToBuffer(buf, count); - } - - *bytesRead = numRead; - return NS_OK; -} - -NS_IMETHODIMP -nsJARDirectoryInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval) -{ - // XXX write me! - NS_NOTREACHED("Consumers should be using Read()!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -nsJARDirectoryInputStream::IsNonBlocking(PRBool *aNonBlocking) -{ - *aNonBlocking = PR_FALSE; - return NS_OK; -} - -NS_IMETHODIMP -nsJARDirectoryInputStream::Close() -{ - mStatus = NS_BASE_STREAM_CLOSED; - return NS_OK; -} - -/* static */ nsresult -nsJARDirectoryInputStream::Create(nsIZipReader* aZip, - const nsACString& aJarDirSpec, - const char* aDir, - nsIInputStream** result) -{ - NS_ENSURE_ARG_POINTER(aZip); - NS_ENSURE_ARG_POINTER(aDir); - NS_ENSURE_ARG_POINTER(result); - - nsJARDirectoryInputStream* jdis = new nsJARDirectoryInputStream(); - if (!jdis) return NS_ERROR_OUT_OF_MEMORY; - - // addref now so we can call Init() - *result = jdis; - NS_ADDREF(*result); - - nsresult rv = jdis->Init(aZip, aJarDirSpec, aDir); - if (NS_FAILED(rv)) NS_RELEASE(*result); - - return rv; -} - -nsresult -nsJARDirectoryInputStream::Init(nsIZipReader* aZip, - const nsACString& aJarDirSpec, - const char* aDir) -{ - // Ensure that aDir is really a directory and that it exists. - // Watch out for the jar:foo.zip!/ (aDir is empty) top-level - // special case! - nsresult rv; - - // Keep the zipReader for getting the actual zipItems - mZip = aZip; - - if (*aDir) { - nsCOMPtr ze; - rv = aZip->GetEntry(aDir, getter_AddRefs(ze)); - if (NS_FAILED(rv)) return rv; - - PRBool isDir; - rv = ze->GetIsDirectory(&isDir); - if (NS_FAILED(rv)) return rv; - - if (!isDir) - return NS_ERROR_ILLEGAL_VALUE; - } - - // We can get aDir's contents as strings via FindEntries - // with the following pattern (see nsIZipReader.findEntries docs) - // assuming dirName is properly escaped: - // - // dirName + "?*~" + dirName + "?*/?*" - nsDependentCString dirName(aDir); - mDirNameLen = dirName.Length(); - - // iterate through dirName and copy it to escDirName, escaping chars - // which are special at the "top" level of the regexp so FindEntries - // works correctly - nsCAutoString escDirName; - const char* curr = dirName.BeginReading(); - const char* end = dirName.EndReading(); - while (curr != end) { - switch (*curr) { - case '*': - case '?': - case '$': - case '[': - case ']': - case '^': - case '~': - case '(': - case ')': - case '\\': - escDirName.Append('\\'); - // fall through - default: - escDirName.Append(*curr); - } - ++curr; - } - - nsCAutoString pattern = escDirName + NS_LITERAL_CSTRING("?*~") + - escDirName + NS_LITERAL_CSTRING("?*/?*"); - - nsCOMPtr dirEnum; - rv = aZip->FindEntries(pattern.get(), getter_AddRefs(dirEnum)); - if (NS_FAILED(rv)) return rv; - - PRBool more; - nsCAutoString entryName; - while (NS_SUCCEEDED(dirEnum->HasMore(&more)) && more) { - rv = dirEnum->GetNext(entryName); - if (NS_SUCCEEDED(rv)) { - mArray.AppendCString(entryName); - } - } - - // Sort it - mArray.Sort(); - - mBuffer.AppendLiteral("300: "); - mBuffer.Append(aJarDirSpec); - mBuffer.AppendLiteral("\n200: filename content-length last-modified file-type\n"); - - return NS_OK; -} - -PRUint32 -nsJARDirectoryInputStream::CopyDataToBuffer(char* &aBuffer, PRUint32 &aCount) -{ - PRUint32 writeLength = PR_MIN(aCount, mBuffer.Length() - mBufPos); - - if (writeLength > 0) { - memcpy(aBuffer, mBuffer.get() + mBufPos, writeLength); - mBufPos += writeLength; - aCount -= writeLength; - aBuffer += writeLength; - } - - // return number of bytes copied to the buffer so the - // Read method can return the number of bytes copied - return writeLength; -} - -//---------------------------------------------- -// nsJARDirectoryInputStream constructor and destructor -//---------------------------------------------- - -nsJARDirectoryInputStream::nsJARDirectoryInputStream() - : mStatus(NS_OK), mArrPos(0), mBufPos(0) -{ -} - -nsJARDirectoryInputStream::~nsJARDirectoryInputStream() -{ - Close(); -} diff --git a/modules/libjar/nsJARInputStream.cpp b/modules/libjar/nsJARInputStream.cpp index 2672e1cdcde..9b7157ffef6 100644 --- a/modules/libjar/nsJARInputStream.cpp +++ b/modules/libjar/nsJARInputStream.cpp @@ -42,6 +42,9 @@ #include "zipstruct.h" // defines ZIP compression codes #include "nsZipArchive.h" +#include "nsNetUtil.h" +#include "nsEscape.h" +#include "nsIFile.h" /*--------------------------------------------- * nsISupports implementation @@ -54,11 +57,11 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputStream, nsIInputStream) *--------------------------------------------------------*/ nsresult -nsJARInputStream::Init(nsJAR* aJAR, nsZipItem *item, PRFileDesc *fd) +nsJARInputStream::InitFile(nsZipArchive* aZip, nsZipItem *item, PRFileDesc *fd) { nsresult rv; - NS_ENSURE_ARG_POINTER(aJAR); + NS_ENSURE_ARG_POINTER(aZip); NS_ENSURE_ARG_POINTER(item); NS_ENSURE_ARG_POINTER(fd); @@ -93,11 +96,96 @@ nsJARInputStream::Init(nsJAR* aJAR, nsZipItem *item, PRFileDesc *fd) } //-- Set filepointer to start of item - rv = aJAR->mZip.SeekToItem(item, mFd); + rv = aZip->SeekToItem(item, mFd); NS_ENSURE_SUCCESS(rv, NS_ERROR_FILE_CORRUPTED); // Open for reading mClosed = PR_FALSE; + mCurPos = 0; + return NS_OK; +} + +nsresult +nsJARInputStream::InitDirectory(nsZipArchive* aZip, + const nsACString& aJarDirSpec, + const char* aDir) +{ + NS_ENSURE_ARG_POINTER(aZip); + NS_ENSURE_ARG_POINTER(aDir); + + // Mark it as closed, in case something fails in initialisation + mClosed = PR_TRUE; + mDirectory = PR_TRUE; + + // Keep the zipReader for getting the actual zipItems + mZip = aZip; + nsZipFind *find; + nsresult rv; + if (*aDir) { + // We can get aDir's contents as strings via FindEntries + // with the following pattern (see nsIZipReader.findEntries docs) + // assuming dirName is properly escaped: + // + // dirName + "?*~" + dirName + "?*/?*" + nsDependentCString dirName(aDir); + mNameLen = dirName.Length(); + + // iterate through dirName and copy it to escDirName, escaping chars + // which are special at the "top" level of the regexp so FindEntries + // works correctly + nsCAutoString escDirName; + const char* curr = dirName.BeginReading(); + const char* end = dirName.EndReading(); + while (curr != end) { + switch (*curr) { + case '*': + case '?': + case '$': + case '[': + case ']': + case '^': + case '~': + case '(': + case ')': + case '\\': + escDirName.Append('\\'); + // fall through + default: + escDirName.Append(*curr); + } + ++curr; + } + nsCAutoString pattern = escDirName + NS_LITERAL_CSTRING("?*~") + + escDirName + NS_LITERAL_CSTRING("?*/?*"); + rv = aZip->FindInit(pattern.get(), &find); + } else { + mNameLen = 0; + rv = aZip->FindInit(nsnull, &find); + } + if (NS_FAILED(rv)) return rv; + + const char *name; + while ((rv = find->FindNext( &name )) == NS_OK) { + // No need to copy string, just share the one from nsZipArchive + mArray.AppendCString(nsDependentCString(name)); + } + delete find; + + if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST && NS_FAILED(rv)) { + return NS_ERROR_FAILURE; // no error translation + } + + // Sort it + mArray.Sort(); + + mBuffer.AssignLiteral("300: "); + mBuffer.Append(aJarDirSpec); + mBuffer.AppendLiteral("\n200: filename content-length last-modified file-type\n"); + + // Open for reading + mClosed = PR_FALSE; + mCurPos = 0; + mArrPos = 0; return NS_OK; } @@ -107,7 +195,9 @@ nsJARInputStream::Available(PRUint32 *_retval) if (mClosed) return NS_BASE_STREAM_CLOSED; - if (mInflate) + if (mDirectory) + *_retval = mBuffer.Length(); + else if (mInflate) *_retval = mInflate->mOutSize - mInflate->mZs.total_out; else *_retval = mInSize - mCurPos; @@ -126,28 +216,31 @@ nsJARInputStream::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead) if (mClosed) return rv; - if (mInflate) { - rv = ContinueInflate(aBuffer, aCount, aBytesRead); + if (mDirectory) { + rv = ReadDirectory(aBuffer, aCount, aBytesRead); } else { - PRInt32 bytesRead = 0; - if (aCount > mInSize - mCurPos) - aCount = mInSize - mCurPos; - if (aCount) { - bytesRead = PR_Read(mFd, aBuffer, aCount); - if (bytesRead < 0) - return NS_ERROR_FILE_CORRUPTED; - mCurPos += bytesRead; + if (mInflate) { + rv = ContinueInflate(aBuffer, aCount, aBytesRead); + } else { + PRInt32 bytesRead = 0; + aCount = PR_MIN(aCount, mInSize - mCurPos); + if (aCount) { + bytesRead = PR_Read(mFd, aBuffer, aCount); + if (bytesRead < 0) + return NS_ERROR_FILE_CORRUPTED; + mCurPos += bytesRead; + } + *aBytesRead = bytesRead; + } + + // be aggressive about closing! + // note that sometimes, we will close mFd before we've finished + // deflating - this is because zlib buffers the input + // So, don't free the ReadBuf/InflateStruct yet. + if (mCurPos >= mInSize && mFd) { + PR_Close(mFd); + mFd = nsnull; } - *aBytesRead = bytesRead; - } - - // be aggressive about closing! - // note that sometimes, we will close mFd before we've finished - // deflating - this is because zlib buffers the input - // So, don't free the ReadBuf/InflateStruct yet. - if (mCurPos >= mInSize && mFd) { - PR_Close(mFd); - mFd = nsnull; } return rv; } @@ -201,7 +294,7 @@ nsJARInputStream::ContinueInflate(char* aBuffer, PRUint32 aCount, if (mInflate->mZs.avail_in == 0 && mCurPos < mInSize) { // time to fill the buffer! - PRUint32 bytesToRead = (mInSize-mCurPos < ZIP_BUFLEN) ? mInSize-mCurPos : ZIP_BUFLEN; + PRUint32 bytesToRead = PR_MIN(mInSize - mCurPos, ZIP_BUFLEN); NS_ASSERTION(mFd, "File handle missing"); PRInt32 bytesRead = PR_Read(mFd, mInflate->mReadBuf, bytesToRead); @@ -244,3 +337,84 @@ nsJARInputStream::ContinueInflate(char* aBuffer, PRUint32 aCount, return NS_OK; } + +nsresult +nsJARInputStream::ReadDirectory(char* buf, PRUint32 count, PRUint32 *bytesRead) +{ + // No need to check the args, ::Read did that, but assert them at least + NS_ASSERTION(aBuffer,"aBuffer parameter must not be null"); + NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null"); + + // If the buffer contains data, copy what's there up to the desired amount + PRUint32 numRead = CopyDataToBuffer(buf, count); + + if (count > 0) { + // empty the buffer and start writing directory entry lines to it + mBuffer.Truncate(); + mCurPos = 0; + const PRUint32 arrayLen = mArray.Count(); + + for ( ;count > mBuffer.Length(); mArrPos++) { + // have we consumed all the directory contents? + if (arrayLen <= mArrPos) + break; + + const char * entryName = mArray[mArrPos]->get(); + PRUint32 entryNameLen = mArray[mArrPos]->Length(); + nsZipItem* ze = mZip->GetItem(entryName); + NS_ENSURE_TRUE(ze, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST); + + // Last Modified Time + PRExplodedTime tm; + PR_ExplodeTime(GetModTime(ze->date, ze->time), PR_GMTParameters, &tm); + char itemLastModTime[65]; + PR_FormatTimeUSEnglish(itemLastModTime, + sizeof(itemLastModTime), + " %a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", + &tm); + + // write a 201: line to the buffer for this item + // 200: filename content-length last-modified file-type + mBuffer.AppendLiteral("201: "); + + // Names must be escaped and relative, so use the pre-calculated length + // of the directory name as the offset into the string + // NS_EscapeURL adds the escaped URL to the give string buffer + NS_EscapeURL(entryName + mNameLen, + entryNameLen - mNameLen, + esc_Minimal | esc_AlwaysCopy, + mBuffer); + + mBuffer.Append(' '); + mBuffer.AppendInt(ze->realsize, 10); + mBuffer.Append(itemLastModTime); // starts/ends with ' ' + if (ze->isDirectory) + mBuffer.AppendLiteral("DIRECTORY\n"); + else + mBuffer.AppendLiteral("FILE\n"); + } + + // Copy up to the desired amount of data to buffer + numRead += CopyDataToBuffer(buf, count); + } + + *bytesRead = numRead; + return NS_OK; +} + +PRUint32 +nsJARInputStream::CopyDataToBuffer(char* &aBuffer, PRUint32 &aCount) +{ + const PRUint32 writeLength = PR_MIN(aCount, mBuffer.Length() - mCurPos); + + if (writeLength > 0) { + memcpy(aBuffer, mBuffer.get() + mCurPos, writeLength); + mCurPos += writeLength; + aCount -= writeLength; + aBuffer += writeLength; + } + + // return number of bytes copied to the buffer so the + // Read method can return the number of bytes copied + return writeLength; +} diff --git a/modules/libjar/nsJARInputStream.h b/modules/libjar/nsJARInputStream.h index ec8249dc97d..12cbd68f97f 100644 --- a/modules/libjar/nsJARInputStream.h +++ b/modules/libjar/nsJARInputStream.h @@ -52,19 +52,23 @@ class nsJARInputStream : public nsIInputStream { public: nsJARInputStream() : - mFd(nsnull), mInSize(0), mCurPos(0), - mClosed(PR_FALSE), mInflate(nsnull) { } + mFd(nsnull), mInSize(0), mCurPos(0), + mClosed(PR_FALSE), mInflate(nsnull), mDirectory(0) { } + ~nsJARInputStream() { Close(); } + NS_DECL_ISUPPORTS NS_DECL_NSIINPUTSTREAM - nsresult Init(nsJAR* jar, nsZipItem *item, PRFileDesc *fd); + nsresult InitFile(nsZipArchive* aZip, nsZipItem *item, PRFileDesc *fd); + nsresult InitDirectory(nsZipArchive* aZip, + const nsACString& aJarDirSpec, + const char* aDir); private: PRFileDesc* mFd; // My own file handle, for reading PRUint32 mInSize; // Size in original file PRUint32 mCurPos; // Current position in input - PRPackedBool mClosed; // Whether the stream is closed struct InflateStruct { PRUint32 mOutSize; // inflated size @@ -75,8 +79,19 @@ class nsJARInputStream : public nsIInputStream }; struct InflateStruct * mInflate; - ~nsJARInputStream() { Close(); } + /* For directory reading */ + nsZipArchive* mZip; // the zipReader + PRUint32 mNameLen; // length of dirname + nsCAutoString mBuffer; // storage for generated text of stream + PRUint32 mArrPos; // current position within mArray + nsCStringArray mArray; // array of names in (zip) directory + + PRPackedBool mDirectory; + PRPackedBool mClosed; // Whether the stream is closed + nsresult ContinueInflate(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead); + nsresult ReadDirectory(char* aBuf, PRUint32 aCount, PRUint32* aBytesRead); + PRUint32 CopyDataToBuffer(char* &aBuffer, PRUint32 &aCount); }; #endif /* nsJARINPUTSTREAM_h__ */ diff --git a/modules/libjar/objs.mk b/modules/libjar/objs.mk index 5c32b638fa8..d87cda66de8 100644 --- a/modules/libjar/objs.mk +++ b/modules/libjar/objs.mk @@ -43,7 +43,6 @@ MODULES_STANDALONE_LCPPSRCS = \ MODULES_LIBJAR_LCPPSRCS = \ $(MODULES_STANDALONE_LCPPSRCS) \ nsJARInputStream.cpp \ - nsJARDirectoryInputStream.cpp \ nsJAR.cpp \ nsJARFactory.cpp \ nsXPTZipLoader.cpp \