modules/libjar/zipwriter/src/nsZipWriter.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/modules/libjar/zipwriter/src/nsZipWriter.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1151 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 + */
     1.8 +
     1.9 +#include "nsZipWriter.h"
    1.10 +
    1.11 +#include <algorithm>
    1.12 +
    1.13 +#include "StreamFunctions.h"
    1.14 +#include "nsZipDataStream.h"
    1.15 +#include "nsISeekableStream.h"
    1.16 +#include "nsIAsyncStreamCopier.h"
    1.17 +#include "nsIStreamListener.h"
    1.18 +#include "nsIInputStreamPump.h"
    1.19 +#include "nsComponentManagerUtils.h"
    1.20 +#include "nsMemory.h"
    1.21 +#include "nsError.h"
    1.22 +#include "nsStreamUtils.h"
    1.23 +#include "nsThreadUtils.h"
    1.24 +#include "nsNetUtil.h"
    1.25 +#include "prio.h"
    1.26 +
    1.27 +#define ZIP_EOCDR_HEADER_SIZE 22
    1.28 +#define ZIP_EOCDR_HEADER_SIGNATURE 0x06054b50
    1.29 +
    1.30 +/**
    1.31 + * nsZipWriter is used to create and add to zip files.
    1.32 + * It is based on the spec available at
    1.33 + * http://www.pkware.com/documents/casestudies/APPNOTE.TXT.
    1.34 + * 
    1.35 + * The basic structure of a zip file created is slightly simpler than that
    1.36 + * illustrated in the spec because certain features of the zip format are
    1.37 + * unsupported:
    1.38 + * 
    1.39 + * [local file header 1]
    1.40 + * [file data 1]
    1.41 + * . 
    1.42 + * .
    1.43 + * .
    1.44 + * [local file header n]
    1.45 + * [file data n]
    1.46 + * [central directory]
    1.47 + * [end of central directory record]
    1.48 + */
    1.49 +NS_IMPL_ISUPPORTS(nsZipWriter, nsIZipWriter,
    1.50 +                  nsIRequestObserver)
    1.51 +
    1.52 +nsZipWriter::nsZipWriter()
    1.53 +{
    1.54 +    mInQueue = false;
    1.55 +}
    1.56 +
    1.57 +nsZipWriter::~nsZipWriter()
    1.58 +{
    1.59 +    if (mStream && !mInQueue)
    1.60 +        Close();
    1.61 +}
    1.62 +
    1.63 +/* attribute AString comment; */
    1.64 +NS_IMETHODIMP nsZipWriter::GetComment(nsACString & aComment)
    1.65 +{
    1.66 +    if (!mStream)
    1.67 +        return NS_ERROR_NOT_INITIALIZED;
    1.68 +
    1.69 +    aComment = mComment;
    1.70 +    return NS_OK;
    1.71 +}
    1.72 +
    1.73 +NS_IMETHODIMP nsZipWriter::SetComment(const nsACString & aComment)
    1.74 +{
    1.75 +    if (!mStream)
    1.76 +        return NS_ERROR_NOT_INITIALIZED;
    1.77 +
    1.78 +    mComment = aComment;
    1.79 +    mCDSDirty = true;
    1.80 +    return NS_OK;
    1.81 +}
    1.82 +
    1.83 +/* readonly attribute boolean inQueue; */
    1.84 +NS_IMETHODIMP nsZipWriter::GetInQueue(bool *aInQueue)
    1.85 +{
    1.86 +    *aInQueue = mInQueue;
    1.87 +    return NS_OK;
    1.88 +}
    1.89 +
    1.90 +/* readonly attribute nsIFile file; */
    1.91 +NS_IMETHODIMP nsZipWriter::GetFile(nsIFile **aFile)
    1.92 +{
    1.93 +    if (!mFile)
    1.94 +        return NS_ERROR_NOT_INITIALIZED;
    1.95 +
    1.96 +    nsCOMPtr<nsIFile> file;
    1.97 +    nsresult rv = mFile->Clone(getter_AddRefs(file));
    1.98 +    NS_ENSURE_SUCCESS(rv, rv);
    1.99 +
   1.100 +    NS_ADDREF(*aFile = file);
   1.101 +    return NS_OK;
   1.102 +}
   1.103 +
   1.104 +/*
   1.105 + * Reads file entries out of an existing zip file.
   1.106 + */
   1.107 +nsresult nsZipWriter::ReadFile(nsIFile *aFile)
   1.108 +{
   1.109 +    int64_t size;
   1.110 +    nsresult rv = aFile->GetFileSize(&size);
   1.111 +    NS_ENSURE_SUCCESS(rv, rv);
   1.112 +
   1.113 +    // If the file is too short, it cannot be a valid archive, thus we fail
   1.114 +    // without even attempting to open it
   1.115 +    NS_ENSURE_TRUE(size > ZIP_EOCDR_HEADER_SIZE, NS_ERROR_FILE_CORRUPTED);
   1.116 +
   1.117 +    nsCOMPtr<nsIInputStream> inputStream;
   1.118 +    rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
   1.119 +    NS_ENSURE_SUCCESS(rv, rv);
   1.120 +
   1.121 +    uint8_t buf[1024];
   1.122 +    int64_t seek = size - 1024;
   1.123 +    uint32_t length = 1024;
   1.124 +
   1.125 +    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(inputStream);
   1.126 +
   1.127 +    while (true) {
   1.128 +        if (seek < 0) {
   1.129 +            length += (int32_t)seek;
   1.130 +            seek = 0;
   1.131 +        }
   1.132 +
   1.133 +        rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, seek);
   1.134 +        if (NS_FAILED(rv)) {
   1.135 +            inputStream->Close();
   1.136 +            return rv;
   1.137 +        }
   1.138 +        rv = ZW_ReadData(inputStream, (char *)buf, length);
   1.139 +        if (NS_FAILED(rv)) {
   1.140 +            inputStream->Close();
   1.141 +            return rv;
   1.142 +        }
   1.143 +
   1.144 +        /*
   1.145 +         * We have to backtrack from the end of the file until we find the
   1.146 +         * CDS signature
   1.147 +         */
   1.148 +        // We know it's at least this far from the end
   1.149 +        for (uint32_t pos = length - ZIP_EOCDR_HEADER_SIZE;
   1.150 +             (int32_t)pos >= 0; pos--) {
   1.151 +            uint32_t sig = PEEK32(buf + pos);
   1.152 +            if (sig == ZIP_EOCDR_HEADER_SIGNATURE) {
   1.153 +                // Skip down to entry count
   1.154 +                pos += 10;
   1.155 +                uint32_t entries = READ16(buf, &pos);
   1.156 +                // Skip past CDS size
   1.157 +                pos += 4;
   1.158 +                mCDSOffset = READ32(buf, &pos);
   1.159 +                uint32_t commentlen = READ16(buf, &pos);
   1.160 +
   1.161 +                if (commentlen == 0)
   1.162 +                    mComment.Truncate();
   1.163 +                else if (pos + commentlen <= length)
   1.164 +                    mComment.Assign((const char *)buf + pos, commentlen);
   1.165 +                else {
   1.166 +                    if ((seek + pos + commentlen) > size) {
   1.167 +                        inputStream->Close();
   1.168 +                        return NS_ERROR_FILE_CORRUPTED;
   1.169 +                    }
   1.170 +                    nsAutoArrayPtr<char> field(new char[commentlen]);
   1.171 +                    NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
   1.172 +                    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
   1.173 +                                        seek + pos);
   1.174 +                    if (NS_FAILED(rv)) {
   1.175 +                        inputStream->Close();
   1.176 +                        return rv;
   1.177 +                    }
   1.178 +                    rv = ZW_ReadData(inputStream, field.get(), length);
   1.179 +                    if (NS_FAILED(rv)) {
   1.180 +                        inputStream->Close();
   1.181 +                        return rv;
   1.182 +                    }
   1.183 +                    mComment.Assign(field.get(), commentlen);
   1.184 +                }
   1.185 +
   1.186 +                rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
   1.187 +                                    mCDSOffset);
   1.188 +                if (NS_FAILED(rv)) {
   1.189 +                    inputStream->Close();
   1.190 +                    return rv;
   1.191 +                }
   1.192 +
   1.193 +                for (uint32_t entry = 0; entry < entries; entry++) {
   1.194 +                    nsZipHeader* header = new nsZipHeader();
   1.195 +                    if (!header) {
   1.196 +                        inputStream->Close();
   1.197 +                        mEntryHash.Clear();
   1.198 +                        mHeaders.Clear();
   1.199 +                        return NS_ERROR_OUT_OF_MEMORY;
   1.200 +                    }
   1.201 +                    rv = header->ReadCDSHeader(inputStream);
   1.202 +                    if (NS_FAILED(rv)) {
   1.203 +                        inputStream->Close();
   1.204 +                        mEntryHash.Clear();
   1.205 +                        mHeaders.Clear();
   1.206 +                        return rv;
   1.207 +                    }
   1.208 +                    mEntryHash.Put(header->mName, mHeaders.Count());
   1.209 +                    if (!mHeaders.AppendObject(header))
   1.210 +                        return NS_ERROR_OUT_OF_MEMORY;
   1.211 +                }
   1.212 +
   1.213 +                return inputStream->Close();
   1.214 +            }
   1.215 +        }
   1.216 +
   1.217 +        if (seek == 0) {
   1.218 +            // We've reached the start with no signature found. Corrupt.
   1.219 +            inputStream->Close();
   1.220 +            return NS_ERROR_FILE_CORRUPTED;
   1.221 +        }
   1.222 +
   1.223 +        // Overlap by the size of the end of cdr
   1.224 +        seek -= (1024 - ZIP_EOCDR_HEADER_SIZE);
   1.225 +    }
   1.226 +    // Will never reach here in reality
   1.227 +    NS_NOTREACHED("Loop should never complete");
   1.228 +    return NS_ERROR_UNEXPECTED;
   1.229 +}
   1.230 +
   1.231 +/* void open (in nsIFile aFile, in int32_t aIoFlags); */
   1.232 +NS_IMETHODIMP nsZipWriter::Open(nsIFile *aFile, int32_t aIoFlags)
   1.233 +{
   1.234 +    if (mStream)
   1.235 +        return NS_ERROR_ALREADY_INITIALIZED;
   1.236 +
   1.237 +    NS_ENSURE_ARG_POINTER(aFile);
   1.238 +
   1.239 +    // Need to be able to write to the file
   1.240 +    if (aIoFlags & PR_RDONLY)
   1.241 +        return NS_ERROR_FAILURE;
   1.242 +    
   1.243 +    nsresult rv = aFile->Clone(getter_AddRefs(mFile));
   1.244 +    NS_ENSURE_SUCCESS(rv, rv);
   1.245 +
   1.246 +    bool exists;
   1.247 +    rv = mFile->Exists(&exists);
   1.248 +    NS_ENSURE_SUCCESS(rv, rv);
   1.249 +    if (!exists && !(aIoFlags & PR_CREATE_FILE))
   1.250 +        return NS_ERROR_FILE_NOT_FOUND;
   1.251 +
   1.252 +    if (exists && !(aIoFlags & (PR_TRUNCATE | PR_WRONLY))) {
   1.253 +        rv = ReadFile(mFile);
   1.254 +        NS_ENSURE_SUCCESS(rv, rv);
   1.255 +        mCDSDirty = false;
   1.256 +    }
   1.257 +    else {
   1.258 +        mCDSOffset = 0;
   1.259 +        mCDSDirty = true;
   1.260 +        mComment.Truncate();
   1.261 +    }
   1.262 +
   1.263 +    // Silently drop PR_APPEND
   1.264 +    aIoFlags &= 0xef;
   1.265 +
   1.266 +    nsCOMPtr<nsIOutputStream> stream;
   1.267 +    rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), mFile, aIoFlags);
   1.268 +    if (NS_FAILED(rv)) {
   1.269 +        mHeaders.Clear();
   1.270 +        mEntryHash.Clear();
   1.271 +        return rv;
   1.272 +    }
   1.273 +
   1.274 +    rv = NS_NewBufferedOutputStream(getter_AddRefs(mStream), stream, 64 * 1024);
   1.275 +    if (NS_FAILED(rv)) {
   1.276 +        stream->Close();
   1.277 +        mHeaders.Clear();
   1.278 +        mEntryHash.Clear();
   1.279 +        return rv;
   1.280 +    }
   1.281 +
   1.282 +    if (mCDSOffset > 0) {
   1.283 +        rv = SeekCDS();
   1.284 +        NS_ENSURE_SUCCESS(rv, rv);
   1.285 +    }
   1.286 +
   1.287 +    return NS_OK;
   1.288 +}
   1.289 +
   1.290 +/* nsIZipEntry getEntry (in AString aZipEntry); */
   1.291 +NS_IMETHODIMP nsZipWriter::GetEntry(const nsACString & aZipEntry,
   1.292 +                                    nsIZipEntry **_retval)
   1.293 +{
   1.294 +    int32_t pos;
   1.295 +    if (mEntryHash.Get(aZipEntry, &pos))
   1.296 +        NS_ADDREF(*_retval = mHeaders[pos]);
   1.297 +    else
   1.298 +        *_retval = nullptr;
   1.299 +
   1.300 +    return NS_OK;
   1.301 +}
   1.302 +
   1.303 +/* boolean hasEntry (in AString aZipEntry); */
   1.304 +NS_IMETHODIMP nsZipWriter::HasEntry(const nsACString & aZipEntry,
   1.305 +                                    bool *_retval)
   1.306 +{
   1.307 +    *_retval = mEntryHash.Get(aZipEntry, nullptr);
   1.308 +
   1.309 +    return NS_OK;
   1.310 +}
   1.311 +
   1.312 +/* void addEntryDirectory (in AUTF8String aZipEntry, in PRTime aModTime,
   1.313 + *                         in boolean aQueue); */
   1.314 +NS_IMETHODIMP nsZipWriter::AddEntryDirectory(const nsACString & aZipEntry,
   1.315 +                                             PRTime aModTime, bool aQueue)
   1.316 +{
   1.317 +    if (!mStream)
   1.318 +        return NS_ERROR_NOT_INITIALIZED;
   1.319 +
   1.320 +    if (aQueue) {
   1.321 +        nsZipQueueItem item;
   1.322 +        item.mOperation = OPERATION_ADD;
   1.323 +        item.mZipEntry = aZipEntry;
   1.324 +        item.mModTime = aModTime;
   1.325 +        item.mPermissions = PERMISSIONS_DIR;
   1.326 +        if (!mQueue.AppendElement(item))
   1.327 +            return NS_ERROR_OUT_OF_MEMORY;
   1.328 +        return NS_OK;
   1.329 +    }
   1.330 +
   1.331 +    if (mInQueue)
   1.332 +        return NS_ERROR_IN_PROGRESS;
   1.333 +    return InternalAddEntryDirectory(aZipEntry, aModTime, PERMISSIONS_DIR);
   1.334 +}
   1.335 +
   1.336 +/* void addEntryFile (in AUTF8String aZipEntry, in int32_t aCompression,
   1.337 + *                    in nsIFile aFile, in boolean aQueue); */
   1.338 +NS_IMETHODIMP nsZipWriter::AddEntryFile(const nsACString & aZipEntry,
   1.339 +                                        int32_t aCompression, nsIFile *aFile,
   1.340 +                                        bool aQueue)
   1.341 +{
   1.342 +    NS_ENSURE_ARG_POINTER(aFile);
   1.343 +    if (!mStream)
   1.344 +        return NS_ERROR_NOT_INITIALIZED;
   1.345 +
   1.346 +    nsresult rv;
   1.347 +    if (aQueue) {
   1.348 +        nsZipQueueItem item;
   1.349 +        item.mOperation = OPERATION_ADD;
   1.350 +        item.mZipEntry = aZipEntry;
   1.351 +        item.mCompression = aCompression;
   1.352 +        rv = aFile->Clone(getter_AddRefs(item.mFile));
   1.353 +        NS_ENSURE_SUCCESS(rv, rv);
   1.354 +        if (!mQueue.AppendElement(item))
   1.355 +            return NS_ERROR_OUT_OF_MEMORY;
   1.356 +        return NS_OK;
   1.357 +    }
   1.358 +
   1.359 +    if (mInQueue)
   1.360 +        return NS_ERROR_IN_PROGRESS;
   1.361 +
   1.362 +    bool exists;
   1.363 +    rv = aFile->Exists(&exists);
   1.364 +    NS_ENSURE_SUCCESS(rv, rv);
   1.365 +    if (!exists)
   1.366 +        return NS_ERROR_FILE_NOT_FOUND;
   1.367 +
   1.368 +    bool isdir;
   1.369 +    rv = aFile->IsDirectory(&isdir);
   1.370 +    NS_ENSURE_SUCCESS(rv, rv);
   1.371 +
   1.372 +    PRTime modtime;
   1.373 +    rv = aFile->GetLastModifiedTime(&modtime);
   1.374 +    NS_ENSURE_SUCCESS(rv, rv);
   1.375 +    modtime *= PR_USEC_PER_MSEC;
   1.376 +
   1.377 +    uint32_t permissions;
   1.378 +    rv = aFile->GetPermissions(&permissions);
   1.379 +    NS_ENSURE_SUCCESS(rv, rv);
   1.380 +
   1.381 +    if (isdir)
   1.382 +        return InternalAddEntryDirectory(aZipEntry, modtime, permissions);
   1.383 +
   1.384 +    if (mEntryHash.Get(aZipEntry, nullptr))
   1.385 +        return NS_ERROR_FILE_ALREADY_EXISTS;
   1.386 +
   1.387 +    nsCOMPtr<nsIInputStream> inputStream;
   1.388 +    rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
   1.389 +                                    aFile);
   1.390 +    NS_ENSURE_SUCCESS(rv, rv);
   1.391 +
   1.392 +    rv = AddEntryStream(aZipEntry, modtime, aCompression, inputStream,
   1.393 +                        false, permissions);
   1.394 +    NS_ENSURE_SUCCESS(rv, rv);
   1.395 +
   1.396 +    return inputStream->Close();
   1.397 +}
   1.398 +
   1.399 +/* void addEntryChannel (in AUTF8String aZipEntry, in PRTime aModTime,
   1.400 + *                       in int32_t aCompression, in nsIChannel aChannel,
   1.401 + *                       in boolean aQueue); */
   1.402 +NS_IMETHODIMP nsZipWriter::AddEntryChannel(const nsACString & aZipEntry,
   1.403 +                                           PRTime aModTime,
   1.404 +                                           int32_t aCompression,
   1.405 +                                           nsIChannel *aChannel,
   1.406 +                                           bool aQueue)
   1.407 +{
   1.408 +    NS_ENSURE_ARG_POINTER(aChannel);
   1.409 +    if (!mStream)
   1.410 +        return NS_ERROR_NOT_INITIALIZED;
   1.411 +
   1.412 +    if (aQueue) {
   1.413 +        nsZipQueueItem item;
   1.414 +        item.mOperation = OPERATION_ADD;
   1.415 +        item.mZipEntry = aZipEntry;
   1.416 +        item.mModTime = aModTime;
   1.417 +        item.mCompression = aCompression;
   1.418 +        item.mPermissions = PERMISSIONS_FILE;
   1.419 +        item.mChannel = aChannel;
   1.420 +        if (!mQueue.AppendElement(item))
   1.421 +            return NS_ERROR_OUT_OF_MEMORY;
   1.422 +        return NS_OK;
   1.423 +    }
   1.424 +
   1.425 +    if (mInQueue)
   1.426 +        return NS_ERROR_IN_PROGRESS;
   1.427 +    if (mEntryHash.Get(aZipEntry, nullptr))
   1.428 +        return NS_ERROR_FILE_ALREADY_EXISTS;
   1.429 +
   1.430 +    nsCOMPtr<nsIInputStream> inputStream;
   1.431 +    nsresult rv = aChannel->Open(getter_AddRefs(inputStream));
   1.432 +    NS_ENSURE_SUCCESS(rv, rv);
   1.433 +
   1.434 +    rv = AddEntryStream(aZipEntry, aModTime, aCompression, inputStream,
   1.435 +                        false, PERMISSIONS_FILE);
   1.436 +    NS_ENSURE_SUCCESS(rv, rv);
   1.437 +
   1.438 +    return inputStream->Close();
   1.439 +}
   1.440 +
   1.441 +/* void addEntryStream (in AUTF8String aZipEntry, in PRTime aModTime,
   1.442 + *                      in int32_t aCompression, in nsIInputStream aStream,
   1.443 + *                      in boolean aQueue); */
   1.444 +NS_IMETHODIMP nsZipWriter::AddEntryStream(const nsACString & aZipEntry,
   1.445 +                                          PRTime aModTime,
   1.446 +                                          int32_t aCompression,
   1.447 +                                          nsIInputStream *aStream,
   1.448 +                                          bool aQueue)
   1.449 +{
   1.450 +    return AddEntryStream(aZipEntry, aModTime, aCompression, aStream, aQueue,
   1.451 +                          PERMISSIONS_FILE);
   1.452 +}
   1.453 +
   1.454 +/* void addEntryStream (in AUTF8String aZipEntry, in PRTime aModTime,
   1.455 + *                      in int32_t aCompression, in nsIInputStream aStream,
   1.456 + *                      in boolean aQueue, in unsigned long aPermissions); */
   1.457 +nsresult nsZipWriter::AddEntryStream(const nsACString & aZipEntry,
   1.458 +                                     PRTime aModTime,
   1.459 +                                     int32_t aCompression,
   1.460 +                                     nsIInputStream *aStream,
   1.461 +                                     bool aQueue,
   1.462 +                                     uint32_t aPermissions)
   1.463 +{
   1.464 +    NS_ENSURE_ARG_POINTER(aStream);
   1.465 +    if (!mStream)
   1.466 +        return NS_ERROR_NOT_INITIALIZED;
   1.467 +
   1.468 +    if (aQueue) {
   1.469 +        nsZipQueueItem item;
   1.470 +        item.mOperation = OPERATION_ADD;
   1.471 +        item.mZipEntry = aZipEntry;
   1.472 +        item.mModTime = aModTime;
   1.473 +        item.mCompression = aCompression;
   1.474 +        item.mPermissions = aPermissions;
   1.475 +        item.mStream = aStream;
   1.476 +        if (!mQueue.AppendElement(item))
   1.477 +            return NS_ERROR_OUT_OF_MEMORY;
   1.478 +        return NS_OK;
   1.479 +    }
   1.480 +
   1.481 +    if (mInQueue)
   1.482 +        return NS_ERROR_IN_PROGRESS;
   1.483 +    if (mEntryHash.Get(aZipEntry, nullptr))
   1.484 +        return NS_ERROR_FILE_ALREADY_EXISTS;
   1.485 +
   1.486 +    nsRefPtr<nsZipHeader> header = new nsZipHeader();
   1.487 +    NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
   1.488 +    header->Init(aZipEntry, aModTime, ZIP_ATTRS(aPermissions, ZIP_ATTRS_FILE),
   1.489 +                 mCDSOffset);
   1.490 +    nsresult rv = header->WriteFileHeader(mStream);
   1.491 +    if (NS_FAILED(rv)) {
   1.492 +        SeekCDS();
   1.493 +        return rv;
   1.494 +    }
   1.495 +
   1.496 +    nsRefPtr<nsZipDataStream> stream = new nsZipDataStream();
   1.497 +    if (!stream) {
   1.498 +        SeekCDS();
   1.499 +        return NS_ERROR_OUT_OF_MEMORY;
   1.500 +    }
   1.501 +    rv = stream->Init(this, mStream, header, aCompression);
   1.502 +    if (NS_FAILED(rv)) {
   1.503 +        SeekCDS();
   1.504 +        return rv;
   1.505 +    }
   1.506 +
   1.507 +    rv = stream->ReadStream(aStream);
   1.508 +    if (NS_FAILED(rv))
   1.509 +        SeekCDS();
   1.510 +    return rv;
   1.511 +}
   1.512 +
   1.513 +/* void removeEntry (in AUTF8String aZipEntry, in boolean aQueue); */
   1.514 +NS_IMETHODIMP nsZipWriter::RemoveEntry(const nsACString & aZipEntry,
   1.515 +                                       bool aQueue)
   1.516 +{
   1.517 +    if (!mStream)
   1.518 +        return NS_ERROR_NOT_INITIALIZED;
   1.519 +
   1.520 +    if (aQueue) {
   1.521 +        nsZipQueueItem item;
   1.522 +        item.mOperation = OPERATION_REMOVE;
   1.523 +        item.mZipEntry = aZipEntry;
   1.524 +        if (!mQueue.AppendElement(item))
   1.525 +            return NS_ERROR_OUT_OF_MEMORY;
   1.526 +        return NS_OK;
   1.527 +    }
   1.528 +
   1.529 +    if (mInQueue)
   1.530 +        return NS_ERROR_IN_PROGRESS;
   1.531 +
   1.532 +    int32_t pos;
   1.533 +    if (mEntryHash.Get(aZipEntry, &pos)) {
   1.534 +        // Flush any remaining data before we seek.
   1.535 +        nsresult rv = mStream->Flush();
   1.536 +        NS_ENSURE_SUCCESS(rv, rv);
   1.537 +        if (pos < mHeaders.Count() - 1) {
   1.538 +            // This is not the last entry, pull back the data.
   1.539 +            nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
   1.540 +            rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
   1.541 +                                mHeaders[pos]->mOffset);
   1.542 +            NS_ENSURE_SUCCESS(rv, rv);
   1.543 +
   1.544 +            nsCOMPtr<nsIInputStream> inputStream;
   1.545 +            rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
   1.546 +                                            mFile);
   1.547 +            NS_ENSURE_SUCCESS(rv, rv);
   1.548 +            seekable = do_QueryInterface(inputStream);
   1.549 +            rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
   1.550 +                                mHeaders[pos + 1]->mOffset);
   1.551 +            if (NS_FAILED(rv)) {
   1.552 +                inputStream->Close();
   1.553 +                return rv;
   1.554 +            }
   1.555 +
   1.556 +            uint32_t count = mCDSOffset - mHeaders[pos + 1]->mOffset;
   1.557 +            uint32_t read = 0;
   1.558 +            char buf[4096];
   1.559 +            while (count > 0) {
   1.560 +                read = std::min(count, (uint32_t) sizeof(buf));
   1.561 +
   1.562 +                rv = inputStream->Read(buf, read, &read);
   1.563 +                if (NS_FAILED(rv)) {
   1.564 +                    inputStream->Close();
   1.565 +                    Cleanup();
   1.566 +                    return rv;
   1.567 +                }
   1.568 +
   1.569 +                rv = ZW_WriteData(mStream, buf, read);
   1.570 +                if (NS_FAILED(rv)) {
   1.571 +                    inputStream->Close();
   1.572 +                    Cleanup();
   1.573 +                    return rv;
   1.574 +                }
   1.575 +
   1.576 +                count -= read;
   1.577 +            }
   1.578 +            inputStream->Close();
   1.579 +
   1.580 +            // Rewrite header offsets and update hash
   1.581 +            uint32_t shift = (mHeaders[pos + 1]->mOffset -
   1.582 +                              mHeaders[pos]->mOffset);
   1.583 +            mCDSOffset -= shift;
   1.584 +            int32_t pos2 = pos + 1;
   1.585 +            while (pos2 < mHeaders.Count()) {
   1.586 +                mEntryHash.Put(mHeaders[pos2]->mName, pos2-1);
   1.587 +                mHeaders[pos2]->mOffset -= shift;
   1.588 +                pos2++;
   1.589 +            }
   1.590 +        }
   1.591 +        else {
   1.592 +            // Remove the last entry is just a case of moving the CDS
   1.593 +            mCDSOffset = mHeaders[pos]->mOffset;
   1.594 +            rv = SeekCDS();
   1.595 +            NS_ENSURE_SUCCESS(rv, rv);
   1.596 +        }
   1.597 +
   1.598 +        mEntryHash.Remove(mHeaders[pos]->mName);
   1.599 +        mHeaders.RemoveObjectAt(pos);
   1.600 +        mCDSDirty = true;
   1.601 +
   1.602 +        return NS_OK;
   1.603 +    }
   1.604 +
   1.605 +    return NS_ERROR_FILE_NOT_FOUND;
   1.606 +}
   1.607 +
   1.608 +/* void processQueue (in nsIRequestObserver aObserver,
   1.609 + *                    in nsISupports aContext); */
   1.610 +NS_IMETHODIMP nsZipWriter::ProcessQueue(nsIRequestObserver *aObserver,
   1.611 +                                        nsISupports *aContext)
   1.612 +{
   1.613 +    if (!mStream)
   1.614 +        return NS_ERROR_NOT_INITIALIZED;
   1.615 +    if (mInQueue)
   1.616 +        return NS_ERROR_IN_PROGRESS;
   1.617 +
   1.618 +    mProcessObserver = aObserver;
   1.619 +    mProcessContext = aContext;
   1.620 +    mInQueue = true;
   1.621 +
   1.622 +    if (mProcessObserver)
   1.623 +        mProcessObserver->OnStartRequest(nullptr, mProcessContext);
   1.624 +
   1.625 +    BeginProcessingNextItem();
   1.626 +
   1.627 +    return NS_OK;
   1.628 +}
   1.629 +
   1.630 +/* void close (); */
   1.631 +NS_IMETHODIMP nsZipWriter::Close()
   1.632 +{
   1.633 +    if (!mStream)
   1.634 +        return NS_ERROR_NOT_INITIALIZED;
   1.635 +    if (mInQueue)
   1.636 +        return NS_ERROR_IN_PROGRESS;
   1.637 +
   1.638 +    if (mCDSDirty) {
   1.639 +        uint32_t size = 0;
   1.640 +        for (int32_t i = 0; i < mHeaders.Count(); i++) {
   1.641 +            nsresult rv = mHeaders[i]->WriteCDSHeader(mStream);
   1.642 +            if (NS_FAILED(rv)) {
   1.643 +                Cleanup();
   1.644 +                return rv;
   1.645 +            }
   1.646 +            size += mHeaders[i]->GetCDSHeaderLength();
   1.647 +        }
   1.648 +
   1.649 +        uint8_t buf[ZIP_EOCDR_HEADER_SIZE];
   1.650 +        uint32_t pos = 0;
   1.651 +        WRITE32(buf, &pos, ZIP_EOCDR_HEADER_SIGNATURE);
   1.652 +        WRITE16(buf, &pos, 0);
   1.653 +        WRITE16(buf, &pos, 0);
   1.654 +        WRITE16(buf, &pos, mHeaders.Count());
   1.655 +        WRITE16(buf, &pos, mHeaders.Count());
   1.656 +        WRITE32(buf, &pos, size);
   1.657 +        WRITE32(buf, &pos, mCDSOffset);
   1.658 +        WRITE16(buf, &pos, mComment.Length());
   1.659 +
   1.660 +        nsresult rv = ZW_WriteData(mStream, (const char *)buf, pos);
   1.661 +        if (NS_FAILED(rv)) {
   1.662 +            Cleanup();
   1.663 +            return rv;
   1.664 +        }
   1.665 +
   1.666 +        rv = ZW_WriteData(mStream, mComment.get(), mComment.Length());
   1.667 +        if (NS_FAILED(rv)) {
   1.668 +            Cleanup();
   1.669 +            return rv;
   1.670 +        }
   1.671 +
   1.672 +        nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
   1.673 +        rv = seekable->SetEOF();
   1.674 +        if (NS_FAILED(rv)) {
   1.675 +            Cleanup();
   1.676 +            return rv;
   1.677 +        }
   1.678 +
   1.679 +        // Go back and rewrite the file headers
   1.680 +        for (int32_t i = 0; i < mHeaders.Count(); i++) {
   1.681 +            nsZipHeader *header = mHeaders[i];
   1.682 +            if (!header->mWriteOnClose)
   1.683 +              continue;
   1.684 +
   1.685 +            rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, header->mOffset);
   1.686 +            if (NS_FAILED(rv)) {
   1.687 +               Cleanup();
   1.688 +               return rv;
   1.689 +            }
   1.690 +            rv = header->WriteFileHeader(mStream);
   1.691 +            if (NS_FAILED(rv)) {
   1.692 +               Cleanup();
   1.693 +               return rv;
   1.694 +            }
   1.695 +        }
   1.696 +    }
   1.697 +
   1.698 +    nsresult rv = mStream->Close();
   1.699 +    mStream = nullptr;
   1.700 +    mHeaders.Clear();
   1.701 +    mEntryHash.Clear();
   1.702 +    mQueue.Clear();
   1.703 +
   1.704 +    return rv;
   1.705 +}
   1.706 +
   1.707 +// Our nsIRequestObserver monitors removal operations performed on the queue
   1.708 +/* void onStartRequest (in nsIRequest aRequest, in nsISupports aContext); */
   1.709 +NS_IMETHODIMP nsZipWriter::OnStartRequest(nsIRequest *aRequest,
   1.710 +                                          nsISupports *aContext)
   1.711 +{
   1.712 +    return NS_OK;
   1.713 +}
   1.714 +
   1.715 +/* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext,
   1.716 + *                                             in nsresult aStatusCode); */
   1.717 +NS_IMETHODIMP nsZipWriter::OnStopRequest(nsIRequest *aRequest,
   1.718 +                                         nsISupports *aContext,
   1.719 +                                         nsresult aStatusCode)
   1.720 +{
   1.721 +    if (NS_FAILED(aStatusCode)) {
   1.722 +        FinishQueue(aStatusCode);
   1.723 +        Cleanup();
   1.724 +    }
   1.725 +
   1.726 +    nsresult rv = mStream->Flush();
   1.727 +    if (NS_FAILED(rv)) {
   1.728 +        FinishQueue(rv);
   1.729 +        Cleanup();
   1.730 +        return rv;
   1.731 +    }
   1.732 +    rv = SeekCDS();
   1.733 +    if (NS_FAILED(rv)) {
   1.734 +        FinishQueue(rv);
   1.735 +        return rv;
   1.736 +    }
   1.737 +
   1.738 +    BeginProcessingNextItem();
   1.739 +
   1.740 +    return NS_OK;
   1.741 +}
   1.742 +
   1.743 +/*
   1.744 + * Make all stored(uncompressed) files align to given alignment size.
   1.745 + */
   1.746 +NS_IMETHODIMP nsZipWriter::AlignStoredFiles(uint16_t aAlignSize)
   1.747 +{
   1.748 +    nsresult rv;
   1.749 +
   1.750 +    // Check for range and power of 2.
   1.751 +    if (aAlignSize < 2 || aAlignSize > 32768 ||
   1.752 +        (aAlignSize & (aAlignSize - 1)) != 0) {
   1.753 +        return NS_ERROR_INVALID_ARG;
   1.754 +    }
   1.755 +
   1.756 +    for (int i = 0; i < mHeaders.Count(); i++) {
   1.757 +        nsZipHeader *header = mHeaders[i];
   1.758 +
   1.759 +        // Check whether this entry is file and compression method is stored.
   1.760 +        bool isdir;
   1.761 +        rv = header->GetIsDirectory(&isdir);
   1.762 +        if (NS_FAILED(rv)) {
   1.763 +            return rv;
   1.764 +        }
   1.765 +        if (isdir || header->mMethod != 0) {
   1.766 +            continue;
   1.767 +        }
   1.768 +        // Pad extra field to align data starting position to specified size.
   1.769 +        uint32_t old_len = header->mLocalFieldLength;
   1.770 +        rv = header->PadExtraField(header->mOffset, aAlignSize);
   1.771 +        if (NS_FAILED(rv)) {
   1.772 +            continue;
   1.773 +        }
   1.774 +        // No padding means data already aligned.
   1.775 +        uint32_t shift = header->mLocalFieldLength - old_len;
   1.776 +        if (shift == 0) {
   1.777 +            continue;
   1.778 +        }
   1.779 +
   1.780 +        // Flush any remaining data before we start.
   1.781 +        rv = mStream->Flush();
   1.782 +        if (NS_FAILED(rv)) {
   1.783 +            return rv;
   1.784 +        }
   1.785 +
   1.786 +        // Open zip file for reading.
   1.787 +        nsCOMPtr<nsIInputStream> inputStream;
   1.788 +        rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), mFile);
   1.789 +        if (NS_FAILED(rv)) {
   1.790 +            return rv;
   1.791 +        }
   1.792 +
   1.793 +        nsCOMPtr<nsISeekableStream> in_seekable = do_QueryInterface(inputStream);
   1.794 +        nsCOMPtr<nsISeekableStream> out_seekable = do_QueryInterface(mStream);
   1.795 +
   1.796 +        uint32_t data_offset = header->mOffset + header->GetFileHeaderLength() - shift;
   1.797 +        uint32_t count = mCDSOffset - data_offset;
   1.798 +        uint32_t read;
   1.799 +        char buf[4096];
   1.800 +
   1.801 +        // Shift data to aligned postion.
   1.802 +        while (count > 0) {
   1.803 +            read = std::min(count, (uint32_t) sizeof(buf));
   1.804 +
   1.805 +            rv = in_seekable->Seek(nsISeekableStream::NS_SEEK_SET,
   1.806 +                                   data_offset + count - read);
   1.807 +            if (NS_FAILED(rv)) {
   1.808 +                break;
   1.809 +             }
   1.810 +
   1.811 +            rv = inputStream->Read(buf, read, &read);
   1.812 +            if (NS_FAILED(rv)) {
   1.813 +                break;
   1.814 +            }
   1.815 +
   1.816 +            rv = out_seekable->Seek(nsISeekableStream::NS_SEEK_SET,
   1.817 +                                    data_offset + count - read + shift);
   1.818 +            if (NS_FAILED(rv)) {
   1.819 +                break;
   1.820 +             }
   1.821 +
   1.822 +            rv = ZW_WriteData(mStream, buf, read);
   1.823 +            if (NS_FAILED(rv)) {
   1.824 +                break;
   1.825 +            }
   1.826 +
   1.827 +            count -= read;
   1.828 +        }
   1.829 +        inputStream->Close();
   1.830 +        if (NS_FAILED(rv)) {
   1.831 +            Cleanup();
   1.832 +            return rv;
   1.833 +        }
   1.834 +
   1.835 +        // Update current header
   1.836 +        rv = out_seekable->Seek(nsISeekableStream::NS_SEEK_SET,
   1.837 +                                header->mOffset);
   1.838 +        if (NS_FAILED(rv)) {
   1.839 +            Cleanup();
   1.840 +            return rv;
   1.841 +        }
   1.842 +        rv = header->WriteFileHeader(mStream);
   1.843 +        if (NS_FAILED(rv)) {
   1.844 +            Cleanup();
   1.845 +            return rv;
   1.846 +        }
   1.847 +
   1.848 +        // Update offset of all other headers
   1.849 +        int pos = i + 1;
   1.850 +        while (pos < mHeaders.Count()) {
   1.851 +            mHeaders[pos]->mOffset += shift;
   1.852 +            pos++;
   1.853 +        }
   1.854 +        mCDSOffset += shift;
   1.855 +        rv = SeekCDS();
   1.856 +        if (NS_FAILED(rv)) {
   1.857 +            return rv;
   1.858 +        }
   1.859 +        mCDSDirty = true;
   1.860 +    }
   1.861 +
   1.862 +    return NS_OK;
   1.863 +}
   1.864 +
   1.865 +nsresult nsZipWriter::InternalAddEntryDirectory(const nsACString & aZipEntry,
   1.866 +                                                PRTime aModTime,
   1.867 +                                                uint32_t aPermissions)
   1.868 +{
   1.869 +    nsRefPtr<nsZipHeader> header = new nsZipHeader();
   1.870 +    NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
   1.871 +
   1.872 +    uint32_t zipAttributes = ZIP_ATTRS(aPermissions, ZIP_ATTRS_DIRECTORY);
   1.873 +
   1.874 +    if (aZipEntry.Last() != '/') {
   1.875 +        nsCString dirPath;
   1.876 +        dirPath.Assign(aZipEntry + NS_LITERAL_CSTRING("/"));
   1.877 +        header->Init(dirPath, aModTime, zipAttributes, mCDSOffset);
   1.878 +    }
   1.879 +    else
   1.880 +        header->Init(aZipEntry, aModTime, zipAttributes, mCDSOffset);
   1.881 +
   1.882 +    if (mEntryHash.Get(header->mName, nullptr))
   1.883 +        return NS_ERROR_FILE_ALREADY_EXISTS;
   1.884 +
   1.885 +    nsresult rv = header->WriteFileHeader(mStream);
   1.886 +    if (NS_FAILED(rv)) {
   1.887 +        Cleanup();
   1.888 +        return rv;
   1.889 +    }
   1.890 +
   1.891 +    mCDSDirty = true;
   1.892 +    mCDSOffset += header->GetFileHeaderLength();
   1.893 +    mEntryHash.Put(header->mName, mHeaders.Count());
   1.894 +
   1.895 +    if (!mHeaders.AppendObject(header)) {
   1.896 +        Cleanup();
   1.897 +        return NS_ERROR_OUT_OF_MEMORY;
   1.898 +    }
   1.899 +
   1.900 +    return NS_OK;
   1.901 +}
   1.902 +
   1.903 +/*
   1.904 + * Recovering from an error while adding a new entry is simply a case of
   1.905 + * seeking back to the CDS. If we fail trying to do that though then cleanup
   1.906 + * and bail out.
   1.907 + */
   1.908 +nsresult nsZipWriter::SeekCDS()
   1.909 +{
   1.910 +    nsresult rv;
   1.911 +    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream, &rv);
   1.912 +    if (NS_FAILED(rv)) {
   1.913 +        Cleanup();
   1.914 +        return rv;
   1.915 +    }
   1.916 +    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mCDSOffset);
   1.917 +    if (NS_FAILED(rv))
   1.918 +        Cleanup();
   1.919 +    return rv;
   1.920 +}
   1.921 +
   1.922 +/*
   1.923 + * In a bad error condition this essentially closes down the component as best
   1.924 + * it can.
   1.925 + */
   1.926 +void nsZipWriter::Cleanup()
   1.927 +{
   1.928 +    mHeaders.Clear();
   1.929 +    mEntryHash.Clear();
   1.930 +    if (mStream)
   1.931 +        mStream->Close();
   1.932 +    mStream = nullptr;
   1.933 +    mFile = nullptr;
   1.934 +}
   1.935 +
   1.936 +/*
   1.937 + * Called when writing a file to the zip is complete.
   1.938 + */
   1.939 +nsresult nsZipWriter::EntryCompleteCallback(nsZipHeader* aHeader,
   1.940 +                                            nsresult aStatus)
   1.941 +{
   1.942 +    if (NS_SUCCEEDED(aStatus)) {
   1.943 +        mEntryHash.Put(aHeader->mName, mHeaders.Count());
   1.944 +        if (!mHeaders.AppendObject(aHeader)) {
   1.945 +            mEntryHash.Remove(aHeader->mName);
   1.946 +            SeekCDS();
   1.947 +            return NS_ERROR_OUT_OF_MEMORY;
   1.948 +        }
   1.949 +        mCDSDirty = true;
   1.950 +        mCDSOffset += aHeader->mCSize + aHeader->GetFileHeaderLength();
   1.951 +
   1.952 +        if (mInQueue)
   1.953 +            BeginProcessingNextItem();
   1.954 +
   1.955 +        return NS_OK;
   1.956 +    }
   1.957 +
   1.958 +    nsresult rv = SeekCDS();
   1.959 +    if (mInQueue)
   1.960 +        FinishQueue(aStatus);
   1.961 +    return rv;
   1.962 +}
   1.963 +
   1.964 +inline nsresult nsZipWriter::BeginProcessingAddition(nsZipQueueItem* aItem,
   1.965 +                                                     bool* complete)
   1.966 +{
   1.967 +    if (aItem->mFile) {
   1.968 +        bool exists;
   1.969 +        nsresult rv = aItem->mFile->Exists(&exists);
   1.970 +        NS_ENSURE_SUCCESS(rv, rv);
   1.971 +
   1.972 +        if (!exists) return NS_ERROR_FILE_NOT_FOUND;
   1.973 +
   1.974 +        bool isdir;
   1.975 +        rv = aItem->mFile->IsDirectory(&isdir);
   1.976 +        NS_ENSURE_SUCCESS(rv, rv);
   1.977 +
   1.978 +        rv = aItem->mFile->GetLastModifiedTime(&aItem->mModTime);
   1.979 +        NS_ENSURE_SUCCESS(rv, rv);
   1.980 +        aItem->mModTime *= PR_USEC_PER_MSEC;
   1.981 +
   1.982 +        rv = aItem->mFile->GetPermissions(&aItem->mPermissions);
   1.983 +        NS_ENSURE_SUCCESS(rv, rv);
   1.984 +
   1.985 +        if (!isdir) {
   1.986 +            // Set up for fall through to stream reader
   1.987 +            rv = NS_NewLocalFileInputStream(getter_AddRefs(aItem->mStream),
   1.988 +                                            aItem->mFile);
   1.989 +            NS_ENSURE_SUCCESS(rv, rv);
   1.990 +        }
   1.991 +        // If a dir then this will fall through to the plain dir addition
   1.992 +    }
   1.993 +
   1.994 +    uint32_t zipAttributes = ZIP_ATTRS(aItem->mPermissions, ZIP_ATTRS_FILE);
   1.995 +
   1.996 +    if (aItem->mStream || aItem->mChannel) {
   1.997 +        nsRefPtr<nsZipHeader> header = new nsZipHeader();
   1.998 +        NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY);
   1.999 +
  1.1000 +        header->Init(aItem->mZipEntry, aItem->mModTime, zipAttributes,
  1.1001 +                     mCDSOffset);
  1.1002 +        nsresult rv = header->WriteFileHeader(mStream);
  1.1003 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1004 +
  1.1005 +        nsRefPtr<nsZipDataStream> stream = new nsZipDataStream();
  1.1006 +        NS_ENSURE_TRUE(stream, NS_ERROR_OUT_OF_MEMORY);
  1.1007 +        rv = stream->Init(this, mStream, header, aItem->mCompression);
  1.1008 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1009 +
  1.1010 +        if (aItem->mStream) {
  1.1011 +            nsCOMPtr<nsIInputStreamPump> pump;
  1.1012 +            rv = NS_NewInputStreamPump(getter_AddRefs(pump), aItem->mStream,
  1.1013 +                                       -1, -1, 0, 0, true);
  1.1014 +            NS_ENSURE_SUCCESS(rv, rv);
  1.1015 +
  1.1016 +            rv = pump->AsyncRead(stream, nullptr);
  1.1017 +            NS_ENSURE_SUCCESS(rv, rv);
  1.1018 +        }
  1.1019 +        else {
  1.1020 +            rv = aItem->mChannel->AsyncOpen(stream, nullptr);
  1.1021 +            NS_ENSURE_SUCCESS(rv, rv);
  1.1022 +        }
  1.1023 +
  1.1024 +        return NS_OK;
  1.1025 +    }
  1.1026 +
  1.1027 +    // Must be plain directory addition
  1.1028 +    *complete = true;
  1.1029 +    return InternalAddEntryDirectory(aItem->mZipEntry, aItem->mModTime,
  1.1030 +                                     aItem->mPermissions);
  1.1031 +}
  1.1032 +
  1.1033 +inline nsresult nsZipWriter::BeginProcessingRemoval(int32_t aPos)
  1.1034 +{
  1.1035 +    // Open the zip file for reading
  1.1036 +    nsCOMPtr<nsIInputStream> inputStream;
  1.1037 +    nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
  1.1038 +                                             mFile);
  1.1039 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1040 +    nsCOMPtr<nsIInputStreamPump> pump;
  1.1041 +    rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream, -1, -1, 0,
  1.1042 +                               0, true);
  1.1043 +    if (NS_FAILED(rv)) {
  1.1044 +        inputStream->Close();
  1.1045 +        return rv;
  1.1046 +    }
  1.1047 +    nsCOMPtr<nsIStreamListener> listener;
  1.1048 +    rv = NS_NewSimpleStreamListener(getter_AddRefs(listener), mStream, this);
  1.1049 +    if (NS_FAILED(rv)) {
  1.1050 +        inputStream->Close();
  1.1051 +        return rv;
  1.1052 +    }
  1.1053 +
  1.1054 +    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream);
  1.1055 +    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
  1.1056 +                        mHeaders[aPos]->mOffset);
  1.1057 +    if (NS_FAILED(rv)) {
  1.1058 +        inputStream->Close();
  1.1059 +        return rv;
  1.1060 +    }
  1.1061 +
  1.1062 +    uint32_t shift = (mHeaders[aPos + 1]->mOffset -
  1.1063 +                      mHeaders[aPos]->mOffset);
  1.1064 +    mCDSOffset -= shift;
  1.1065 +    int32_t pos2 = aPos + 1;
  1.1066 +    while (pos2 < mHeaders.Count()) {
  1.1067 +        mEntryHash.Put(mHeaders[pos2]->mName, pos2 - 1);
  1.1068 +        mHeaders[pos2]->mOffset -= shift;
  1.1069 +        pos2++;
  1.1070 +    }
  1.1071 +
  1.1072 +    mEntryHash.Remove(mHeaders[aPos]->mName);
  1.1073 +    mHeaders.RemoveObjectAt(aPos);
  1.1074 +    mCDSDirty = true;
  1.1075 +
  1.1076 +    rv = pump->AsyncRead(listener, nullptr);
  1.1077 +    if (NS_FAILED(rv)) {
  1.1078 +        inputStream->Close();
  1.1079 +        Cleanup();
  1.1080 +        return rv;
  1.1081 +    }
  1.1082 +    return NS_OK;
  1.1083 +}
  1.1084 +
  1.1085 +/*
  1.1086 + * Starts processing on the next item in the queue.
  1.1087 + */
  1.1088 +void nsZipWriter::BeginProcessingNextItem()
  1.1089 +{
  1.1090 +    while (!mQueue.IsEmpty()) {
  1.1091 +
  1.1092 +        nsZipQueueItem next = mQueue[0];
  1.1093 +        mQueue.RemoveElementAt(0);
  1.1094 +
  1.1095 +        if (next.mOperation == OPERATION_REMOVE) {
  1.1096 +            int32_t pos = -1;
  1.1097 +            if (mEntryHash.Get(next.mZipEntry, &pos)) {
  1.1098 +                if (pos < mHeaders.Count() - 1) {
  1.1099 +                    nsresult rv = BeginProcessingRemoval(pos);
  1.1100 +                    if (NS_FAILED(rv)) FinishQueue(rv);
  1.1101 +                    return;
  1.1102 +                }
  1.1103 +
  1.1104 +                mCDSOffset = mHeaders[pos]->mOffset;
  1.1105 +                nsresult rv = SeekCDS();
  1.1106 +                if (NS_FAILED(rv)) {
  1.1107 +                    FinishQueue(rv);
  1.1108 +                    return;
  1.1109 +                }
  1.1110 +                mEntryHash.Remove(mHeaders[pos]->mName);
  1.1111 +                mHeaders.RemoveObjectAt(pos);
  1.1112 +            }
  1.1113 +            else {
  1.1114 +                FinishQueue(NS_ERROR_FILE_NOT_FOUND);
  1.1115 +                return;
  1.1116 +            }
  1.1117 +        }
  1.1118 +        else if (next.mOperation == OPERATION_ADD) {
  1.1119 +            if (mEntryHash.Get(next.mZipEntry, nullptr)) {
  1.1120 +                FinishQueue(NS_ERROR_FILE_ALREADY_EXISTS);
  1.1121 +                return;
  1.1122 +            }
  1.1123 +
  1.1124 +            bool complete = false;
  1.1125 +            nsresult rv = BeginProcessingAddition(&next, &complete);
  1.1126 +            if (NS_FAILED(rv)) {
  1.1127 +                SeekCDS();
  1.1128 +                FinishQueue(rv);
  1.1129 +                return;
  1.1130 +            }
  1.1131 +            if (!complete)
  1.1132 +                return;
  1.1133 +        }
  1.1134 +    }
  1.1135 +
  1.1136 +    FinishQueue(NS_OK);
  1.1137 +}
  1.1138 +
  1.1139 +/*
  1.1140 + * Ends processing with the given status.
  1.1141 + */
  1.1142 +void nsZipWriter::FinishQueue(nsresult aStatus)
  1.1143 +{
  1.1144 +    nsCOMPtr<nsIRequestObserver> observer = mProcessObserver;
  1.1145 +    nsCOMPtr<nsISupports> context = mProcessContext;
  1.1146 +    // Clean up everything first in case the observer decides to queue more
  1.1147 +    // things
  1.1148 +    mProcessObserver = nullptr;
  1.1149 +    mProcessContext = nullptr;
  1.1150 +    mInQueue = false;
  1.1151 +
  1.1152 +    if (observer)
  1.1153 +        observer->OnStopRequest(nullptr, context, aStatus);
  1.1154 +}

mercurial