dom/file/ArchiveZipFile.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "ArchiveZipFile.h"
michael@0 8 #include "ArchiveZipEvent.h"
michael@0 9
michael@0 10 #include "nsIInputStream.h"
michael@0 11 #include "zlib.h"
michael@0 12 #include "mozilla/Attributes.h"
michael@0 13
michael@0 14 USING_FILE_NAMESPACE
michael@0 15
michael@0 16 #define ZIP_CHUNK 16384
michael@0 17
michael@0 18 /**
michael@0 19 * Input stream object for zip files
michael@0 20 */
michael@0 21 class ArchiveInputStream MOZ_FINAL : public nsIInputStream,
michael@0 22 public nsISeekableStream
michael@0 23 {
michael@0 24 public:
michael@0 25 ArchiveInputStream(uint64_t aParentSize,
michael@0 26 nsIInputStream* aInputStream,
michael@0 27 nsString& aFilename,
michael@0 28 uint32_t aStart,
michael@0 29 uint32_t aLength,
michael@0 30 ZipCentral& aCentral)
michael@0 31 : mCentral(aCentral),
michael@0 32 mFilename(aFilename),
michael@0 33 mStart(aStart),
michael@0 34 mLength(aLength),
michael@0 35 mStatus(NotStarted)
michael@0 36 {
michael@0 37 MOZ_COUNT_CTOR(ArchiveInputStream);
michael@0 38
michael@0 39 // Reset the data:
michael@0 40 memset(&mData, 0, sizeof(mData));
michael@0 41
michael@0 42 mData.parentSize = aParentSize;
michael@0 43 mData.inputStream = aInputStream;
michael@0 44 }
michael@0 45
michael@0 46 virtual ~ArchiveInputStream()
michael@0 47 {
michael@0 48 MOZ_COUNT_DTOR(ArchiveInputStream);
michael@0 49 Close();
michael@0 50 }
michael@0 51
michael@0 52 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 53 NS_DECL_NSIINPUTSTREAM
michael@0 54 NS_DECL_NSISEEKABLESTREAM
michael@0 55
michael@0 56 private:
michael@0 57 nsresult Init();
michael@0 58
michael@0 59 private: // data
michael@0 60 ZipCentral mCentral;
michael@0 61 nsString mFilename;
michael@0 62 uint32_t mStart;
michael@0 63 uint32_t mLength;
michael@0 64
michael@0 65 z_stream mZs;
michael@0 66
michael@0 67 enum {
michael@0 68 NotStarted,
michael@0 69 Started,
michael@0 70 Done
michael@0 71 } mStatus;
michael@0 72
michael@0 73 struct {
michael@0 74 uint64_t parentSize;
michael@0 75 nsCOMPtr<nsIInputStream> inputStream;
michael@0 76
michael@0 77 unsigned char input[ZIP_CHUNK];
michael@0 78 uint32_t sizeToBeRead;
michael@0 79 uint32_t cursor;
michael@0 80
michael@0 81 bool compressed; // a zip file can contain stored or compressed files
michael@0 82 } mData;
michael@0 83 };
michael@0 84
michael@0 85 NS_IMPL_ISUPPORTS(ArchiveInputStream,
michael@0 86 nsIInputStream,
michael@0 87 nsISeekableStream)
michael@0 88
michael@0 89 nsresult
michael@0 90 ArchiveInputStream::Init()
michael@0 91 {
michael@0 92 nsresult rv;
michael@0 93
michael@0 94 memset(&mZs, 0, sizeof(z_stream));
michael@0 95 int zerr = inflateInit2(&mZs, -MAX_WBITS);
michael@0 96 if (zerr != Z_OK) {
michael@0 97 return NS_ERROR_OUT_OF_MEMORY;
michael@0 98 }
michael@0 99
michael@0 100 mData.sizeToBeRead = ArchiveZipItem::StrToInt32(mCentral.size);
michael@0 101
michael@0 102 uint32_t offset = ArchiveZipItem::StrToInt32(mCentral.localhdr_offset);
michael@0 103
michael@0 104 // The file is corrupt
michael@0 105 if (offset + ZIPLOCAL_SIZE > mData.parentSize) {
michael@0 106 return NS_ERROR_UNEXPECTED;
michael@0 107 }
michael@0 108
michael@0 109 // From the input stream to a seekable stream
michael@0 110 nsCOMPtr<nsISeekableStream> seekableStream;
michael@0 111 seekableStream = do_QueryInterface(mData.inputStream);
michael@0 112 if (!seekableStream) {
michael@0 113 return NS_ERROR_UNEXPECTED;
michael@0 114 }
michael@0 115
michael@0 116 // Seek + read the ZipLocal struct
michael@0 117 seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset);
michael@0 118 uint8_t buffer[ZIPLOCAL_SIZE];
michael@0 119 uint32_t ret;
michael@0 120
michael@0 121 rv = mData.inputStream->Read((char*)buffer, ZIPLOCAL_SIZE, &ret);
michael@0 122 if (NS_FAILED(rv) || ret != ZIPLOCAL_SIZE) {
michael@0 123 return NS_ERROR_UNEXPECTED;
michael@0 124 }
michael@0 125
michael@0 126 // Signature check:
michael@0 127 if (ArchiveZipItem::StrToInt32(buffer) != LOCALSIG) {
michael@0 128 return NS_ERROR_UNEXPECTED;
michael@0 129 }
michael@0 130
michael@0 131 ZipLocal local;
michael@0 132 memcpy(&local, buffer, ZIPLOCAL_SIZE);
michael@0 133
michael@0 134 // Seek to the real data:
michael@0 135 offset += ZIPLOCAL_SIZE +
michael@0 136 ArchiveZipItem::StrToInt16(local.filename_len) +
michael@0 137 ArchiveZipItem::StrToInt16(local.extrafield_len);
michael@0 138
michael@0 139 // The file is corrupt if there is not enough data
michael@0 140 if (offset + mData.sizeToBeRead > mData.parentSize) {
michael@0 141 return NS_ERROR_UNEXPECTED;
michael@0 142 }
michael@0 143
michael@0 144 // Data starts here:
michael@0 145 seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset);
michael@0 146
michael@0 147 // The file is compressed or not?
michael@0 148 mData.compressed = (ArchiveZipItem::StrToInt16(mCentral.method) != 0);
michael@0 149
michael@0 150 // We have to skip the first mStart bytes:
michael@0 151 if (mStart != 0) {
michael@0 152 rv = Seek(NS_SEEK_SET, mStart);
michael@0 153 NS_ENSURE_SUCCESS(rv, rv);
michael@0 154 }
michael@0 155
michael@0 156 return NS_OK;
michael@0 157 }
michael@0 158
michael@0 159 NS_IMETHODIMP
michael@0 160 ArchiveInputStream::Close()
michael@0 161 {
michael@0 162 if (mStatus != NotStarted) {
michael@0 163 inflateEnd(&mZs);
michael@0 164 mStatus = NotStarted;
michael@0 165 }
michael@0 166
michael@0 167 return NS_OK;
michael@0 168 }
michael@0 169
michael@0 170 NS_IMETHODIMP
michael@0 171 ArchiveInputStream::Available(uint64_t* _retval)
michael@0 172 {
michael@0 173 *_retval = mLength - mData.cursor - mStart;
michael@0 174 return NS_OK;
michael@0 175 }
michael@0 176
michael@0 177 NS_IMETHODIMP
michael@0 178 ArchiveInputStream::Read(char* aBuffer,
michael@0 179 uint32_t aCount,
michael@0 180 uint32_t* _retval)
michael@0 181 {
michael@0 182 NS_ENSURE_ARG_POINTER(aBuffer);
michael@0 183 NS_ENSURE_ARG_POINTER(_retval);
michael@0 184
michael@0 185 nsresult rv;
michael@0 186
michael@0 187 // This is the first time:
michael@0 188 if (mStatus == NotStarted) {
michael@0 189 mStatus = Started;
michael@0 190
michael@0 191 rv = Init();
michael@0 192 if (NS_FAILED(rv)) {
michael@0 193 return rv;
michael@0 194 }
michael@0 195
michael@0 196 // Let's set avail_out to -1 so we read something from the stream.
michael@0 197 mZs.avail_out = (uInt)-1;
michael@0 198 }
michael@0 199
michael@0 200 // Nothing more can be read
michael@0 201 if (mStatus == Done) {
michael@0 202 *_retval = 0;
michael@0 203 return NS_OK;
michael@0 204 }
michael@0 205
michael@0 206 // Stored file:
michael@0 207 if (!mData.compressed) {
michael@0 208 rv = mData.inputStream->Read(aBuffer,
michael@0 209 (mData.sizeToBeRead > aCount ?
michael@0 210 aCount : mData.sizeToBeRead),
michael@0 211 _retval);
michael@0 212 if (NS_SUCCEEDED(rv)) {
michael@0 213 mData.sizeToBeRead -= *_retval;
michael@0 214 mData.cursor += *_retval;
michael@0 215
michael@0 216 if (mData.sizeToBeRead == 0) {
michael@0 217 mStatus = Done;
michael@0 218 }
michael@0 219 }
michael@0 220
michael@0 221 return rv;
michael@0 222 }
michael@0 223
michael@0 224 // We have nothing ready to be processed:
michael@0 225 if (mZs.avail_out != 0 && mData.sizeToBeRead != 0) {
michael@0 226 uint32_t ret;
michael@0 227 rv = mData.inputStream->Read((char*)mData.input,
michael@0 228 (mData.sizeToBeRead > sizeof(mData.input) ?
michael@0 229 sizeof(mData.input) : mData.sizeToBeRead),
michael@0 230 &ret);
michael@0 231 if (NS_FAILED(rv)) {
michael@0 232 return rv;
michael@0 233 }
michael@0 234
michael@0 235 // Terminator:
michael@0 236 if (ret == 0) {
michael@0 237 *_retval = 0;
michael@0 238 return NS_OK;
michael@0 239 }
michael@0 240
michael@0 241 mData.sizeToBeRead -= ret;
michael@0 242 mZs.avail_in = ret;
michael@0 243 mZs.next_in = mData.input;
michael@0 244 }
michael@0 245
michael@0 246 mZs.avail_out = aCount;
michael@0 247 mZs.next_out = (unsigned char*)aBuffer;
michael@0 248
michael@0 249 int ret = inflate(&mZs, mData.sizeToBeRead ? Z_NO_FLUSH : Z_FINISH);
michael@0 250 if (ret != Z_BUF_ERROR && ret != Z_OK && ret != Z_STREAM_END) {
michael@0 251 return NS_ERROR_UNEXPECTED;
michael@0 252 }
michael@0 253
michael@0 254 if (ret == Z_STREAM_END) {
michael@0 255 mStatus = Done;
michael@0 256 }
michael@0 257
michael@0 258 *_retval = aCount - mZs.avail_out;
michael@0 259 mData.cursor += *_retval;
michael@0 260 return NS_OK;
michael@0 261 }
michael@0 262
michael@0 263 NS_IMETHODIMP
michael@0 264 ArchiveInputStream::ReadSegments(nsWriteSegmentFun aWriter,
michael@0 265 void* aClosure,
michael@0 266 uint32_t aCount,
michael@0 267 uint32_t* _retval)
michael@0 268 {
michael@0 269 // don't have a buffer to read from, so this better not be called!
michael@0 270 NS_NOTREACHED("Consumers should be using Read()!");
michael@0 271 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 272 }
michael@0 273
michael@0 274 NS_IMETHODIMP
michael@0 275 ArchiveInputStream::IsNonBlocking(bool* _retval)
michael@0 276 {
michael@0 277 // We are blocking
michael@0 278 *_retval = false;
michael@0 279 return NS_OK;
michael@0 280 }
michael@0 281
michael@0 282 NS_IMETHODIMP
michael@0 283 ArchiveInputStream::Seek(int32_t aWhence, int64_t aOffset)
michael@0 284 {
michael@0 285 int64_t pos = aOffset;
michael@0 286
michael@0 287 switch (aWhence) {
michael@0 288 case NS_SEEK_SET:
michael@0 289 break;
michael@0 290
michael@0 291 case NS_SEEK_CUR:
michael@0 292 pos += mData.cursor;
michael@0 293 break;
michael@0 294
michael@0 295 case NS_SEEK_END:
michael@0 296 pos += mLength;
michael@0 297 break;
michael@0 298
michael@0 299 default:
michael@0 300 NS_NOTREACHED("unexpected whence value");
michael@0 301 return NS_ERROR_UNEXPECTED;
michael@0 302 }
michael@0 303
michael@0 304 if (pos == int64_t(mData.cursor)) {
michael@0 305 return NS_OK;
michael@0 306 }
michael@0 307
michael@0 308 if (pos < 0 || pos >= mLength) {
michael@0 309 return NS_ERROR_FAILURE;
michael@0 310 }
michael@0 311
michael@0 312 // We have to terminate the previous operation:
michael@0 313 nsresult rv;
michael@0 314 if (mStatus != NotStarted) {
michael@0 315 rv = Close();
michael@0 316 NS_ENSURE_SUCCESS(rv, rv);
michael@0 317 }
michael@0 318
michael@0 319 // Reset the cursor:
michael@0 320 mData.cursor = 0;
michael@0 321
michael@0 322 // Note: This code is heavy but inflate does not have any seek() support:
michael@0 323 uint32_t ret;
michael@0 324 char buffer[1024];
michael@0 325 while (pos > 0) {
michael@0 326 rv = Read(buffer, pos > int64_t(sizeof(buffer)) ? sizeof(buffer) : pos, &ret);
michael@0 327 if (NS_FAILED(rv)) {
michael@0 328 return rv;
michael@0 329 }
michael@0 330
michael@0 331 if (ret == 0) {
michael@0 332 return NS_ERROR_UNEXPECTED;
michael@0 333 }
michael@0 334
michael@0 335 pos -= ret;
michael@0 336 }
michael@0 337
michael@0 338 return NS_OK;
michael@0 339 }
michael@0 340
michael@0 341 NS_IMETHODIMP
michael@0 342 ArchiveInputStream::Tell(int64_t *aResult)
michael@0 343 {
michael@0 344 *aResult = mData.cursor;
michael@0 345 return NS_OK;
michael@0 346 }
michael@0 347
michael@0 348 NS_IMETHODIMP
michael@0 349 ArchiveInputStream::SetEOF()
michael@0 350 {
michael@0 351 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 352 }
michael@0 353
michael@0 354 // ArchiveZipFile
michael@0 355
michael@0 356 NS_IMETHODIMP
michael@0 357 ArchiveZipFile::GetInternalStream(nsIInputStream** aStream)
michael@0 358 {
michael@0 359 if (mLength > INT32_MAX) {
michael@0 360 return NS_ERROR_FAILURE;
michael@0 361 }
michael@0 362
michael@0 363 uint64_t size;
michael@0 364 nsresult rv = mArchiveReader->GetSize(&size);
michael@0 365 NS_ENSURE_SUCCESS(rv, rv);
michael@0 366
michael@0 367 nsCOMPtr<nsIInputStream> inputStream;
michael@0 368 rv = mArchiveReader->GetInputStream(getter_AddRefs(inputStream));
michael@0 369 if (NS_FAILED(rv) || !inputStream) {
michael@0 370 return NS_ERROR_UNEXPECTED;
michael@0 371 }
michael@0 372
michael@0 373 nsRefPtr<ArchiveInputStream> stream = new ArchiveInputStream(size,
michael@0 374 inputStream,
michael@0 375 mFilename,
michael@0 376 mStart,
michael@0 377 mLength,
michael@0 378 mCentral);
michael@0 379 NS_ADDREF(stream);
michael@0 380
michael@0 381 *aStream = stream;
michael@0 382 return NS_OK;
michael@0 383 }
michael@0 384
michael@0 385 already_AddRefed<nsIDOMBlob>
michael@0 386 ArchiveZipFile::CreateSlice(uint64_t aStart,
michael@0 387 uint64_t aLength,
michael@0 388 const nsAString& aContentType)
michael@0 389 {
michael@0 390 nsCOMPtr<nsIDOMBlob> t = new ArchiveZipFile(mFilename,
michael@0 391 mContentType,
michael@0 392 aStart,
michael@0 393 mLength,
michael@0 394 mCentral,
michael@0 395 mArchiveReader);
michael@0 396 return t.forget();
michael@0 397 }
michael@0 398
michael@0 399 NS_IMPL_CYCLE_COLLECTION(ArchiveZipFile,
michael@0 400 mArchiveReader)
michael@0 401
michael@0 402 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ArchiveZipFile)
michael@0 403 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
michael@0 404 NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
michael@0 405 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
michael@0 406 NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
michael@0 407 NS_INTERFACE_MAP_ENTRY(nsIMutable)
michael@0 408 NS_INTERFACE_MAP_END_INHERITING(nsDOMFileCC)
michael@0 409
michael@0 410 NS_IMPL_ADDREF_INHERITED(ArchiveZipFile, nsDOMFileCC)
michael@0 411 NS_IMPL_RELEASE_INHERITED(ArchiveZipFile, nsDOMFileCC)

mercurial