1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/file/ArchiveZipFile.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,411 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "ArchiveZipFile.h" 1.11 +#include "ArchiveZipEvent.h" 1.12 + 1.13 +#include "nsIInputStream.h" 1.14 +#include "zlib.h" 1.15 +#include "mozilla/Attributes.h" 1.16 + 1.17 +USING_FILE_NAMESPACE 1.18 + 1.19 +#define ZIP_CHUNK 16384 1.20 + 1.21 +/** 1.22 + * Input stream object for zip files 1.23 + */ 1.24 +class ArchiveInputStream MOZ_FINAL : public nsIInputStream, 1.25 + public nsISeekableStream 1.26 +{ 1.27 +public: 1.28 + ArchiveInputStream(uint64_t aParentSize, 1.29 + nsIInputStream* aInputStream, 1.30 + nsString& aFilename, 1.31 + uint32_t aStart, 1.32 + uint32_t aLength, 1.33 + ZipCentral& aCentral) 1.34 + : mCentral(aCentral), 1.35 + mFilename(aFilename), 1.36 + mStart(aStart), 1.37 + mLength(aLength), 1.38 + mStatus(NotStarted) 1.39 + { 1.40 + MOZ_COUNT_CTOR(ArchiveInputStream); 1.41 + 1.42 + // Reset the data: 1.43 + memset(&mData, 0, sizeof(mData)); 1.44 + 1.45 + mData.parentSize = aParentSize; 1.46 + mData.inputStream = aInputStream; 1.47 + } 1.48 + 1.49 + virtual ~ArchiveInputStream() 1.50 + { 1.51 + MOZ_COUNT_DTOR(ArchiveInputStream); 1.52 + Close(); 1.53 + } 1.54 + 1.55 + NS_DECL_THREADSAFE_ISUPPORTS 1.56 + NS_DECL_NSIINPUTSTREAM 1.57 + NS_DECL_NSISEEKABLESTREAM 1.58 + 1.59 +private: 1.60 + nsresult Init(); 1.61 + 1.62 +private: // data 1.63 + ZipCentral mCentral; 1.64 + nsString mFilename; 1.65 + uint32_t mStart; 1.66 + uint32_t mLength; 1.67 + 1.68 + z_stream mZs; 1.69 + 1.70 + enum { 1.71 + NotStarted, 1.72 + Started, 1.73 + Done 1.74 + } mStatus; 1.75 + 1.76 + struct { 1.77 + uint64_t parentSize; 1.78 + nsCOMPtr<nsIInputStream> inputStream; 1.79 + 1.80 + unsigned char input[ZIP_CHUNK]; 1.81 + uint32_t sizeToBeRead; 1.82 + uint32_t cursor; 1.83 + 1.84 + bool compressed; // a zip file can contain stored or compressed files 1.85 + } mData; 1.86 +}; 1.87 + 1.88 +NS_IMPL_ISUPPORTS(ArchiveInputStream, 1.89 + nsIInputStream, 1.90 + nsISeekableStream) 1.91 + 1.92 +nsresult 1.93 +ArchiveInputStream::Init() 1.94 +{ 1.95 + nsresult rv; 1.96 + 1.97 + memset(&mZs, 0, sizeof(z_stream)); 1.98 + int zerr = inflateInit2(&mZs, -MAX_WBITS); 1.99 + if (zerr != Z_OK) { 1.100 + return NS_ERROR_OUT_OF_MEMORY; 1.101 + } 1.102 + 1.103 + mData.sizeToBeRead = ArchiveZipItem::StrToInt32(mCentral.size); 1.104 + 1.105 + uint32_t offset = ArchiveZipItem::StrToInt32(mCentral.localhdr_offset); 1.106 + 1.107 + // The file is corrupt 1.108 + if (offset + ZIPLOCAL_SIZE > mData.parentSize) { 1.109 + return NS_ERROR_UNEXPECTED; 1.110 + } 1.111 + 1.112 + // From the input stream to a seekable stream 1.113 + nsCOMPtr<nsISeekableStream> seekableStream; 1.114 + seekableStream = do_QueryInterface(mData.inputStream); 1.115 + if (!seekableStream) { 1.116 + return NS_ERROR_UNEXPECTED; 1.117 + } 1.118 + 1.119 + // Seek + read the ZipLocal struct 1.120 + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); 1.121 + uint8_t buffer[ZIPLOCAL_SIZE]; 1.122 + uint32_t ret; 1.123 + 1.124 + rv = mData.inputStream->Read((char*)buffer, ZIPLOCAL_SIZE, &ret); 1.125 + if (NS_FAILED(rv) || ret != ZIPLOCAL_SIZE) { 1.126 + return NS_ERROR_UNEXPECTED; 1.127 + } 1.128 + 1.129 + // Signature check: 1.130 + if (ArchiveZipItem::StrToInt32(buffer) != LOCALSIG) { 1.131 + return NS_ERROR_UNEXPECTED; 1.132 + } 1.133 + 1.134 + ZipLocal local; 1.135 + memcpy(&local, buffer, ZIPLOCAL_SIZE); 1.136 + 1.137 + // Seek to the real data: 1.138 + offset += ZIPLOCAL_SIZE + 1.139 + ArchiveZipItem::StrToInt16(local.filename_len) + 1.140 + ArchiveZipItem::StrToInt16(local.extrafield_len); 1.141 + 1.142 + // The file is corrupt if there is not enough data 1.143 + if (offset + mData.sizeToBeRead > mData.parentSize) { 1.144 + return NS_ERROR_UNEXPECTED; 1.145 + } 1.146 + 1.147 + // Data starts here: 1.148 + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); 1.149 + 1.150 + // The file is compressed or not? 1.151 + mData.compressed = (ArchiveZipItem::StrToInt16(mCentral.method) != 0); 1.152 + 1.153 + // We have to skip the first mStart bytes: 1.154 + if (mStart != 0) { 1.155 + rv = Seek(NS_SEEK_SET, mStart); 1.156 + NS_ENSURE_SUCCESS(rv, rv); 1.157 + } 1.158 + 1.159 + return NS_OK; 1.160 +} 1.161 + 1.162 +NS_IMETHODIMP 1.163 +ArchiveInputStream::Close() 1.164 +{ 1.165 + if (mStatus != NotStarted) { 1.166 + inflateEnd(&mZs); 1.167 + mStatus = NotStarted; 1.168 + } 1.169 + 1.170 + return NS_OK; 1.171 +} 1.172 + 1.173 +NS_IMETHODIMP 1.174 +ArchiveInputStream::Available(uint64_t* _retval) 1.175 +{ 1.176 + *_retval = mLength - mData.cursor - mStart; 1.177 + return NS_OK; 1.178 +} 1.179 + 1.180 +NS_IMETHODIMP 1.181 +ArchiveInputStream::Read(char* aBuffer, 1.182 + uint32_t aCount, 1.183 + uint32_t* _retval) 1.184 +{ 1.185 + NS_ENSURE_ARG_POINTER(aBuffer); 1.186 + NS_ENSURE_ARG_POINTER(_retval); 1.187 + 1.188 + nsresult rv; 1.189 + 1.190 + // This is the first time: 1.191 + if (mStatus == NotStarted) { 1.192 + mStatus = Started; 1.193 + 1.194 + rv = Init(); 1.195 + if (NS_FAILED(rv)) { 1.196 + return rv; 1.197 + } 1.198 + 1.199 + // Let's set avail_out to -1 so we read something from the stream. 1.200 + mZs.avail_out = (uInt)-1; 1.201 + } 1.202 + 1.203 + // Nothing more can be read 1.204 + if (mStatus == Done) { 1.205 + *_retval = 0; 1.206 + return NS_OK; 1.207 + } 1.208 + 1.209 + // Stored file: 1.210 + if (!mData.compressed) { 1.211 + rv = mData.inputStream->Read(aBuffer, 1.212 + (mData.sizeToBeRead > aCount ? 1.213 + aCount : mData.sizeToBeRead), 1.214 + _retval); 1.215 + if (NS_SUCCEEDED(rv)) { 1.216 + mData.sizeToBeRead -= *_retval; 1.217 + mData.cursor += *_retval; 1.218 + 1.219 + if (mData.sizeToBeRead == 0) { 1.220 + mStatus = Done; 1.221 + } 1.222 + } 1.223 + 1.224 + return rv; 1.225 + } 1.226 + 1.227 + // We have nothing ready to be processed: 1.228 + if (mZs.avail_out != 0 && mData.sizeToBeRead != 0) { 1.229 + uint32_t ret; 1.230 + rv = mData.inputStream->Read((char*)mData.input, 1.231 + (mData.sizeToBeRead > sizeof(mData.input) ? 1.232 + sizeof(mData.input) : mData.sizeToBeRead), 1.233 + &ret); 1.234 + if (NS_FAILED(rv)) { 1.235 + return rv; 1.236 + } 1.237 + 1.238 + // Terminator: 1.239 + if (ret == 0) { 1.240 + *_retval = 0; 1.241 + return NS_OK; 1.242 + } 1.243 + 1.244 + mData.sizeToBeRead -= ret; 1.245 + mZs.avail_in = ret; 1.246 + mZs.next_in = mData.input; 1.247 + } 1.248 + 1.249 + mZs.avail_out = aCount; 1.250 + mZs.next_out = (unsigned char*)aBuffer; 1.251 + 1.252 + int ret = inflate(&mZs, mData.sizeToBeRead ? Z_NO_FLUSH : Z_FINISH); 1.253 + if (ret != Z_BUF_ERROR && ret != Z_OK && ret != Z_STREAM_END) { 1.254 + return NS_ERROR_UNEXPECTED; 1.255 + } 1.256 + 1.257 + if (ret == Z_STREAM_END) { 1.258 + mStatus = Done; 1.259 + } 1.260 + 1.261 + *_retval = aCount - mZs.avail_out; 1.262 + mData.cursor += *_retval; 1.263 + return NS_OK; 1.264 +} 1.265 + 1.266 +NS_IMETHODIMP 1.267 +ArchiveInputStream::ReadSegments(nsWriteSegmentFun aWriter, 1.268 + void* aClosure, 1.269 + uint32_t aCount, 1.270 + uint32_t* _retval) 1.271 +{ 1.272 + // don't have a buffer to read from, so this better not be called! 1.273 + NS_NOTREACHED("Consumers should be using Read()!"); 1.274 + return NS_ERROR_NOT_IMPLEMENTED; 1.275 +} 1.276 + 1.277 +NS_IMETHODIMP 1.278 +ArchiveInputStream::IsNonBlocking(bool* _retval) 1.279 +{ 1.280 + // We are blocking 1.281 + *_retval = false; 1.282 + return NS_OK; 1.283 +} 1.284 + 1.285 +NS_IMETHODIMP 1.286 +ArchiveInputStream::Seek(int32_t aWhence, int64_t aOffset) 1.287 +{ 1.288 + int64_t pos = aOffset; 1.289 + 1.290 + switch (aWhence) { 1.291 + case NS_SEEK_SET: 1.292 + break; 1.293 + 1.294 + case NS_SEEK_CUR: 1.295 + pos += mData.cursor; 1.296 + break; 1.297 + 1.298 + case NS_SEEK_END: 1.299 + pos += mLength; 1.300 + break; 1.301 + 1.302 + default: 1.303 + NS_NOTREACHED("unexpected whence value"); 1.304 + return NS_ERROR_UNEXPECTED; 1.305 + } 1.306 + 1.307 + if (pos == int64_t(mData.cursor)) { 1.308 + return NS_OK; 1.309 + } 1.310 + 1.311 + if (pos < 0 || pos >= mLength) { 1.312 + return NS_ERROR_FAILURE; 1.313 + } 1.314 + 1.315 + // We have to terminate the previous operation: 1.316 + nsresult rv; 1.317 + if (mStatus != NotStarted) { 1.318 + rv = Close(); 1.319 + NS_ENSURE_SUCCESS(rv, rv); 1.320 + } 1.321 + 1.322 + // Reset the cursor: 1.323 + mData.cursor = 0; 1.324 + 1.325 + // Note: This code is heavy but inflate does not have any seek() support: 1.326 + uint32_t ret; 1.327 + char buffer[1024]; 1.328 + while (pos > 0) { 1.329 + rv = Read(buffer, pos > int64_t(sizeof(buffer)) ? sizeof(buffer) : pos, &ret); 1.330 + if (NS_FAILED(rv)) { 1.331 + return rv; 1.332 + } 1.333 + 1.334 + if (ret == 0) { 1.335 + return NS_ERROR_UNEXPECTED; 1.336 + } 1.337 + 1.338 + pos -= ret; 1.339 + } 1.340 + 1.341 + return NS_OK; 1.342 +} 1.343 + 1.344 +NS_IMETHODIMP 1.345 +ArchiveInputStream::Tell(int64_t *aResult) 1.346 +{ 1.347 + *aResult = mData.cursor; 1.348 + return NS_OK; 1.349 +} 1.350 + 1.351 +NS_IMETHODIMP 1.352 +ArchiveInputStream::SetEOF() 1.353 +{ 1.354 + return NS_ERROR_NOT_IMPLEMENTED; 1.355 +} 1.356 + 1.357 +// ArchiveZipFile 1.358 + 1.359 +NS_IMETHODIMP 1.360 +ArchiveZipFile::GetInternalStream(nsIInputStream** aStream) 1.361 +{ 1.362 + if (mLength > INT32_MAX) { 1.363 + return NS_ERROR_FAILURE; 1.364 + } 1.365 + 1.366 + uint64_t size; 1.367 + nsresult rv = mArchiveReader->GetSize(&size); 1.368 + NS_ENSURE_SUCCESS(rv, rv); 1.369 + 1.370 + nsCOMPtr<nsIInputStream> inputStream; 1.371 + rv = mArchiveReader->GetInputStream(getter_AddRefs(inputStream)); 1.372 + if (NS_FAILED(rv) || !inputStream) { 1.373 + return NS_ERROR_UNEXPECTED; 1.374 + } 1.375 + 1.376 + nsRefPtr<ArchiveInputStream> stream = new ArchiveInputStream(size, 1.377 + inputStream, 1.378 + mFilename, 1.379 + mStart, 1.380 + mLength, 1.381 + mCentral); 1.382 + NS_ADDREF(stream); 1.383 + 1.384 + *aStream = stream; 1.385 + return NS_OK; 1.386 +} 1.387 + 1.388 +already_AddRefed<nsIDOMBlob> 1.389 +ArchiveZipFile::CreateSlice(uint64_t aStart, 1.390 + uint64_t aLength, 1.391 + const nsAString& aContentType) 1.392 +{ 1.393 + nsCOMPtr<nsIDOMBlob> t = new ArchiveZipFile(mFilename, 1.394 + mContentType, 1.395 + aStart, 1.396 + mLength, 1.397 + mCentral, 1.398 + mArchiveReader); 1.399 + return t.forget(); 1.400 +} 1.401 + 1.402 +NS_IMPL_CYCLE_COLLECTION(ArchiveZipFile, 1.403 + mArchiveReader) 1.404 + 1.405 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ArchiveZipFile) 1.406 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile) 1.407 + NS_INTERFACE_MAP_ENTRY(nsIDOMBlob) 1.408 + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile) 1.409 + NS_INTERFACE_MAP_ENTRY(nsIXHRSendable) 1.410 + NS_INTERFACE_MAP_ENTRY(nsIMutable) 1.411 +NS_INTERFACE_MAP_END_INHERITING(nsDOMFileCC) 1.412 + 1.413 +NS_IMPL_ADDREF_INHERITED(ArchiveZipFile, nsDOMFileCC) 1.414 +NS_IMPL_RELEASE_INHERITED(ArchiveZipFile, nsDOMFileCC)