1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ArchiveZipFile.h"
8 #include "ArchiveZipEvent.h"
10 #include "nsIInputStream.h"
12 #include "mozilla/Attributes.h"
14 USING_ARCHIVEREADER_NAMESPACE
16 #define ZIP_CHUNK 16384
19 * Input stream object for zip files
21 class ArchiveInputStream MOZ_FINAL
: public nsIInputStream
,
22 public nsISeekableStream
25 ArchiveInputStream(uint64_t aParentSize
,
26 nsIInputStream
* aInputStream
,
37 MOZ_COUNT_CTOR(ArchiveInputStream
);
40 memset(&mData
, 0, sizeof(mData
));
42 mData
.parentSize
= aParentSize
;
43 mData
.inputStream
= aInputStream
;
46 NS_DECL_THREADSAFE_ISUPPORTS
47 NS_DECL_NSIINPUTSTREAM
48 NS_DECL_NSISEEKABLESTREAM
51 virtual ~ArchiveInputStream()
53 MOZ_COUNT_DTOR(ArchiveInputStream
);
75 nsCOMPtr
<nsIInputStream
> inputStream
;
77 unsigned char input
[ZIP_CHUNK
];
78 uint32_t sizeToBeRead
;
81 bool compressed
; // a zip file can contain stored or compressed files
85 NS_IMPL_ISUPPORTS(ArchiveInputStream
,
90 ArchiveInputStream::Init()
94 memset(&mZs
, 0, sizeof(z_stream
));
95 int zerr
= inflateInit2(&mZs
, -MAX_WBITS
);
97 return NS_ERROR_OUT_OF_MEMORY
;
100 mData
.sizeToBeRead
= ArchiveZipItem::StrToInt32(mCentral
.size
);
102 uint32_t offset
= ArchiveZipItem::StrToInt32(mCentral
.localhdr_offset
);
104 // The file is corrupt
105 if (offset
+ ZIPLOCAL_SIZE
> mData
.parentSize
) {
106 return NS_ERROR_UNEXPECTED
;
109 // From the input stream to a seekable stream
110 nsCOMPtr
<nsISeekableStream
> seekableStream
;
111 seekableStream
= do_QueryInterface(mData
.inputStream
);
112 if (!seekableStream
) {
113 return NS_ERROR_UNEXPECTED
;
116 // Seek + read the ZipLocal struct
117 seekableStream
->Seek(nsISeekableStream::NS_SEEK_SET
, offset
);
118 uint8_t buffer
[ZIPLOCAL_SIZE
];
121 rv
= mData
.inputStream
->Read((char*)buffer
, ZIPLOCAL_SIZE
, &ret
);
122 if (NS_FAILED(rv
) || ret
!= ZIPLOCAL_SIZE
) {
123 return NS_ERROR_UNEXPECTED
;
127 if (ArchiveZipItem::StrToInt32(buffer
) != LOCALSIG
) {
128 return NS_ERROR_UNEXPECTED
;
132 memcpy(&local
, buffer
, ZIPLOCAL_SIZE
);
134 // Seek to the real data:
135 offset
+= ZIPLOCAL_SIZE
+
136 ArchiveZipItem::StrToInt16(local
.filename_len
) +
137 ArchiveZipItem::StrToInt16(local
.extrafield_len
);
139 // The file is corrupt if there is not enough data
140 if (offset
+ mData
.sizeToBeRead
> mData
.parentSize
) {
141 return NS_ERROR_UNEXPECTED
;
145 seekableStream
->Seek(nsISeekableStream::NS_SEEK_SET
, offset
);
147 // The file is compressed or not?
148 mData
.compressed
= (ArchiveZipItem::StrToInt16(mCentral
.method
) != 0);
150 // We have to skip the first mStart bytes:
152 rv
= Seek(NS_SEEK_SET
, mStart
);
153 NS_ENSURE_SUCCESS(rv
, rv
);
160 ArchiveInputStream::Close()
162 if (mStatus
!= NotStarted
) {
164 mStatus
= NotStarted
;
171 ArchiveInputStream::Available(uint64_t* _retval
)
173 *_retval
= mLength
- mData
.cursor
- mStart
;
178 ArchiveInputStream::Read(char* aBuffer
,
182 NS_ENSURE_ARG_POINTER(aBuffer
);
183 NS_ENSURE_ARG_POINTER(_retval
);
187 // This is the first time:
188 if (mStatus
== NotStarted
) {
196 // Let's set avail_out to -1 so we read something from the stream.
197 mZs
.avail_out
= (uInt
)-1;
200 // Nothing more can be read
201 if (mStatus
== Done
) {
207 if (!mData
.compressed
) {
208 rv
= mData
.inputStream
->Read(aBuffer
,
209 (mData
.sizeToBeRead
> aCount
?
210 aCount
: mData
.sizeToBeRead
),
212 if (NS_SUCCEEDED(rv
)) {
213 mData
.sizeToBeRead
-= *_retval
;
214 mData
.cursor
+= *_retval
;
216 if (mData
.sizeToBeRead
== 0) {
224 // We have nothing ready to be processed:
225 if (mZs
.avail_out
!= 0 && mData
.sizeToBeRead
!= 0) {
227 rv
= mData
.inputStream
->Read((char*)mData
.input
,
228 (mData
.sizeToBeRead
> sizeof(mData
.input
) ?
229 sizeof(mData
.input
) : mData
.sizeToBeRead
),
241 mData
.sizeToBeRead
-= ret
;
243 mZs
.next_in
= mData
.input
;
246 mZs
.avail_out
= aCount
;
247 mZs
.next_out
= (unsigned char*)aBuffer
;
249 int ret
= inflate(&mZs
, mData
.sizeToBeRead
? Z_NO_FLUSH
: Z_FINISH
);
250 if (ret
!= Z_BUF_ERROR
&& ret
!= Z_OK
&& ret
!= Z_STREAM_END
) {
251 return NS_ERROR_UNEXPECTED
;
254 if (ret
== Z_STREAM_END
) {
258 *_retval
= aCount
- mZs
.avail_out
;
259 mData
.cursor
+= *_retval
;
264 ArchiveInputStream::ReadSegments(nsWriteSegmentFun aWriter
,
269 // don't have a buffer to read from, so this better not be called!
270 NS_NOTREACHED("Consumers should be using Read()!");
271 return NS_ERROR_NOT_IMPLEMENTED
;
275 ArchiveInputStream::IsNonBlocking(bool* _retval
)
283 ArchiveInputStream::Seek(int32_t aWhence
, int64_t aOffset
)
285 int64_t pos
= aOffset
;
300 NS_NOTREACHED("unexpected whence value");
301 return NS_ERROR_UNEXPECTED
;
304 if (pos
== int64_t(mData
.cursor
)) {
308 if (pos
< 0 || pos
>= mLength
) {
309 return NS_ERROR_FAILURE
;
312 // We have to terminate the previous operation:
314 if (mStatus
!= NotStarted
) {
316 NS_ENSURE_SUCCESS(rv
, rv
);
322 // Note: This code is heavy but inflate does not have any seek() support:
326 rv
= Read(buffer
, pos
> int64_t(sizeof(buffer
)) ? sizeof(buffer
) : pos
, &ret
);
332 return NS_ERROR_UNEXPECTED
;
342 ArchiveInputStream::Tell(int64_t *aResult
)
344 *aResult
= mData
.cursor
;
349 ArchiveInputStream::SetEOF()
351 return NS_ERROR_NOT_IMPLEMENTED
;
354 // ArchiveZipFileImpl
357 ArchiveZipFileImpl::GetInternalStream(nsIInputStream
** aStream
)
359 if (mLength
> INT32_MAX
) {
360 return NS_ERROR_FAILURE
;
364 nsresult rv
= mArchiveReader
->GetSize(&size
);
365 NS_ENSURE_SUCCESS(rv
, rv
);
367 nsCOMPtr
<nsIInputStream
> inputStream
;
368 rv
= mArchiveReader
->GetInputStream(getter_AddRefs(inputStream
));
369 if (NS_FAILED(rv
) || !inputStream
) {
370 return NS_ERROR_UNEXPECTED
;
373 nsRefPtr
<ArchiveInputStream
> stream
= new ArchiveInputStream(size
,
386 ArchiveZipFileImpl::Unlink()
388 ArchiveZipFileImpl
* tmp
= this;
389 NS_IMPL_CYCLE_COLLECTION_UNLINK(mArchiveReader
);
393 ArchiveZipFileImpl::Traverse(nsCycleCollectionTraversalCallback
&cb
)
395 ArchiveZipFileImpl
* tmp
= this;
396 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArchiveReader
);
399 already_AddRefed
<nsIDOMBlob
>
400 ArchiveZipFileImpl::CreateSlice(uint64_t aStart
,
402 const nsAString
& aContentType
)
404 nsCOMPtr
<nsIDOMBlob
> t
=
405 new DOMFile(new ArchiveZipFileImpl(mFilename
, mContentType
,
406 aStart
, mLength
, mCentral
,
411 NS_IMPL_ISUPPORTS_INHERITED0(ArchiveZipFileImpl
, DOMFileImpl
)