michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: */ michael@0: michael@0: #include "StreamFunctions.h" michael@0: #include "nsZipDataStream.h" michael@0: #include "nsIStringStream.h" michael@0: #include "nsISeekableStream.h" michael@0: #include "nsDeflateConverter.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsMemory.h" michael@0: michael@0: #define ZIP_METHOD_STORE 0 michael@0: #define ZIP_METHOD_DEFLATE 8 michael@0: michael@0: /** michael@0: * nsZipDataStream handles the writing an entry's into the zip file. michael@0: * It is set up to wither write the data as is, or in the event that compression michael@0: * has been requested to pass it through a stream converter. michael@0: * Currently only the deflate compression method is supported. michael@0: * The CRC checksum for the entry's data is also generated here. michael@0: */ michael@0: NS_IMPL_ISUPPORTS(nsZipDataStream, nsIStreamListener, michael@0: nsIRequestObserver) michael@0: michael@0: nsresult nsZipDataStream::Init(nsZipWriter *aWriter, michael@0: nsIOutputStream *aStream, michael@0: nsZipHeader *aHeader, michael@0: int32_t aCompression) michael@0: { michael@0: mWriter = aWriter; michael@0: mHeader = aHeader; michael@0: mStream = aStream; michael@0: mHeader->mCRC = crc32(0L, Z_NULL, 0); michael@0: michael@0: nsresult rv = NS_NewSimpleStreamListener(getter_AddRefs(mOutput), aStream, michael@0: nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aCompression > 0) { michael@0: mHeader->mMethod = ZIP_METHOD_DEFLATE; michael@0: nsCOMPtr converter = michael@0: new nsDeflateConverter(aCompression); michael@0: NS_ENSURE_TRUE(converter, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: rv = converter->AsyncConvertData("uncompressed", "rawdeflate", mOutput, michael@0: nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mOutput = do_QueryInterface(converter, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: mHeader->mMethod = ZIP_METHOD_STORE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void onDataAvailable (in nsIRequest aRequest, in nsISupports aContext, michael@0: * in nsIInputStream aInputStream, michael@0: * in unsigned long long aOffset, in unsigned long aCount); */ michael@0: NS_IMETHODIMP nsZipDataStream::OnDataAvailable(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsIInputStream *aInputStream, michael@0: uint64_t aOffset, michael@0: uint32_t aCount) michael@0: { michael@0: if (!mOutput) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsAutoArrayPtr buffer(new char[aCount]); michael@0: NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: nsresult rv = ZW_ReadData(aInputStream, buffer.get(), aCount); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return ProcessData(aRequest, aContext, buffer.get(), aOffset, aCount); michael@0: } michael@0: michael@0: /* void onStartRequest (in nsIRequest aRequest, in nsISupports aContext); */ michael@0: NS_IMETHODIMP nsZipDataStream::OnStartRequest(nsIRequest *aRequest, michael@0: nsISupports *aContext) michael@0: { michael@0: if (!mOutput) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: return mOutput->OnStartRequest(aRequest, aContext); michael@0: } michael@0: michael@0: /* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext, michael@0: * in nsresult aStatusCode); */ michael@0: NS_IMETHODIMP nsZipDataStream::OnStopRequest(nsIRequest *aRequest, michael@0: nsISupports *aContext, michael@0: nsresult aStatusCode) michael@0: { michael@0: if (!mOutput) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsresult rv = mOutput->OnStopRequest(aRequest, aContext, aStatusCode); michael@0: mOutput = nullptr; michael@0: if (NS_FAILED(rv)) { michael@0: mWriter->EntryCompleteCallback(mHeader, rv); michael@0: } michael@0: else { michael@0: rv = CompleteEntry(); michael@0: rv = mWriter->EntryCompleteCallback(mHeader, rv); michael@0: } michael@0: michael@0: mStream = nullptr; michael@0: mWriter = nullptr; michael@0: mHeader = nullptr; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: inline nsresult nsZipDataStream::CompleteEntry() michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr seekable = do_QueryInterface(mStream, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: int64_t pos; michael@0: rv = seekable->Tell(&pos); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mHeader->mCSize = pos - mHeader->mOffset - mHeader->GetFileHeaderLength(); michael@0: mHeader->mWriteOnClose = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsZipDataStream::ProcessData(nsIRequest *aRequest, michael@0: nsISupports *aContext, char *aBuffer, michael@0: uint64_t aOffset, uint32_t aCount) michael@0: { michael@0: mHeader->mCRC = crc32(mHeader->mCRC, michael@0: reinterpret_cast(aBuffer), michael@0: aCount); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr stream = michael@0: do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: stream->ShareData(aBuffer, aCount); michael@0: rv = mOutput->OnDataAvailable(aRequest, aContext, stream, aOffset, aCount); michael@0: mHeader->mUSize += aCount; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult nsZipDataStream::ReadStream(nsIInputStream *aStream) michael@0: { michael@0: if (!mOutput) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: nsresult rv = OnStartRequest(nullptr, nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoArrayPtr buffer(new char[4096]); michael@0: NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: uint32_t read = 0; michael@0: uint32_t offset = 0; michael@0: do michael@0: { michael@0: rv = aStream->Read(buffer.get(), 4096, &read); michael@0: if (NS_FAILED(rv)) { michael@0: OnStopRequest(nullptr, nullptr, rv); michael@0: return rv; michael@0: } michael@0: michael@0: if (read > 0) { michael@0: rv = ProcessData(nullptr, nullptr, buffer.get(), offset, read); michael@0: if (NS_FAILED(rv)) { michael@0: OnStopRequest(nullptr, nullptr, rv); michael@0: return rv; michael@0: } michael@0: offset += read; michael@0: } michael@0: } while (read > 0); michael@0: michael@0: return OnStopRequest(nullptr, nullptr, NS_OK); michael@0: }