modules/libjar/zipwriter/src/nsZipWriter.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 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4 */
michael@0 5
michael@0 6 #include "nsZipWriter.h"
michael@0 7
michael@0 8 #include <algorithm>
michael@0 9
michael@0 10 #include "StreamFunctions.h"
michael@0 11 #include "nsZipDataStream.h"
michael@0 12 #include "nsISeekableStream.h"
michael@0 13 #include "nsIAsyncStreamCopier.h"
michael@0 14 #include "nsIStreamListener.h"
michael@0 15 #include "nsIInputStreamPump.h"
michael@0 16 #include "nsComponentManagerUtils.h"
michael@0 17 #include "nsMemory.h"
michael@0 18 #include "nsError.h"
michael@0 19 #include "nsStreamUtils.h"
michael@0 20 #include "nsThreadUtils.h"
michael@0 21 #include "nsNetUtil.h"
michael@0 22 #include "prio.h"
michael@0 23
michael@0 24 #define ZIP_EOCDR_HEADER_SIZE 22
michael@0 25 #define ZIP_EOCDR_HEADER_SIGNATURE 0x06054b50
michael@0 26
michael@0 27 /**
michael@0 28 * nsZipWriter is used to create and add to zip files.
michael@0 29 * It is based on the spec available at
michael@0 30 * http://www.pkware.com/documents/casestudies/APPNOTE.TXT.
michael@0 31 *
michael@0 32 * The basic structure of a zip file created is slightly simpler than that
michael@0 33 * illustrated in the spec because certain features of the zip format are
michael@0 34 * unsupported:
michael@0 35 *
michael@0 36 * [local file header 1]
michael@0 37 * [file data 1]
michael@0 38 * .
michael@0 39 * .
michael@0 40 * .
michael@0 41 * [local file header n]
michael@0 42 * [file data n]
michael@0 43 * [central directory]
michael@0 44 * [end of central directory record]
michael@0 45 */
michael@0 46 NS_IMPL_ISUPPORTS(nsZipWriter, nsIZipWriter,
michael@0 47 nsIRequestObserver)
michael@0 48
michael@0 49 nsZipWriter::nsZipWriter()
michael@0 50 {
michael@0 51 mInQueue = false;
michael@0 52 }
michael@0 53
michael@0 54 nsZipWriter::~nsZipWriter()
michael@0 55 {
michael@0 56 if (mStream && !mInQueue)
michael@0 57 Close();
michael@0 58 }
michael@0 59
michael@0 60 /* attribute AString comment; */
michael@0 61 NS_IMETHODIMP nsZipWriter::GetComment(nsACString & aComment)
michael@0 62 {
michael@0 63 if (!mStream)
michael@0 64 return NS_ERROR_NOT_INITIALIZED;
michael@0 65
michael@0 66 aComment = mComment;
michael@0 67 return NS_OK;
michael@0 68 }
michael@0 69
michael@0 70 NS_IMETHODIMP nsZipWriter::SetComment(const nsACString & aComment)
michael@0 71 {
michael@0 72 if (!mStream)
michael@0 73 return NS_ERROR_NOT_INITIALIZED;
michael@0 74
michael@0 75 mComment = aComment;
michael@0 76 mCDSDirty = true;
michael@0 77 return NS_OK;
michael@0 78 }
michael@0 79
michael@0 80 /* readonly attribute boolean inQueue; */
michael@0 81 NS_IMETHODIMP nsZipWriter::GetInQueue(bool *aInQueue)
michael@0 82 {
michael@0 83 *aInQueue = mInQueue;
michael@0 84 return NS_OK;
michael@0 85 }
michael@0 86
michael@0 87 /* readonly attribute nsIFile file; */
michael@0 88 NS_IMETHODIMP nsZipWriter::GetFile(nsIFile **aFile)
michael@0 89 {
michael@0 90 if (!mFile)
michael@0 91 return NS_ERROR_NOT_INITIALIZED;
michael@0 92
michael@0 93 nsCOMPtr<nsIFile> file;
michael@0 94 nsresult rv = mFile->Clone(getter_AddRefs(file));
michael@0 95 NS_ENSURE_SUCCESS(rv, rv);
michael@0 96
michael@0 97 NS_ADDREF(*aFile = file);
michael@0 98 return NS_OK;
michael@0 99 }
michael@0 100
michael@0 101 /*
michael@0 102 * Reads file entries out of an existing zip file.
michael@0 103 */
michael@0 104 nsresult nsZipWriter::ReadFile(nsIFile *aFile)
michael@0 105 {
michael@0 106 int64_t size;
michael@0 107 nsresult rv = aFile->GetFileSize(&size);
michael@0 108 NS_ENSURE_SUCCESS(rv, rv);
michael@0 109
michael@0 110 // If the file is too short, it cannot be a valid archive, thus we fail
michael@0 111 // without even attempting to open it
michael@0 112 NS_ENSURE_TRUE(size > ZIP_EOCDR_HEADER_SIZE, NS_ERROR_FILE_CORRUPTED);
michael@0 113
michael@0 114 nsCOMPtr<nsIInputStream> inputStream;
michael@0 115 rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
michael@0 116 NS_ENSURE_SUCCESS(rv, rv);
michael@0 117
michael@0 118 uint8_t buf[1024];
michael@0 119 int64_t seek = size - 1024;
michael@0 120 uint32_t length = 1024;
michael@0 121
michael@0 122 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(inputStream);
michael@0 123
michael@0 124 while (true) {
michael@0 125 if (seek < 0) {
michael@0 126 length += (int32_t)seek;
michael@0 127 seek = 0;
michael@0 128 }
michael@0 129
michael@0 130 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, seek);
michael@0 131 if (NS_FAILED(rv)) {
michael@0 132 inputStream->Close();
michael@0 133 return rv;
michael@0 134 }
michael@0 135 rv = ZW_ReadData(inputStream, (char *)buf, length);
michael@0 136 if (NS_FAILED(rv)) {
michael@0 137 inputStream->Close();
michael@0 138 return rv;
michael@0 139 }
michael@0 140
michael@0 141 /*
michael@0 142 * We have to backtrack from the end of the file until we find the
michael@0 143 * CDS signature
michael@0 144 */
michael@0 145 // We know it's at least this far from the end
michael@0 146 for (uint32_t pos = length - ZIP_EOCDR_HEADER_SIZE;
michael@0 147 (int32_t)pos >= 0; pos--) {
michael@0 148 uint32_t sig = PEEK32(buf + pos);
michael@0 149 if (sig == ZIP_EOCDR_HEADER_SIGNATURE) {
michael@0 150 // Skip down to entry count
michael@0 151 pos += 10;
michael@0 152 uint32_t entries = READ16(buf, &pos);
michael@0 153 // Skip past CDS size
michael@0 154 pos += 4;
michael@0 155 mCDSOffset = READ32(buf, &pos);
michael@0 156 uint32_t commentlen = READ16(buf, &pos);
michael@0 157
michael@0 158 if (commentlen == 0)
michael@0 159 mComment.Truncate();
michael@0 160 else if (pos + commentlen <= length)
michael@0 161 mComment.Assign((const char *)buf + pos, commentlen);
michael@0 162 else {
michael@0 163 if ((seek + pos + commentlen) > size) {
michael@0 164 inputStream->Close();
michael@0 165 return NS_ERROR_FILE_CORRUPTED;
michael@0 166 }
michael@0 167 nsAutoArrayPtr<char> field(new char[commentlen]);
michael@0 168 NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
michael@0 169 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
michael@0 170 seek + pos);
michael@0 171 if (NS_FAILED(rv)) {
michael@0 172 inputStream->Close();
michael@0 173 return rv;
michael@0 174 }
michael@0 175 rv = ZW_ReadData(inputStream, field.get(), length);
michael@0 176 if (NS_FAILED(rv)) {
michael@0 177 inputStream->Close();
michael@0 178 return rv;
michael@0 179 }
michael@0 180 mComment.Assign(field.get(), commentlen);
michael@0 181 }
michael@0 182
michael@0 183 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
michael@0 184 mCDSOffset);
michael@0 185 if (NS_FAILED(rv)) {
michael@0 186 inputStream->Close();
michael@0 187 return rv;
michael@0 188 }
michael@0 189
michael@0 190 for (uint32_t entry = 0; entry < entries; entry++) {
michael@0 191 nsZipHeader* header = new nsZipHeader();
michael@0 192 if (!header) {
michael@0 193 inputStream->Close();
michael@0 194 mEntryHash.Clear();
michael@0 195 mHeaders.Clear();
michael@0 196 return NS_ERROR_OUT_OF_MEMORY;
michael@0 197 }
michael@0 198 rv = header->ReadCDSHeader(inputStream);
michael@0 199 if (NS_FAILED(rv)) {
michael@0 200 inputStream->Close();
michael@0 201 mEntryHash.Clear();
michael@0 202 mHeaders.Clear();
michael@0 203 return rv;
michael@0 204 }
michael@0 205 mEntryHash.Put(header->mName, mHeaders.Count());
michael@0 206 if (!mHeaders.AppendObject(header))
michael@0 207 return NS_ERROR_OUT_OF_MEMORY;
michael@0 208 }
michael@0 209
michael@0 210 return inputStream->Close();
michael@0 211 }
michael@0 212 }
michael@0 213
michael@0 214 if (seek == 0) {
michael@0 215 // We've reached the start with no signature found. Corrupt.
michael@0 216 inputStream->Close();
michael@0 217 return NS_ERROR_FILE_CORRUPTED;
michael@0 218 }
michael@0 219
michael@0 220 // Overlap by the size of the end of cdr
michael@0 221 seek -= (1024 - ZIP_EOCDR_HEADER_SIZE);
michael@0 222 }
michael@0 223 // Will never reach here in reality
michael@0 224 NS_NOTREACHED("Loop should never complete");
michael@0 225 return NS_ERROR_UNEXPECTED;
michael@0 226 }
michael@0 227
michael@0 228 /* void open (in nsIFile aFile, in int32_t aIoFlags); */
michael@0 229 NS_IMETHODIMP nsZipWriter::Open(nsIFile *aFile, int32_t aIoFlags)
michael@0 230 {
michael@0 231 if (mStream)
michael@0 232 return NS_ERROR_ALREADY_INITIALIZED;
michael@0 233
michael@0 234 NS_ENSURE_ARG_POINTER(aFile);
michael@0 235
michael@0 236 // Need to be able to write to the file
michael@0 237 if (aIoFlags & PR_RDONLY)
michael@0 238 return NS_ERROR_FAILURE;
michael@0 239
michael@0 240 nsresult rv = aFile->Clone(getter_AddRefs(mFile));
michael@0 241 NS_ENSURE_SUCCESS(rv, rv);
michael@0 242
michael@0 243 bool exists;
michael@0 244 rv = mFile->Exists(&exists);
michael@0 245 NS_ENSURE_SUCCESS(rv, rv);
michael@0 246 if (!exists && !(aIoFlags & PR_CREATE_FILE))
michael@0 247 return NS_ERROR_FILE_NOT_FOUND;
michael@0 248
michael@0 249 if (exists && !(aIoFlags & (PR_TRUNCATE | PR_WRONLY))) {
michael@0 250 rv = ReadFile(mFile);
michael@0 251 NS_ENSURE_SUCCESS(rv, rv);
michael@0 252 mCDSDirty = false;
michael@0 253 }
michael@0 254 else {
michael@0 255 mCDSOffset = 0;
michael@0 256 mCDSDirty = true;
michael@0 257 mComment.Truncate();
michael@0 258 }
michael@0 259
michael@0 260 // Silently drop PR_APPEND
michael@0 261 aIoFlags &= 0xef;
michael@0 262
michael@0 263 nsCOMPtr<nsIOutputStream> stream;
michael@0 264 rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), mFile, aIoFlags);
michael@0 265 if (NS_FAILED(rv)) {
michael@0 266 mHeaders.Clear();
michael@0 267 mEntryHash.Clear();
michael@0 268 return rv;
michael@0 269 }
michael@0 270
michael@0 271 rv = NS_NewBufferedOutputStream(getter_AddRefs(mStream), stream, 64 * 1024);
michael@0 272 if (NS_FAILED(rv)) {
michael@0 273 stream->Close();
michael@0 274 mHeaders.Clear();
michael@0 275 mEntryHash.Clear();
michael@0 276 return rv;
michael@0 277 }
michael@0 278
michael@0 279 if (mCDSOffset > 0) {
michael@0 280 rv = SeekCDS();
michael@0 281 NS_ENSURE_SUCCESS(rv, rv);
michael@0 282 }
michael@0 283
michael@0 284 return NS_OK;
michael@0 285 }
michael@0 286
michael@0 287 /* nsIZipEntry getEntry (in AString aZipEntry); */
michael@0 288 NS_IMETHODIMP nsZipWriter::GetEntry(const nsACString & aZipEntry,
michael@0 289 nsIZipEntry **_retval)
michael@0 290 {
michael@0 291 int32_t pos;
michael@0 292 if (mEntryHash.Get(aZipEntry, &pos))
michael@0 293 NS_ADDREF(*_retval = mHeaders[pos]);
michael@0 294 else
michael@0 295 *_retval = nullptr;
michael@0 296
michael@0 297 return NS_OK;
michael@0 298 }
michael@0 299
michael@0 300 /* boolean hasEntry (in AString aZipEntry); */
michael@0 301 NS_IMETHODIMP nsZipWriter::HasEntry(const nsACString & aZipEntry,
michael@0 302 bool *_retval)
michael@0 303 {
michael@0 304 *_retval = mEntryHash.Get(aZipEntry, nullptr);
michael@0 305
michael@0 306 return NS_OK;
michael@0 307 }
michael@0 308
michael@0 309 /* void addEntryDirectory (in AUTF8String aZipEntry, in PRTime aModTime,
michael@0 310 * in boolean aQueue); */
michael@0 311 NS_IMETHODIMP nsZipWriter::AddEntryDirectory(const nsACString & aZipEntry,
michael@0 312 PRTime aModTime, bool aQueue)
michael@0 313 {
michael@0 314 if (!mStream)
michael@0 315 return NS_ERROR_NOT_INITIALIZED;
michael@0 316
michael@0 317 if (aQueue) {
michael@0 318 nsZipQueueItem item;
michael@0 319 item.mOperation = OPERATION_ADD;
michael@0 320 item.mZipEntry = aZipEntry;
michael@0 321 item.mModTime = aModTime;
michael@0 322 item.mPermissions = PERMISSIONS_DIR;
michael@0 323 if (!mQueue.AppendElement(item))
michael@0 324 return NS_ERROR_OUT_OF_MEMORY;
michael@0 325 return NS_OK;
michael@0 326 }
michael@0 327
michael@0 328 if (mInQueue)
michael@0 329 return NS_ERROR_IN_PROGRESS;
michael@0 330 return InternalAddEntryDirectory(aZipEntry, aModTime, PERMISSIONS_DIR);
michael@0 331 }
michael@0 332
michael@0 333 /* void addEntryFile (in AUTF8String aZipEntry, in int32_t aCompression,
michael@0 334 * in nsIFile aFile, in boolean aQueue); */
michael@0 335 NS_IMETHODIMP nsZipWriter::AddEntryFile(const nsACString & aZipEntry,
michael@0 336 int32_t aCompression, nsIFile *aFile,
michael@0 337 bool aQueue)
michael@0 338 {
michael@0 339 NS_ENSURE_ARG_POINTER(aFile);
michael@0 340 if (!mStream)
michael@0 341 return NS_ERROR_NOT_INITIALIZED;
michael@0 342
michael@0 343 nsresult rv;
michael@0 344 if (aQueue) {
michael@0 345 nsZipQueueItem item;
michael@0 346 item.mOperation = OPERATION_ADD;
michael@0 347 item.mZipEntry = aZipEntry;
michael@0 348 item.mCompression = aCompression;
michael@0 349 rv = aFile->Clone(getter_AddRefs(item.mFile));
michael@0 350 NS_ENSURE_SUCCESS(rv, rv);
michael@0 351 if (!mQueue.AppendElement(item))
michael@0 352 return NS_ERROR_OUT_OF_MEMORY;
michael@0 353 return NS_OK;
michael@0 354 }
michael@0 355
michael@0 356 if (mInQueue)
michael@0 357 return NS_ERROR_IN_PROGRESS;
michael@0 358
michael@0 359 bool exists;
michael@0 360 rv = aFile->Exists(&exists);
michael@0 361 NS_ENSURE_SUCCESS(rv, rv);
michael@0 362 if (!exists)
michael@0 363 return NS_ERROR_FILE_NOT_FOUND;
michael@0 364
michael@0 365 bool isdir;
michael@0 366 rv = aFile->IsDirectory(&isdir);
michael@0 367 NS_ENSURE_SUCCESS(rv, rv);
michael@0 368
michael@0 369 PRTime modtime;
michael@0 370 rv = aFile->GetLastModifiedTime(&modtime);
michael@0 371 NS_ENSURE_SUCCESS(rv, rv);
michael@0 372 modtime *= PR_USEC_PER_MSEC;
michael@0 373
michael@0 374 uint32_t permissions;
michael@0 375 rv = aFile->GetPermissions(&permissions);
michael@0 376 NS_ENSURE_SUCCESS(rv, rv);
michael@0 377
michael@0 378 if (isdir)
michael@0 379 return InternalAddEntryDirectory(aZipEntry, modtime, permissions);
michael@0 380
michael@0 381 if (mEntryHash.Get(aZipEntry, nullptr))
michael@0 382 return NS_ERROR_FILE_ALREADY_EXISTS;
michael@0 383
michael@0 384 nsCOMPtr<nsIInputStream> inputStream;
michael@0 385 rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
michael@0 386 aFile);
michael@0 387 NS_ENSURE_SUCCESS(rv, rv);
michael@0 388
michael@0 389 rv = AddEntryStream(aZipEntry, modtime, aCompression, inputStream,
michael@0 390 false, permissions);
michael@0 391 NS_ENSURE_SUCCESS(rv, rv);
michael@0 392
michael@0 393 return inputStream->Close();
michael@0 394 }
michael@0 395
michael@0 396 /* void addEntryChannel (in AUTF8String aZipEntry, in PRTime aModTime,
michael@0 397 * in int32_t aCompression, in nsIChannel aChannel,
michael@0 398 * in boolean aQueue); */
michael@0 399 NS_IMETHODIMP nsZipWriter::AddEntryChannel(const nsACString & aZipEntry,
michael@0 400 PRTime aModTime,
michael@0 401 int32_t aCompression,
michael@0 402 nsIChannel *aChannel,
michael@0 403 bool aQueue)
michael@0 404 {
michael@0 405 NS_ENSURE_ARG_POINTER(aChannel);
michael@0 406 if (!mStream)
michael@0 407 return NS_ERROR_NOT_INITIALIZED;
michael@0 408
michael@0 409 if (aQueue) {
michael@0 410 nsZipQueueItem item;
michael@0 411 item.mOperation = OPERATION_ADD;
michael@0 412 item.mZipEntry = aZipEntry;
michael@0 413 item.mModTime = aModTime;
michael@0 414 item.mCompression = aCompression;
michael@0 415 item.mPermissions = PERMISSIONS_FILE;
michael@0 416 item.mChannel = aChannel;
michael@0 417 if (!mQueue.AppendElement(item))
michael@0 418 return NS_ERROR_OUT_OF_MEMORY;
michael@0 419 return NS_OK;
michael@0 420 }
michael@0 421
michael@0 422 if (mInQueue)
michael@0 423 return NS_ERROR_IN_PROGRESS;
michael@0 424 if (mEntryHash.Get(aZipEntry, nullptr))
michael@0 425 return NS_ERROR_FILE_ALREADY_EXISTS;
michael@0 426
michael@0 427 nsCOMPtr<nsIInputStream> inputStream;
michael@0 428 nsresult rv = aChannel->Open(getter_AddRefs(inputStream));
michael@0 429 NS_ENSURE_SUCCESS(rv, rv);
michael@0 430
michael@0 431 rv = AddEntryStream(aZipEntry, aModTime, aCompression, inputStream,
michael@0 432 false, PERMISSIONS_FILE);
michael@0 433 NS_ENSURE_SUCCESS(rv, rv);
michael@0 434
michael@0 435 return inputStream->Close();
michael@0 436 }
michael@0 437
michael@0 438 /* void addEntryStream (in AUTF8String aZipEntry, in PRTime aModTime,
michael@0 439 * in int32_t aCompression, in nsIInputStream aStream,
michael@0 440 * in boolean aQueue); */
michael@0 441 NS_IMETHODIMP nsZipWriter::AddEntryStream(const nsACString & aZipEntry,
michael@0 442 PRTime aModTime,
michael@0 443 int32_t aCompression,
michael@0 444 nsIInputStream *aStream,
michael@0 445 bool aQueue)
michael@0 446 {
michael@0 447 return AddEntryStream(aZipEntry, aModTime, aCompression, aStream, aQueue,
michael@0 448 PERMISSIONS_FILE);
michael@0 449 }
michael@0 450
michael@0 451 /* void addEntryStream (in AUTF8String aZipEntry, in PRTime aModTime,
michael@0 452 * in int32_t aCompression, in nsIInputStream aStream,
michael@0 453 * in boolean aQueue, in unsigned long aPermissions); */
michael@0 454 nsresult nsZipWriter::AddEntryStream(const nsACString & aZipEntry,
michael@0 455 PRTime aModTime,
michael@0 456 int32_t aCompression,
michael@0 457 nsIInputStream *aStream,
michael@0 458 bool aQueue,
michael@0 459 uint32_t aPermissions)
michael@0 460 {
michael@0 461 NS_ENSURE_ARG_POINTER(aStream);
michael@0 462 if (!mStream)
michael@0 463 return NS_ERROR_NOT_INITIALIZED;
michael@0 464
michael@0 465 if (aQueue) {
michael@0 466 nsZipQueueItem item;
michael@0 467 item.mOperation = OPERATION_ADD;
michael@0 468 item.mZipEntry = aZipEntry;
michael@0 469 item.mModTime = aModTime;
michael@0 470 item.mCompression = aCompression;
michael@0 471 item.mPermissions = aPermissions;
michael@0 472 item.mStream = aStream;
michael@0 473 if (!mQueue.AppendElement(item))
michael@0 474 return NS_ERROR_OUT_OF_MEMORY;
michael@0 475 return NS_OK;
michael@0 476 }
michael@0 477
michael@0 478 if (mInQueue)
michael@0 479 return NS_ERROR_IN_PROGRESS;
michael@0 480 if (mEntryHash.Get(aZipEntry, nullptr))
michael@0 481 return NS_ERROR_FILE_ALREADY_EXISTS;
michael@0 482
michael@0 483 nsRefPtr<nsZipHeader> header = new nsZipHeader();
michael@0 484 NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
michael@0 485 header->Init(aZipEntry, aModTime, ZIP_ATTRS(aPermissions, ZIP_ATTRS_FILE),
michael@0 486 mCDSOffset);
michael@0 487 nsresult rv = header->WriteFileHeader(mStream);
michael@0 488 if (NS_FAILED(rv)) {
michael@0 489 SeekCDS();
michael@0 490 return rv;
michael@0 491 }
michael@0 492
michael@0 493 nsRefPtr<nsZipDataStream> stream = new nsZipDataStream();
michael@0 494 if (!stream) {
michael@0 495 SeekCDS();
michael@0 496 return NS_ERROR_OUT_OF_MEMORY;
michael@0 497 }
michael@0 498 rv = stream->Init(this, mStream, header, aCompression);
michael@0 499 if (NS_FAILED(rv)) {
michael@0 500 SeekCDS();
michael@0 501 return rv;
michael@0 502 }
michael@0 503
michael@0 504 rv = stream->ReadStream(aStream);
michael@0 505 if (NS_FAILED(rv))
michael@0 506 SeekCDS();
michael@0 507 return rv;
michael@0 508 }
michael@0 509
michael@0 510 /* void removeEntry (in AUTF8String aZipEntry, in boolean aQueue); */
michael@0 511 NS_IMETHODIMP nsZipWriter::RemoveEntry(const nsACString & aZipEntry,
michael@0 512 bool aQueue)
michael@0 513 {
michael@0 514 if (!mStream)
michael@0 515 return NS_ERROR_NOT_INITIALIZED;
michael@0 516
michael@0 517 if (aQueue) {
michael@0 518 nsZipQueueItem item;
michael@0 519 item.mOperation = OPERATION_REMOVE;
michael@0 520 item.mZipEntry = aZipEntry;
michael@0 521 if (!mQueue.AppendElement(item))
michael@0 522 return NS_ERROR_OUT_OF_MEMORY;
michael@0 523 return NS_OK;
michael@0 524 }
michael@0 525
michael@0 526 if (mInQueue)
michael@0 527 return NS_ERROR_IN_PROGRESS;
michael@0 528
michael@0 529 int32_t pos;
michael@0 530 if (mEntryHash.Get(aZipEntry, &pos)) {
michael@0 531 // Flush any remaining data before we seek.
michael@0 532 nsresult rv = mStream->Flush();
michael@0 533 NS_ENSURE_SUCCESS(rv, rv);
michael@0 534 if (pos < mHeaders.Count() - 1) {
michael@0 535 // This is not the last entry, pull back the data.
michael@0 536 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
michael@0 537 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
michael@0 538 mHeaders[pos]->mOffset);
michael@0 539 NS_ENSURE_SUCCESS(rv, rv);
michael@0 540
michael@0 541 nsCOMPtr<nsIInputStream> inputStream;
michael@0 542 rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
michael@0 543 mFile);
michael@0 544 NS_ENSURE_SUCCESS(rv, rv);
michael@0 545 seekable = do_QueryInterface(inputStream);
michael@0 546 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
michael@0 547 mHeaders[pos + 1]->mOffset);
michael@0 548 if (NS_FAILED(rv)) {
michael@0 549 inputStream->Close();
michael@0 550 return rv;
michael@0 551 }
michael@0 552
michael@0 553 uint32_t count = mCDSOffset - mHeaders[pos + 1]->mOffset;
michael@0 554 uint32_t read = 0;
michael@0 555 char buf[4096];
michael@0 556 while (count > 0) {
michael@0 557 read = std::min(count, (uint32_t) sizeof(buf));
michael@0 558
michael@0 559 rv = inputStream->Read(buf, read, &read);
michael@0 560 if (NS_FAILED(rv)) {
michael@0 561 inputStream->Close();
michael@0 562 Cleanup();
michael@0 563 return rv;
michael@0 564 }
michael@0 565
michael@0 566 rv = ZW_WriteData(mStream, buf, read);
michael@0 567 if (NS_FAILED(rv)) {
michael@0 568 inputStream->Close();
michael@0 569 Cleanup();
michael@0 570 return rv;
michael@0 571 }
michael@0 572
michael@0 573 count -= read;
michael@0 574 }
michael@0 575 inputStream->Close();
michael@0 576
michael@0 577 // Rewrite header offsets and update hash
michael@0 578 uint32_t shift = (mHeaders[pos + 1]->mOffset -
michael@0 579 mHeaders[pos]->mOffset);
michael@0 580 mCDSOffset -= shift;
michael@0 581 int32_t pos2 = pos + 1;
michael@0 582 while (pos2 < mHeaders.Count()) {
michael@0 583 mEntryHash.Put(mHeaders[pos2]->mName, pos2-1);
michael@0 584 mHeaders[pos2]->mOffset -= shift;
michael@0 585 pos2++;
michael@0 586 }
michael@0 587 }
michael@0 588 else {
michael@0 589 // Remove the last entry is just a case of moving the CDS
michael@0 590 mCDSOffset = mHeaders[pos]->mOffset;
michael@0 591 rv = SeekCDS();
michael@0 592 NS_ENSURE_SUCCESS(rv, rv);
michael@0 593 }
michael@0 594
michael@0 595 mEntryHash.Remove(mHeaders[pos]->mName);
michael@0 596 mHeaders.RemoveObjectAt(pos);
michael@0 597 mCDSDirty = true;
michael@0 598
michael@0 599 return NS_OK;
michael@0 600 }
michael@0 601
michael@0 602 return NS_ERROR_FILE_NOT_FOUND;
michael@0 603 }
michael@0 604
michael@0 605 /* void processQueue (in nsIRequestObserver aObserver,
michael@0 606 * in nsISupports aContext); */
michael@0 607 NS_IMETHODIMP nsZipWriter::ProcessQueue(nsIRequestObserver *aObserver,
michael@0 608 nsISupports *aContext)
michael@0 609 {
michael@0 610 if (!mStream)
michael@0 611 return NS_ERROR_NOT_INITIALIZED;
michael@0 612 if (mInQueue)
michael@0 613 return NS_ERROR_IN_PROGRESS;
michael@0 614
michael@0 615 mProcessObserver = aObserver;
michael@0 616 mProcessContext = aContext;
michael@0 617 mInQueue = true;
michael@0 618
michael@0 619 if (mProcessObserver)
michael@0 620 mProcessObserver->OnStartRequest(nullptr, mProcessContext);
michael@0 621
michael@0 622 BeginProcessingNextItem();
michael@0 623
michael@0 624 return NS_OK;
michael@0 625 }
michael@0 626
michael@0 627 /* void close (); */
michael@0 628 NS_IMETHODIMP nsZipWriter::Close()
michael@0 629 {
michael@0 630 if (!mStream)
michael@0 631 return NS_ERROR_NOT_INITIALIZED;
michael@0 632 if (mInQueue)
michael@0 633 return NS_ERROR_IN_PROGRESS;
michael@0 634
michael@0 635 if (mCDSDirty) {
michael@0 636 uint32_t size = 0;
michael@0 637 for (int32_t i = 0; i < mHeaders.Count(); i++) {
michael@0 638 nsresult rv = mHeaders[i]->WriteCDSHeader(mStream);
michael@0 639 if (NS_FAILED(rv)) {
michael@0 640 Cleanup();
michael@0 641 return rv;
michael@0 642 }
michael@0 643 size += mHeaders[i]->GetCDSHeaderLength();
michael@0 644 }
michael@0 645
michael@0 646 uint8_t buf[ZIP_EOCDR_HEADER_SIZE];
michael@0 647 uint32_t pos = 0;
michael@0 648 WRITE32(buf, &pos, ZIP_EOCDR_HEADER_SIGNATURE);
michael@0 649 WRITE16(buf, &pos, 0);
michael@0 650 WRITE16(buf, &pos, 0);
michael@0 651 WRITE16(buf, &pos, mHeaders.Count());
michael@0 652 WRITE16(buf, &pos, mHeaders.Count());
michael@0 653 WRITE32(buf, &pos, size);
michael@0 654 WRITE32(buf, &pos, mCDSOffset);
michael@0 655 WRITE16(buf, &pos, mComment.Length());
michael@0 656
michael@0 657 nsresult rv = ZW_WriteData(mStream, (const char *)buf, pos);
michael@0 658 if (NS_FAILED(rv)) {
michael@0 659 Cleanup();
michael@0 660 return rv;
michael@0 661 }
michael@0 662
michael@0 663 rv = ZW_WriteData(mStream, mComment.get(), mComment.Length());
michael@0 664 if (NS_FAILED(rv)) {
michael@0 665 Cleanup();
michael@0 666 return rv;
michael@0 667 }
michael@0 668
michael@0 669 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
michael@0 670 rv = seekable->SetEOF();
michael@0 671 if (NS_FAILED(rv)) {
michael@0 672 Cleanup();
michael@0 673 return rv;
michael@0 674 }
michael@0 675
michael@0 676 // Go back and rewrite the file headers
michael@0 677 for (int32_t i = 0; i < mHeaders.Count(); i++) {
michael@0 678 nsZipHeader *header = mHeaders[i];
michael@0 679 if (!header->mWriteOnClose)
michael@0 680 continue;
michael@0 681
michael@0 682 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, header->mOffset);
michael@0 683 if (NS_FAILED(rv)) {
michael@0 684 Cleanup();
michael@0 685 return rv;
michael@0 686 }
michael@0 687 rv = header->WriteFileHeader(mStream);
michael@0 688 if (NS_FAILED(rv)) {
michael@0 689 Cleanup();
michael@0 690 return rv;
michael@0 691 }
michael@0 692 }
michael@0 693 }
michael@0 694
michael@0 695 nsresult rv = mStream->Close();
michael@0 696 mStream = nullptr;
michael@0 697 mHeaders.Clear();
michael@0 698 mEntryHash.Clear();
michael@0 699 mQueue.Clear();
michael@0 700
michael@0 701 return rv;
michael@0 702 }
michael@0 703
michael@0 704 // Our nsIRequestObserver monitors removal operations performed on the queue
michael@0 705 /* void onStartRequest (in nsIRequest aRequest, in nsISupports aContext); */
michael@0 706 NS_IMETHODIMP nsZipWriter::OnStartRequest(nsIRequest *aRequest,
michael@0 707 nsISupports *aContext)
michael@0 708 {
michael@0 709 return NS_OK;
michael@0 710 }
michael@0 711
michael@0 712 /* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext,
michael@0 713 * in nsresult aStatusCode); */
michael@0 714 NS_IMETHODIMP nsZipWriter::OnStopRequest(nsIRequest *aRequest,
michael@0 715 nsISupports *aContext,
michael@0 716 nsresult aStatusCode)
michael@0 717 {
michael@0 718 if (NS_FAILED(aStatusCode)) {
michael@0 719 FinishQueue(aStatusCode);
michael@0 720 Cleanup();
michael@0 721 }
michael@0 722
michael@0 723 nsresult rv = mStream->Flush();
michael@0 724 if (NS_FAILED(rv)) {
michael@0 725 FinishQueue(rv);
michael@0 726 Cleanup();
michael@0 727 return rv;
michael@0 728 }
michael@0 729 rv = SeekCDS();
michael@0 730 if (NS_FAILED(rv)) {
michael@0 731 FinishQueue(rv);
michael@0 732 return rv;
michael@0 733 }
michael@0 734
michael@0 735 BeginProcessingNextItem();
michael@0 736
michael@0 737 return NS_OK;
michael@0 738 }
michael@0 739
michael@0 740 /*
michael@0 741 * Make all stored(uncompressed) files align to given alignment size.
michael@0 742 */
michael@0 743 NS_IMETHODIMP nsZipWriter::AlignStoredFiles(uint16_t aAlignSize)
michael@0 744 {
michael@0 745 nsresult rv;
michael@0 746
michael@0 747 // Check for range and power of 2.
michael@0 748 if (aAlignSize < 2 || aAlignSize > 32768 ||
michael@0 749 (aAlignSize & (aAlignSize - 1)) != 0) {
michael@0 750 return NS_ERROR_INVALID_ARG;
michael@0 751 }
michael@0 752
michael@0 753 for (int i = 0; i < mHeaders.Count(); i++) {
michael@0 754 nsZipHeader *header = mHeaders[i];
michael@0 755
michael@0 756 // Check whether this entry is file and compression method is stored.
michael@0 757 bool isdir;
michael@0 758 rv = header->GetIsDirectory(&isdir);
michael@0 759 if (NS_FAILED(rv)) {
michael@0 760 return rv;
michael@0 761 }
michael@0 762 if (isdir || header->mMethod != 0) {
michael@0 763 continue;
michael@0 764 }
michael@0 765 // Pad extra field to align data starting position to specified size.
michael@0 766 uint32_t old_len = header->mLocalFieldLength;
michael@0 767 rv = header->PadExtraField(header->mOffset, aAlignSize);
michael@0 768 if (NS_FAILED(rv)) {
michael@0 769 continue;
michael@0 770 }
michael@0 771 // No padding means data already aligned.
michael@0 772 uint32_t shift = header->mLocalFieldLength - old_len;
michael@0 773 if (shift == 0) {
michael@0 774 continue;
michael@0 775 }
michael@0 776
michael@0 777 // Flush any remaining data before we start.
michael@0 778 rv = mStream->Flush();
michael@0 779 if (NS_FAILED(rv)) {
michael@0 780 return rv;
michael@0 781 }
michael@0 782
michael@0 783 // Open zip file for reading.
michael@0 784 nsCOMPtr<nsIInputStream> inputStream;
michael@0 785 rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), mFile);
michael@0 786 if (NS_FAILED(rv)) {
michael@0 787 return rv;
michael@0 788 }
michael@0 789
michael@0 790 nsCOMPtr<nsISeekableStream> in_seekable = do_QueryInterface(inputStream);
michael@0 791 nsCOMPtr<nsISeekableStream> out_seekable = do_QueryInterface(mStream);
michael@0 792
michael@0 793 uint32_t data_offset = header->mOffset + header->GetFileHeaderLength() - shift;
michael@0 794 uint32_t count = mCDSOffset - data_offset;
michael@0 795 uint32_t read;
michael@0 796 char buf[4096];
michael@0 797
michael@0 798 // Shift data to aligned postion.
michael@0 799 while (count > 0) {
michael@0 800 read = std::min(count, (uint32_t) sizeof(buf));
michael@0 801
michael@0 802 rv = in_seekable->Seek(nsISeekableStream::NS_SEEK_SET,
michael@0 803 data_offset + count - read);
michael@0 804 if (NS_FAILED(rv)) {
michael@0 805 break;
michael@0 806 }
michael@0 807
michael@0 808 rv = inputStream->Read(buf, read, &read);
michael@0 809 if (NS_FAILED(rv)) {
michael@0 810 break;
michael@0 811 }
michael@0 812
michael@0 813 rv = out_seekable->Seek(nsISeekableStream::NS_SEEK_SET,
michael@0 814 data_offset + count - read + shift);
michael@0 815 if (NS_FAILED(rv)) {
michael@0 816 break;
michael@0 817 }
michael@0 818
michael@0 819 rv = ZW_WriteData(mStream, buf, read);
michael@0 820 if (NS_FAILED(rv)) {
michael@0 821 break;
michael@0 822 }
michael@0 823
michael@0 824 count -= read;
michael@0 825 }
michael@0 826 inputStream->Close();
michael@0 827 if (NS_FAILED(rv)) {
michael@0 828 Cleanup();
michael@0 829 return rv;
michael@0 830 }
michael@0 831
michael@0 832 // Update current header
michael@0 833 rv = out_seekable->Seek(nsISeekableStream::NS_SEEK_SET,
michael@0 834 header->mOffset);
michael@0 835 if (NS_FAILED(rv)) {
michael@0 836 Cleanup();
michael@0 837 return rv;
michael@0 838 }
michael@0 839 rv = header->WriteFileHeader(mStream);
michael@0 840 if (NS_FAILED(rv)) {
michael@0 841 Cleanup();
michael@0 842 return rv;
michael@0 843 }
michael@0 844
michael@0 845 // Update offset of all other headers
michael@0 846 int pos = i + 1;
michael@0 847 while (pos < mHeaders.Count()) {
michael@0 848 mHeaders[pos]->mOffset += shift;
michael@0 849 pos++;
michael@0 850 }
michael@0 851 mCDSOffset += shift;
michael@0 852 rv = SeekCDS();
michael@0 853 if (NS_FAILED(rv)) {
michael@0 854 return rv;
michael@0 855 }
michael@0 856 mCDSDirty = true;
michael@0 857 }
michael@0 858
michael@0 859 return NS_OK;
michael@0 860 }
michael@0 861
michael@0 862 nsresult nsZipWriter::InternalAddEntryDirectory(const nsACString & aZipEntry,
michael@0 863 PRTime aModTime,
michael@0 864 uint32_t aPermissions)
michael@0 865 {
michael@0 866 nsRefPtr<nsZipHeader> header = new nsZipHeader();
michael@0 867 NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
michael@0 868
michael@0 869 uint32_t zipAttributes = ZIP_ATTRS(aPermissions, ZIP_ATTRS_DIRECTORY);
michael@0 870
michael@0 871 if (aZipEntry.Last() != '/') {
michael@0 872 nsCString dirPath;
michael@0 873 dirPath.Assign(aZipEntry + NS_LITERAL_CSTRING("/"));
michael@0 874 header->Init(dirPath, aModTime, zipAttributes, mCDSOffset);
michael@0 875 }
michael@0 876 else
michael@0 877 header->Init(aZipEntry, aModTime, zipAttributes, mCDSOffset);
michael@0 878
michael@0 879 if (mEntryHash.Get(header->mName, nullptr))
michael@0 880 return NS_ERROR_FILE_ALREADY_EXISTS;
michael@0 881
michael@0 882 nsresult rv = header->WriteFileHeader(mStream);
michael@0 883 if (NS_FAILED(rv)) {
michael@0 884 Cleanup();
michael@0 885 return rv;
michael@0 886 }
michael@0 887
michael@0 888 mCDSDirty = true;
michael@0 889 mCDSOffset += header->GetFileHeaderLength();
michael@0 890 mEntryHash.Put(header->mName, mHeaders.Count());
michael@0 891
michael@0 892 if (!mHeaders.AppendObject(header)) {
michael@0 893 Cleanup();
michael@0 894 return NS_ERROR_OUT_OF_MEMORY;
michael@0 895 }
michael@0 896
michael@0 897 return NS_OK;
michael@0 898 }
michael@0 899
michael@0 900 /*
michael@0 901 * Recovering from an error while adding a new entry is simply a case of
michael@0 902 * seeking back to the CDS. If we fail trying to do that though then cleanup
michael@0 903 * and bail out.
michael@0 904 */
michael@0 905 nsresult nsZipWriter::SeekCDS()
michael@0 906 {
michael@0 907 nsresult rv;
michael@0 908 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream, &rv);
michael@0 909 if (NS_FAILED(rv)) {
michael@0 910 Cleanup();
michael@0 911 return rv;
michael@0 912 }
michael@0 913 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mCDSOffset);
michael@0 914 if (NS_FAILED(rv))
michael@0 915 Cleanup();
michael@0 916 return rv;
michael@0 917 }
michael@0 918
michael@0 919 /*
michael@0 920 * In a bad error condition this essentially closes down the component as best
michael@0 921 * it can.
michael@0 922 */
michael@0 923 void nsZipWriter::Cleanup()
michael@0 924 {
michael@0 925 mHeaders.Clear();
michael@0 926 mEntryHash.Clear();
michael@0 927 if (mStream)
michael@0 928 mStream->Close();
michael@0 929 mStream = nullptr;
michael@0 930 mFile = nullptr;
michael@0 931 }
michael@0 932
michael@0 933 /*
michael@0 934 * Called when writing a file to the zip is complete.
michael@0 935 */
michael@0 936 nsresult nsZipWriter::EntryCompleteCallback(nsZipHeader* aHeader,
michael@0 937 nsresult aStatus)
michael@0 938 {
michael@0 939 if (NS_SUCCEEDED(aStatus)) {
michael@0 940 mEntryHash.Put(aHeader->mName, mHeaders.Count());
michael@0 941 if (!mHeaders.AppendObject(aHeader)) {
michael@0 942 mEntryHash.Remove(aHeader->mName);
michael@0 943 SeekCDS();
michael@0 944 return NS_ERROR_OUT_OF_MEMORY;
michael@0 945 }
michael@0 946 mCDSDirty = true;
michael@0 947 mCDSOffset += aHeader->mCSize + aHeader->GetFileHeaderLength();
michael@0 948
michael@0 949 if (mInQueue)
michael@0 950 BeginProcessingNextItem();
michael@0 951
michael@0 952 return NS_OK;
michael@0 953 }
michael@0 954
michael@0 955 nsresult rv = SeekCDS();
michael@0 956 if (mInQueue)
michael@0 957 FinishQueue(aStatus);
michael@0 958 return rv;
michael@0 959 }
michael@0 960
michael@0 961 inline nsresult nsZipWriter::BeginProcessingAddition(nsZipQueueItem* aItem,
michael@0 962 bool* complete)
michael@0 963 {
michael@0 964 if (aItem->mFile) {
michael@0 965 bool exists;
michael@0 966 nsresult rv = aItem->mFile->Exists(&exists);
michael@0 967 NS_ENSURE_SUCCESS(rv, rv);
michael@0 968
michael@0 969 if (!exists) return NS_ERROR_FILE_NOT_FOUND;
michael@0 970
michael@0 971 bool isdir;
michael@0 972 rv = aItem->mFile->IsDirectory(&isdir);
michael@0 973 NS_ENSURE_SUCCESS(rv, rv);
michael@0 974
michael@0 975 rv = aItem->mFile->GetLastModifiedTime(&aItem->mModTime);
michael@0 976 NS_ENSURE_SUCCESS(rv, rv);
michael@0 977 aItem->mModTime *= PR_USEC_PER_MSEC;
michael@0 978
michael@0 979 rv = aItem->mFile->GetPermissions(&aItem->mPermissions);
michael@0 980 NS_ENSURE_SUCCESS(rv, rv);
michael@0 981
michael@0 982 if (!isdir) {
michael@0 983 // Set up for fall through to stream reader
michael@0 984 rv = NS_NewLocalFileInputStream(getter_AddRefs(aItem->mStream),
michael@0 985 aItem->mFile);
michael@0 986 NS_ENSURE_SUCCESS(rv, rv);
michael@0 987 }
michael@0 988 // If a dir then this will fall through to the plain dir addition
michael@0 989 }
michael@0 990
michael@0 991 uint32_t zipAttributes = ZIP_ATTRS(aItem->mPermissions, ZIP_ATTRS_FILE);
michael@0 992
michael@0 993 if (aItem->mStream || aItem->mChannel) {
michael@0 994 nsRefPtr<nsZipHeader> header = new nsZipHeader();
michael@0 995 NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
michael@0 996
michael@0 997 header->Init(aItem->mZipEntry, aItem->mModTime, zipAttributes,
michael@0 998 mCDSOffset);
michael@0 999 nsresult rv = header->WriteFileHeader(mStream);
michael@0 1000 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1001
michael@0 1002 nsRefPtr<nsZipDataStream> stream = new nsZipDataStream();
michael@0 1003 NS_ENSURE_TRUE(stream, NS_ERROR_OUT_OF_MEMORY);
michael@0 1004 rv = stream->Init(this, mStream, header, aItem->mCompression);
michael@0 1005 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1006
michael@0 1007 if (aItem->mStream) {
michael@0 1008 nsCOMPtr<nsIInputStreamPump> pump;
michael@0 1009 rv = NS_NewInputStreamPump(getter_AddRefs(pump), aItem->mStream,
michael@0 1010 -1, -1, 0, 0, true);
michael@0 1011 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1012
michael@0 1013 rv = pump->AsyncRead(stream, nullptr);
michael@0 1014 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1015 }
michael@0 1016 else {
michael@0 1017 rv = aItem->mChannel->AsyncOpen(stream, nullptr);
michael@0 1018 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1019 }
michael@0 1020
michael@0 1021 return NS_OK;
michael@0 1022 }
michael@0 1023
michael@0 1024 // Must be plain directory addition
michael@0 1025 *complete = true;
michael@0 1026 return InternalAddEntryDirectory(aItem->mZipEntry, aItem->mModTime,
michael@0 1027 aItem->mPermissions);
michael@0 1028 }
michael@0 1029
michael@0 1030 inline nsresult nsZipWriter::BeginProcessingRemoval(int32_t aPos)
michael@0 1031 {
michael@0 1032 // Open the zip file for reading
michael@0 1033 nsCOMPtr<nsIInputStream> inputStream;
michael@0 1034 nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
michael@0 1035 mFile);
michael@0 1036 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1037 nsCOMPtr<nsIInputStreamPump> pump;
michael@0 1038 rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream, -1, -1, 0,
michael@0 1039 0, true);
michael@0 1040 if (NS_FAILED(rv)) {
michael@0 1041 inputStream->Close();
michael@0 1042 return rv;
michael@0 1043 }
michael@0 1044 nsCOMPtr<nsIStreamListener> listener;
michael@0 1045 rv = NS_NewSimpleStreamListener(getter_AddRefs(listener), mStream, this);
michael@0 1046 if (NS_FAILED(rv)) {
michael@0 1047 inputStream->Close();
michael@0 1048 return rv;
michael@0 1049 }
michael@0 1050
michael@0 1051 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
michael@0 1052 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
michael@0 1053 mHeaders[aPos]->mOffset);
michael@0 1054 if (NS_FAILED(rv)) {
michael@0 1055 inputStream->Close();
michael@0 1056 return rv;
michael@0 1057 }
michael@0 1058
michael@0 1059 uint32_t shift = (mHeaders[aPos + 1]->mOffset -
michael@0 1060 mHeaders[aPos]->mOffset);
michael@0 1061 mCDSOffset -= shift;
michael@0 1062 int32_t pos2 = aPos + 1;
michael@0 1063 while (pos2 < mHeaders.Count()) {
michael@0 1064 mEntryHash.Put(mHeaders[pos2]->mName, pos2 - 1);
michael@0 1065 mHeaders[pos2]->mOffset -= shift;
michael@0 1066 pos2++;
michael@0 1067 }
michael@0 1068
michael@0 1069 mEntryHash.Remove(mHeaders[aPos]->mName);
michael@0 1070 mHeaders.RemoveObjectAt(aPos);
michael@0 1071 mCDSDirty = true;
michael@0 1072
michael@0 1073 rv = pump->AsyncRead(listener, nullptr);
michael@0 1074 if (NS_FAILED(rv)) {
michael@0 1075 inputStream->Close();
michael@0 1076 Cleanup();
michael@0 1077 return rv;
michael@0 1078 }
michael@0 1079 return NS_OK;
michael@0 1080 }
michael@0 1081
michael@0 1082 /*
michael@0 1083 * Starts processing on the next item in the queue.
michael@0 1084 */
michael@0 1085 void nsZipWriter::BeginProcessingNextItem()
michael@0 1086 {
michael@0 1087 while (!mQueue.IsEmpty()) {
michael@0 1088
michael@0 1089 nsZipQueueItem next = mQueue[0];
michael@0 1090 mQueue.RemoveElementAt(0);
michael@0 1091
michael@0 1092 if (next.mOperation == OPERATION_REMOVE) {
michael@0 1093 int32_t pos = -1;
michael@0 1094 if (mEntryHash.Get(next.mZipEntry, &pos)) {
michael@0 1095 if (pos < mHeaders.Count() - 1) {
michael@0 1096 nsresult rv = BeginProcessingRemoval(pos);
michael@0 1097 if (NS_FAILED(rv)) FinishQueue(rv);
michael@0 1098 return;
michael@0 1099 }
michael@0 1100
michael@0 1101 mCDSOffset = mHeaders[pos]->mOffset;
michael@0 1102 nsresult rv = SeekCDS();
michael@0 1103 if (NS_FAILED(rv)) {
michael@0 1104 FinishQueue(rv);
michael@0 1105 return;
michael@0 1106 }
michael@0 1107 mEntryHash.Remove(mHeaders[pos]->mName);
michael@0 1108 mHeaders.RemoveObjectAt(pos);
michael@0 1109 }
michael@0 1110 else {
michael@0 1111 FinishQueue(NS_ERROR_FILE_NOT_FOUND);
michael@0 1112 return;
michael@0 1113 }
michael@0 1114 }
michael@0 1115 else if (next.mOperation == OPERATION_ADD) {
michael@0 1116 if (mEntryHash.Get(next.mZipEntry, nullptr)) {
michael@0 1117 FinishQueue(NS_ERROR_FILE_ALREADY_EXISTS);
michael@0 1118 return;
michael@0 1119 }
michael@0 1120
michael@0 1121 bool complete = false;
michael@0 1122 nsresult rv = BeginProcessingAddition(&next, &complete);
michael@0 1123 if (NS_FAILED(rv)) {
michael@0 1124 SeekCDS();
michael@0 1125 FinishQueue(rv);
michael@0 1126 return;
michael@0 1127 }
michael@0 1128 if (!complete)
michael@0 1129 return;
michael@0 1130 }
michael@0 1131 }
michael@0 1132
michael@0 1133 FinishQueue(NS_OK);
michael@0 1134 }
michael@0 1135
michael@0 1136 /*
michael@0 1137 * Ends processing with the given status.
michael@0 1138 */
michael@0 1139 void nsZipWriter::FinishQueue(nsresult aStatus)
michael@0 1140 {
michael@0 1141 nsCOMPtr<nsIRequestObserver> observer = mProcessObserver;
michael@0 1142 nsCOMPtr<nsISupports> context = mProcessContext;
michael@0 1143 // Clean up everything first in case the observer decides to queue more
michael@0 1144 // things
michael@0 1145 mProcessObserver = nullptr;
michael@0 1146 mProcessContext = nullptr;
michael@0 1147 mInQueue = false;
michael@0 1148
michael@0 1149 if (observer)
michael@0 1150 observer->OnStopRequest(nullptr, context, aStatus);
michael@0 1151 }

mercurial