1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is Zip Writer Component.
16 * The Initial Developer of the Original Code is
17 * Dave Townsend <dtownsend@oxymoronical.com>.
19 * Portions created by the Initial Developer are Copyright (C) 2007
20 * the Initial Developer. All Rights Reserved.
23 * Mook <mook.moz+random.code@gmail.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK *****
40 #include "StreamFunctions.h"
41 #include "nsZipWriter.h"
42 #include "nsZipDataStream.h"
43 #include "nsISeekableStream.h"
44 #include "nsIAsyncStreamCopier.h"
45 #include "nsIStreamListener.h"
46 #include "nsIInputStreamPump.h"
47 #include "nsComponentManagerUtils.h"
49 #include "nsNetError.h"
50 #include "nsStreamUtils.h"
51 #include "nsThreadUtils.h"
52 #include "nsNetUtil.h"
55 #define ZIP_EOCDR_HEADER_SIZE 22
56 #define ZIP_EOCDR_HEADER_SIGNATURE 0x06054b50
59 * nsZipWriter is used to create and add to zip files.
60 * It is based on the spec available at
61 * http://www.pkware.com/documents/casestudies/APPNOTE.TXT.
63 * The basic structure of a zip file created is slightly simpler than that
64 * illustrated in the spec because certain features of the zip format are
67 * [local file header 1]
72 * [local file header n]
75 * [end of central directory record]
77 NS_IMPL_ISUPPORTS2(nsZipWriter
, nsIZipWriter
,
80 nsZipWriter::nsZipWriter()
86 nsZipWriter::~nsZipWriter()
88 if (mStream
&& !mInQueue
)
92 /* attribute AString comment; */
93 NS_IMETHODIMP
nsZipWriter::GetComment(nsACString
& aComment
)
96 return NS_ERROR_NOT_INITIALIZED
;
102 NS_IMETHODIMP
nsZipWriter::SetComment(const nsACString
& aComment
)
105 return NS_ERROR_NOT_INITIALIZED
;
112 /* readonly attribute boolean inQueue; */
113 NS_IMETHODIMP
nsZipWriter::GetInQueue(PRBool
*aInQueue
)
115 *aInQueue
= mInQueue
;
119 /* readonly attribute nsIFile file; */
120 NS_IMETHODIMP
nsZipWriter::GetFile(nsIFile
**aFile
)
123 return NS_ERROR_NOT_INITIALIZED
;
125 nsCOMPtr
<nsIFile
> file
;
126 nsresult rv
= mFile
->Clone(getter_AddRefs(file
));
127 NS_ENSURE_SUCCESS(rv
, rv
);
129 NS_ADDREF(*aFile
= file
);
134 * Reads file entries out of an existing zip file.
136 nsresult
nsZipWriter::ReadFile(nsIFile
*aFile
)
139 nsresult rv
= aFile
->GetFileSize(&size
);
140 NS_ENSURE_SUCCESS(rv
, rv
);
142 nsCOMPtr
<nsIInputStream
> inputStream
;
143 rv
= NS_NewLocalFileInputStream(getter_AddRefs(inputStream
), aFile
);
144 NS_ENSURE_SUCCESS(rv
, rv
);
147 PRInt64 seek
= size
- 1024;
148 PRUint32 length
= 1024;
157 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(inputStream
);
160 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, seek
);
162 inputStream
->Close();
165 rv
= ZW_ReadData(inputStream
, buf
, length
);
167 inputStream
->Close();
172 * We have to backtrack from the end of the file until we find the
175 // We know it's at least this far from the end
176 pos
= length
- ZIP_EOCDR_HEADER_SIZE
;
177 sig
= READ32(buf
, &pos
);
180 if (sig
== ZIP_EOCDR_HEADER_SIGNATURE
) {
181 // Skip down to entry count
183 PRUint32 entries
= READ16(buf
, &pos
);
184 // Skip past CDS size
186 mCDSOffset
= READ32(buf
, &pos
);
187 PRUint32 commentlen
= READ16(buf
, &pos
);
191 else if (pos
+ commentlen
<= length
)
192 mComment
.Assign(buf
+ pos
, commentlen
);
194 if ((seek
+ pos
+ commentlen
) > size
) {
195 inputStream
->Close();
196 return NS_ERROR_FILE_CORRUPTED
;
198 nsAutoArrayPtr
<char> field(new char[commentlen
]);
199 NS_ENSURE_TRUE(field
, NS_ERROR_OUT_OF_MEMORY
);
200 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
,
203 inputStream
->Close();
206 rv
= ZW_ReadData(inputStream
, field
.get(), length
);
208 inputStream
->Close();
211 mComment
.Assign(field
.get(), commentlen
);
214 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
,
217 inputStream
->Close();
221 for (PRUint32 entry
= 0; entry
< entries
; entry
++) {
222 nsZipHeader
* header
= new nsZipHeader();
224 inputStream
->Close();
227 return NS_ERROR_OUT_OF_MEMORY
;
229 rv
= header
->ReadCDSHeader(inputStream
);
231 inputStream
->Close();
236 if (!mEntryHash
.Put(header
->mName
, mHeaders
.Count()))
237 return NS_ERROR_OUT_OF_MEMORY
;
238 if (!mHeaders
.AppendObject(header
))
239 return NS_ERROR_OUT_OF_MEMORY
;
242 return inputStream
->Close();
249 // We've reached the start with no signature found. Corrupt.
250 inputStream
->Close();
251 return NS_ERROR_FILE_CORRUPTED
;
254 // Overlap by the size of the end of cdr
255 seek
-= (1024 - ZIP_EOCDR_HEADER_SIZE
);
261 // Will never reach here in reality
262 NS_NOTREACHED("Loop should never complete");
263 return NS_ERROR_UNEXPECTED
;
266 /* void open (in nsIFile aFile, in PRInt32 aIoFlags); */
267 NS_IMETHODIMP
nsZipWriter::Open(nsIFile
*aFile
, PRInt32 aIoFlags
)
270 return NS_ERROR_ALREADY_INITIALIZED
;
272 NS_ENSURE_ARG_POINTER(aFile
);
274 // Need to be able to write to the file
275 if (aIoFlags
& PR_RDONLY
)
276 return NS_ERROR_FAILURE
;
278 nsresult rv
= aFile
->Clone(getter_AddRefs(mFile
));
279 NS_ENSURE_SUCCESS(rv
, rv
);
282 rv
= mFile
->Exists(&exists
);
283 NS_ENSURE_SUCCESS(rv
, rv
);
284 if (!exists
&& !(aIoFlags
& PR_CREATE_FILE
))
285 return NS_ERROR_FILE_NOT_FOUND
;
287 if (exists
&& !(aIoFlags
& (PR_TRUNCATE
| PR_WRONLY
))) {
288 rv
= ReadFile(mFile
);
289 NS_ENSURE_SUCCESS(rv
, rv
);
290 mCDSDirty
= PR_FALSE
;
298 // Silently drop PR_APPEND
301 nsCOMPtr
<nsIOutputStream
> stream
;
302 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(stream
), mFile
, aIoFlags
);
309 rv
= NS_NewBufferedOutputStream(getter_AddRefs(mStream
), stream
, 0x800);
317 if (mCDSOffset
> 0) {
319 NS_ENSURE_SUCCESS(rv
, rv
);
325 /* nsIZipEntry getEntry (in AString aZipEntry); */
326 NS_IMETHODIMP
nsZipWriter::GetEntry(const nsACString
& aZipEntry
,
327 nsIZipEntry
**_retval
)
330 if (mEntryHash
.Get(aZipEntry
, &pos
))
331 NS_ADDREF(*_retval
= mHeaders
[pos
]);
338 /* boolean hasEntry (in AString aZipEntry); */
339 NS_IMETHODIMP
nsZipWriter::HasEntry(const nsACString
& aZipEntry
,
342 *_retval
= mEntryHash
.Get(aZipEntry
, nsnull
);
347 /* void addEntryDirectory (in AUTF8String aZipEntry, in PRTime aModTime,
348 * in boolean aQueue); */
349 NS_IMETHODIMP
nsZipWriter::AddEntryDirectory(const nsACString
& aZipEntry
,
350 PRTime aModTime
, PRBool aQueue
)
353 return NS_ERROR_NOT_INITIALIZED
;
357 item
.mOperation
= OPERATION_ADD
;
358 item
.mZipEntry
= aZipEntry
;
359 item
.mModTime
= aModTime
;
360 item
.mPermissions
= PERMISSIONS_DIR
;
361 if (!mQueue
.AppendElement(item
))
362 return NS_ERROR_OUT_OF_MEMORY
;
367 return NS_ERROR_IN_PROGRESS
;
368 return InternalAddEntryDirectory(aZipEntry
, aModTime
, PERMISSIONS_DIR
);
371 /* void addEntryFile (in AUTF8String aZipEntry, in PRInt32 aCompression,
372 * in nsIFile aFile, in boolean aQueue); */
373 NS_IMETHODIMP
nsZipWriter::AddEntryFile(const nsACString
& aZipEntry
,
374 PRInt32 aCompression
, nsIFile
*aFile
,
377 NS_ENSURE_ARG_POINTER(aFile
);
379 return NS_ERROR_NOT_INITIALIZED
;
384 item
.mOperation
= OPERATION_ADD
;
385 item
.mZipEntry
= aZipEntry
;
386 item
.mCompression
= aCompression
;
387 rv
= aFile
->Clone(getter_AddRefs(item
.mFile
));
388 NS_ENSURE_SUCCESS(rv
, rv
);
389 if (!mQueue
.AppendElement(item
))
390 return NS_ERROR_OUT_OF_MEMORY
;
395 return NS_ERROR_IN_PROGRESS
;
398 rv
= aFile
->Exists(&exists
);
399 NS_ENSURE_SUCCESS(rv
, rv
);
401 return NS_ERROR_FILE_NOT_FOUND
;
404 rv
= aFile
->IsDirectory(&isdir
);
405 NS_ENSURE_SUCCESS(rv
, rv
);
408 rv
= aFile
->GetLastModifiedTime(&modtime
);
409 NS_ENSURE_SUCCESS(rv
, rv
);
410 modtime
*= PR_USEC_PER_MSEC
;
412 PRUint32 permissions
;
413 rv
= aFile
->GetPermissions(&permissions
);
414 NS_ENSURE_SUCCESS(rv
, rv
);
417 return InternalAddEntryDirectory(aZipEntry
, modtime
, permissions
);
419 if (mEntryHash
.Get(aZipEntry
, nsnull
))
420 return NS_ERROR_FILE_ALREADY_EXISTS
;
422 nsCOMPtr
<nsIInputStream
> inputStream
;
423 rv
= NS_NewLocalFileInputStream(getter_AddRefs(inputStream
),
425 NS_ENSURE_SUCCESS(rv
, rv
);
427 rv
= AddEntryStream(aZipEntry
, modtime
, aCompression
, inputStream
,
428 PR_FALSE
, permissions
);
429 NS_ENSURE_SUCCESS(rv
, rv
);
431 return inputStream
->Close();
434 /* void addEntryChannel (in AUTF8String aZipEntry, in PRTime aModTime,
435 * in PRInt32 aCompression, in nsIChannel aChannel,
436 * in boolean aQueue); */
437 NS_IMETHODIMP
nsZipWriter::AddEntryChannel(const nsACString
& aZipEntry
,
439 PRInt32 aCompression
,
440 nsIChannel
*aChannel
,
443 NS_ENSURE_ARG_POINTER(aChannel
);
445 return NS_ERROR_NOT_INITIALIZED
;
449 item
.mOperation
= OPERATION_ADD
;
450 item
.mZipEntry
= aZipEntry
;
451 item
.mModTime
= aModTime
;
452 item
.mCompression
= aCompression
;
453 item
.mPermissions
= PERMISSIONS_FILE
;
454 item
.mChannel
= aChannel
;
455 if (!mQueue
.AppendElement(item
))
456 return NS_ERROR_OUT_OF_MEMORY
;
461 return NS_ERROR_IN_PROGRESS
;
462 if (mEntryHash
.Get(aZipEntry
, nsnull
))
463 return NS_ERROR_FILE_ALREADY_EXISTS
;
465 nsCOMPtr
<nsIInputStream
> inputStream
;
466 nsresult rv
= aChannel
->Open(getter_AddRefs(inputStream
));
467 NS_ENSURE_SUCCESS(rv
, rv
);
469 rv
= AddEntryStream(aZipEntry
, aModTime
, aCompression
, inputStream
,
470 PR_FALSE
, PERMISSIONS_FILE
);
471 NS_ENSURE_SUCCESS(rv
, rv
);
473 return inputStream
->Close();
476 /* void addEntryStream (in AUTF8String aZipEntry, in PRTime aModTime,
477 * in PRInt32 aCompression, in nsIInputStream aStream,
478 * in boolean aQueue); */
479 NS_IMETHODIMP
nsZipWriter::AddEntryStream(const nsACString
& aZipEntry
,
481 PRInt32 aCompression
,
482 nsIInputStream
*aStream
,
485 return AddEntryStream(aZipEntry
, aModTime
, aCompression
, aStream
, aQueue
,
489 /* void addEntryStream (in AUTF8String aZipEntry, in PRTime aModTime,
490 * in PRInt32 aCompression, in nsIInputStream aStream,
491 * in boolean aQueue, in unsigned long aPermissions); */
492 nsresult
nsZipWriter::AddEntryStream(const nsACString
& aZipEntry
,
494 PRInt32 aCompression
,
495 nsIInputStream
*aStream
,
497 PRUint32 aPermissions
)
499 NS_ENSURE_ARG_POINTER(aStream
);
501 return NS_ERROR_NOT_INITIALIZED
;
505 item
.mOperation
= OPERATION_ADD
;
506 item
.mZipEntry
= aZipEntry
;
507 item
.mModTime
= aModTime
;
508 item
.mCompression
= aCompression
;
509 item
.mPermissions
= aPermissions
;
510 item
.mStream
= aStream
;
511 if (!mQueue
.AppendElement(item
))
512 return NS_ERROR_OUT_OF_MEMORY
;
517 return NS_ERROR_IN_PROGRESS
;
518 if (mEntryHash
.Get(aZipEntry
, nsnull
))
519 return NS_ERROR_FILE_ALREADY_EXISTS
;
521 nsRefPtr
<nsZipHeader
> header
= new nsZipHeader();
522 NS_ENSURE_TRUE(header
, NS_ERROR_OUT_OF_MEMORY
);
523 header
->Init(aZipEntry
, aModTime
, ZIP_ATTRS(aPermissions
, ZIP_ATTRS_FILE
),
525 nsresult rv
= header
->WriteFileHeader(mStream
);
531 nsRefPtr
<nsZipDataStream
> stream
= new nsZipDataStream();
534 return NS_ERROR_OUT_OF_MEMORY
;
536 rv
= stream
->Init(this, mStream
, header
, aCompression
);
542 rv
= stream
->ReadStream(aStream
);
548 /* void removeEntry (in AUTF8String aZipEntry, in boolean aQueue); */
549 NS_IMETHODIMP
nsZipWriter::RemoveEntry(const nsACString
& aZipEntry
,
553 return NS_ERROR_NOT_INITIALIZED
;
557 item
.mOperation
= OPERATION_REMOVE
;
558 item
.mZipEntry
= aZipEntry
;
559 if (!mQueue
.AppendElement(item
))
560 return NS_ERROR_OUT_OF_MEMORY
;
565 return NS_ERROR_IN_PROGRESS
;
568 if (mEntryHash
.Get(aZipEntry
, &pos
)) {
569 // Flush any remaining data before we seek.
570 nsresult rv
= mStream
->Flush();
571 NS_ENSURE_SUCCESS(rv
, rv
);
572 if (pos
< mHeaders
.Count() - 1) {
573 // This is not the last entry, pull back the data.
574 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(mStream
);
575 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
,
576 mHeaders
[pos
]->mOffset
);
577 NS_ENSURE_SUCCESS(rv
, rv
);
579 nsCOMPtr
<nsIInputStream
> inputStream
;
580 rv
= NS_NewLocalFileInputStream(getter_AddRefs(inputStream
),
582 NS_ENSURE_SUCCESS(rv
, rv
);
583 seekable
= do_QueryInterface(inputStream
);
584 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
,
585 mHeaders
[pos
+ 1]->mOffset
);
587 inputStream
->Close();
591 PRUint32 count
= mCDSOffset
- mHeaders
[pos
+ 1]->mOffset
;
595 if (count
< sizeof(buf
))
600 rv
= inputStream
->Read(buf
, read
, &read
);
602 inputStream
->Close();
607 rv
= ZW_WriteData(mStream
, buf
, read
);
609 inputStream
->Close();
616 inputStream
->Close();
618 // Rewrite header offsets and update hash
619 PRUint32 shift
= (mHeaders
[pos
+ 1]->mOffset
-
620 mHeaders
[pos
]->mOffset
);
622 PRInt32 pos2
= pos
+ 1;
623 while (pos2
< mHeaders
.Count()) {
624 if (!mEntryHash
.Put(mHeaders
[pos2
]->mName
, pos2
-1)) {
626 return NS_ERROR_OUT_OF_MEMORY
;
628 mHeaders
[pos2
]->mOffset
-= shift
;
633 // Remove the last entry is just a case of moving the CDS
634 mCDSOffset
= mHeaders
[pos
]->mOffset
;
636 NS_ENSURE_SUCCESS(rv
, rv
);
639 mEntryHash
.Remove(mHeaders
[pos
]->mName
);
640 mHeaders
.RemoveObjectAt(pos
);
646 return NS_ERROR_FILE_NOT_FOUND
;
649 /* void processQueue (in nsIRequestObserver aObserver,
650 * in nsISupports aContext); */
651 NS_IMETHODIMP
nsZipWriter::ProcessQueue(nsIRequestObserver
*aObserver
,
652 nsISupports
*aContext
)
655 return NS_ERROR_NOT_INITIALIZED
;
657 return NS_ERROR_IN_PROGRESS
;
659 mProcessObserver
= aObserver
;
660 mProcessContext
= aContext
;
663 if (mProcessObserver
)
664 mProcessObserver
->OnStartRequest(nsnull
, mProcessContext
);
666 BeginProcessingNextItem();
672 NS_IMETHODIMP
nsZipWriter::Close()
675 return NS_ERROR_NOT_INITIALIZED
;
677 return NS_ERROR_IN_PROGRESS
;
681 for (PRInt32 i
= 0; i
< mHeaders
.Count(); i
++) {
682 nsresult rv
= mHeaders
[i
]->WriteCDSHeader(mStream
);
687 size
+= mHeaders
[i
]->GetCDSHeaderLength();
690 char buf
[ZIP_EOCDR_HEADER_SIZE
];
692 WRITE32(buf
, &pos
, ZIP_EOCDR_HEADER_SIGNATURE
);
693 WRITE16(buf
, &pos
, 0);
694 WRITE16(buf
, &pos
, 0);
695 WRITE16(buf
, &pos
, mHeaders
.Count());
696 WRITE16(buf
, &pos
, mHeaders
.Count());
697 WRITE32(buf
, &pos
, size
);
698 WRITE32(buf
, &pos
, mCDSOffset
);
699 WRITE16(buf
, &pos
, mComment
.Length());
701 nsresult rv
= ZW_WriteData(mStream
, buf
, pos
);
707 rv
= ZW_WriteData(mStream
, mComment
.get(), mComment
.Length());
713 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(mStream
);
714 rv
= seekable
->SetEOF();
721 nsresult rv
= mStream
->Close();
730 // Our nsIRequestObserver monitors removal operations performed on the queue
731 /* void onStartRequest (in nsIRequest aRequest, in nsISupports aContext); */
732 NS_IMETHODIMP
nsZipWriter::OnStartRequest(nsIRequest
*aRequest
,
733 nsISupports
*aContext
)
738 /* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext,
739 * in nsresult aStatusCode); */
740 NS_IMETHODIMP
nsZipWriter::OnStopRequest(nsIRequest
*aRequest
,
741 nsISupports
*aContext
,
742 nsresult aStatusCode
)
744 if (NS_FAILED(aStatusCode
)) {
745 FinishQueue(aStatusCode
);
749 nsresult rv
= mStream
->Flush();
761 BeginProcessingNextItem();
766 nsresult
nsZipWriter::InternalAddEntryDirectory(const nsACString
& aZipEntry
,
768 PRUint32 aPermissions
)
770 nsRefPtr
<nsZipHeader
> header
= new nsZipHeader();
771 NS_ENSURE_TRUE(header
, NS_ERROR_OUT_OF_MEMORY
);
773 PRUint32 zipAttributes
= ZIP_ATTRS(aPermissions
, ZIP_ATTRS_DIRECTORY
);
775 if (aZipEntry
.Last() != '/') {
777 dirPath
.Assign(aZipEntry
+ NS_LITERAL_CSTRING("/"));
778 header
->Init(dirPath
, aModTime
, zipAttributes
, mCDSOffset
);
781 header
->Init(aZipEntry
, aModTime
, zipAttributes
, mCDSOffset
);
783 if (mEntryHash
.Get(header
->mName
, nsnull
))
784 return NS_ERROR_FILE_ALREADY_EXISTS
;
786 nsresult rv
= header
->WriteFileHeader(mStream
);
793 mCDSOffset
+= header
->GetFileHeaderLength();
794 if (!mEntryHash
.Put(header
->mName
, mHeaders
.Count())) {
796 return NS_ERROR_OUT_OF_MEMORY
;
798 if (!mHeaders
.AppendObject(header
)) {
800 return NS_ERROR_OUT_OF_MEMORY
;
807 * Recovering from an error while adding a new entry is simply a case of
808 * seeking back to the CDS. If we fail trying to do that though then cleanup
811 nsresult
nsZipWriter::SeekCDS()
814 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(mStream
, &rv
);
819 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, mCDSOffset
);
826 * In a bad error condition this essentially closes down the component as best
829 void nsZipWriter::Cleanup()
840 * Called when writing a file to the zip is complete.
842 nsresult
nsZipWriter::EntryCompleteCallback(nsZipHeader
* aHeader
,
845 if (NS_SUCCEEDED(aStatus
)) {
846 if (!mEntryHash
.Put(aHeader
->mName
, mHeaders
.Count())) {
848 return NS_ERROR_OUT_OF_MEMORY
;
850 if (!mHeaders
.AppendObject(aHeader
)) {
851 mEntryHash
.Remove(aHeader
->mName
);
853 return NS_ERROR_OUT_OF_MEMORY
;
856 mCDSOffset
+= aHeader
->mCSize
+ aHeader
->GetFileHeaderLength();
859 BeginProcessingNextItem();
864 nsresult rv
= SeekCDS();
866 FinishQueue(aStatus
);
870 inline nsresult
nsZipWriter::BeginProcessingAddition(nsZipQueueItem
* aItem
,
875 nsresult rv
= aItem
->mFile
->Exists(&exists
);
876 NS_ENSURE_SUCCESS(rv
, rv
);
878 if (!exists
) return NS_ERROR_FILE_NOT_FOUND
;
881 rv
= aItem
->mFile
->IsDirectory(&isdir
);
882 NS_ENSURE_SUCCESS(rv
, rv
);
884 rv
= aItem
->mFile
->GetLastModifiedTime(&aItem
->mModTime
);
885 NS_ENSURE_SUCCESS(rv
, rv
);
886 aItem
->mModTime
*= PR_USEC_PER_MSEC
;
888 rv
= aItem
->mFile
->GetPermissions(&aItem
->mPermissions
);
889 NS_ENSURE_SUCCESS(rv
, rv
);
892 // Set up for fall through to stream reader
893 rv
= NS_NewLocalFileInputStream(getter_AddRefs(aItem
->mStream
),
895 NS_ENSURE_SUCCESS(rv
, rv
);
897 // If a dir then this will fall through to the plain dir addition
900 PRUint32 zipAttributes
= ZIP_ATTRS(aItem
->mPermissions
, ZIP_ATTRS_FILE
);
902 if (aItem
->mStream
) {
903 nsRefPtr
<nsZipHeader
> header
= new nsZipHeader();
904 NS_ENSURE_TRUE(header
, NS_ERROR_OUT_OF_MEMORY
);
906 header
->Init(aItem
->mZipEntry
, aItem
->mModTime
, zipAttributes
,
908 nsresult rv
= header
->WriteFileHeader(mStream
);
909 NS_ENSURE_SUCCESS(rv
, rv
);
911 nsRefPtr
<nsZipDataStream
> stream
= new nsZipDataStream();
912 rv
= stream
->Init(this, mStream
, header
, aItem
->mCompression
);
913 NS_ENSURE_SUCCESS(rv
, rv
);
915 nsCOMPtr
<nsIInputStreamPump
> pump
;
916 rv
= NS_NewInputStreamPump(getter_AddRefs(pump
), aItem
->mStream
, -1,
918 NS_ENSURE_SUCCESS(rv
, rv
);
920 rv
= pump
->AsyncRead(stream
, nsnull
);
921 NS_ENSURE_SUCCESS(rv
, rv
);
926 if (aItem
->mChannel
) {
927 nsRefPtr
<nsZipHeader
> header
= new nsZipHeader();
928 NS_ENSURE_TRUE(header
, NS_ERROR_OUT_OF_MEMORY
);
930 header
->Init(aItem
->mZipEntry
, aItem
->mModTime
, zipAttributes
,
933 nsRefPtr
<nsZipDataStream
> stream
= new nsZipDataStream();
934 NS_ENSURE_TRUE(stream
, NS_ERROR_OUT_OF_MEMORY
);
935 nsresult rv
= stream
->Init(this, mStream
, header
, aItem
->mCompression
);
936 NS_ENSURE_SUCCESS(rv
, rv
);
937 rv
= aItem
->mChannel
->AsyncOpen(stream
, nsnull
);
938 NS_ENSURE_SUCCESS(rv
, rv
);
943 // Must be plain directory addition
945 return InternalAddEntryDirectory(aItem
->mZipEntry
, aItem
->mModTime
,
946 aItem
->mPermissions
);
949 inline nsresult
nsZipWriter::BeginProcessingRemoval(PRInt32 aPos
)
951 // Open the zip file for reading
952 nsCOMPtr
<nsIInputStream
> inputStream
;
953 nsresult rv
= NS_NewLocalFileInputStream(getter_AddRefs(inputStream
),
955 NS_ENSURE_SUCCESS(rv
, rv
);
956 nsCOMPtr
<nsIInputStreamPump
> pump
;
957 rv
= NS_NewInputStreamPump(getter_AddRefs(pump
), inputStream
, -1, -1, 0,
960 inputStream
->Close();
963 nsCOMPtr
<nsIStreamListener
> listener
;
964 rv
= NS_NewSimpleStreamListener(getter_AddRefs(listener
), mStream
, this);
966 inputStream
->Close();
970 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(mStream
);
971 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
,
972 mHeaders
[aPos
]->mOffset
);
974 inputStream
->Close();
978 PRUint32 shift
= (mHeaders
[aPos
+ 1]->mOffset
-
979 mHeaders
[aPos
]->mOffset
);
981 PRInt32 pos2
= aPos
+ 1;
982 while (pos2
< mHeaders
.Count()) {
983 mEntryHash
.Put(mHeaders
[pos2
]->mName
, pos2
- 1);
984 mHeaders
[pos2
]->mOffset
-= shift
;
988 mEntryHash
.Remove(mHeaders
[aPos
]->mName
);
989 mHeaders
.RemoveObjectAt(aPos
);
992 rv
= pump
->AsyncRead(listener
, nsnull
);
994 inputStream
->Close();
1002 * Starts processing on the next item in the queue.
1004 void nsZipWriter::BeginProcessingNextItem()
1006 while (!mQueue
.IsEmpty()) {
1008 nsZipQueueItem next
= mQueue
[0];
1009 mQueue
.RemoveElementAt(0);
1011 if (next
.mOperation
== OPERATION_REMOVE
) {
1013 if (mEntryHash
.Get(next
.mZipEntry
, &pos
)) {
1014 if (pos
< mHeaders
.Count() - 1) {
1015 nsresult rv
= BeginProcessingRemoval(pos
);
1016 if (NS_FAILED(rv
)) FinishQueue(rv
);
1020 mCDSOffset
= mHeaders
[pos
]->mOffset
;
1021 nsresult rv
= SeekCDS();
1022 if (NS_FAILED(rv
)) {
1026 mEntryHash
.Remove(mHeaders
[pos
]->mName
);
1027 mHeaders
.RemoveObjectAt(pos
);
1030 FinishQueue(NS_ERROR_FILE_NOT_FOUND
);
1034 else if (next
.mOperation
== OPERATION_ADD
) {
1035 if (mEntryHash
.Get(next
.mZipEntry
, nsnull
)) {
1036 FinishQueue(NS_ERROR_FILE_ALREADY_EXISTS
);
1040 PRBool complete
= PR_FALSE
;
1041 nsresult rv
= BeginProcessingAddition(&next
, &complete
);
1042 if (NS_FAILED(rv
)) {
1056 * Ends processing with the given status.
1058 void nsZipWriter::FinishQueue(nsresult aStatus
)
1060 nsCOMPtr
<nsIRequestObserver
> observer
= mProcessObserver
;
1061 nsCOMPtr
<nsISupports
> context
= mProcessContext
;
1062 // Clean up everything first in case the observer decides to queue more
1064 mProcessObserver
= nsnull
;
1065 mProcessContext
= nsnull
;
1066 mInQueue
= PR_FALSE
;
1069 observer
->OnStopRequest(nsnull
, context
, aStatus
);