|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 */ |
|
5 |
|
6 #include "StreamFunctions.h" |
|
7 #include "nsZipDataStream.h" |
|
8 #include "nsIStringStream.h" |
|
9 #include "nsISeekableStream.h" |
|
10 #include "nsDeflateConverter.h" |
|
11 #include "nsNetUtil.h" |
|
12 #include "nsComponentManagerUtils.h" |
|
13 #include "nsMemory.h" |
|
14 |
|
15 #define ZIP_METHOD_STORE 0 |
|
16 #define ZIP_METHOD_DEFLATE 8 |
|
17 |
|
18 /** |
|
19 * nsZipDataStream handles the writing an entry's into the zip file. |
|
20 * It is set up to wither write the data as is, or in the event that compression |
|
21 * has been requested to pass it through a stream converter. |
|
22 * Currently only the deflate compression method is supported. |
|
23 * The CRC checksum for the entry's data is also generated here. |
|
24 */ |
|
25 NS_IMPL_ISUPPORTS(nsZipDataStream, nsIStreamListener, |
|
26 nsIRequestObserver) |
|
27 |
|
28 nsresult nsZipDataStream::Init(nsZipWriter *aWriter, |
|
29 nsIOutputStream *aStream, |
|
30 nsZipHeader *aHeader, |
|
31 int32_t aCompression) |
|
32 { |
|
33 mWriter = aWriter; |
|
34 mHeader = aHeader; |
|
35 mStream = aStream; |
|
36 mHeader->mCRC = crc32(0L, Z_NULL, 0); |
|
37 |
|
38 nsresult rv = NS_NewSimpleStreamListener(getter_AddRefs(mOutput), aStream, |
|
39 nullptr); |
|
40 NS_ENSURE_SUCCESS(rv, rv); |
|
41 |
|
42 if (aCompression > 0) { |
|
43 mHeader->mMethod = ZIP_METHOD_DEFLATE; |
|
44 nsCOMPtr<nsIStreamConverter> converter = |
|
45 new nsDeflateConverter(aCompression); |
|
46 NS_ENSURE_TRUE(converter, NS_ERROR_OUT_OF_MEMORY); |
|
47 |
|
48 rv = converter->AsyncConvertData("uncompressed", "rawdeflate", mOutput, |
|
49 nullptr); |
|
50 NS_ENSURE_SUCCESS(rv, rv); |
|
51 |
|
52 mOutput = do_QueryInterface(converter, &rv); |
|
53 NS_ENSURE_SUCCESS(rv, rv); |
|
54 } |
|
55 else { |
|
56 mHeader->mMethod = ZIP_METHOD_STORE; |
|
57 } |
|
58 |
|
59 return NS_OK; |
|
60 } |
|
61 |
|
62 /* void onDataAvailable (in nsIRequest aRequest, in nsISupports aContext, |
|
63 * in nsIInputStream aInputStream, |
|
64 * in unsigned long long aOffset, in unsigned long aCount); */ |
|
65 NS_IMETHODIMP nsZipDataStream::OnDataAvailable(nsIRequest *aRequest, |
|
66 nsISupports *aContext, |
|
67 nsIInputStream *aInputStream, |
|
68 uint64_t aOffset, |
|
69 uint32_t aCount) |
|
70 { |
|
71 if (!mOutput) |
|
72 return NS_ERROR_NOT_INITIALIZED; |
|
73 |
|
74 nsAutoArrayPtr<char> buffer(new char[aCount]); |
|
75 NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); |
|
76 |
|
77 nsresult rv = ZW_ReadData(aInputStream, buffer.get(), aCount); |
|
78 NS_ENSURE_SUCCESS(rv, rv); |
|
79 |
|
80 return ProcessData(aRequest, aContext, buffer.get(), aOffset, aCount); |
|
81 } |
|
82 |
|
83 /* void onStartRequest (in nsIRequest aRequest, in nsISupports aContext); */ |
|
84 NS_IMETHODIMP nsZipDataStream::OnStartRequest(nsIRequest *aRequest, |
|
85 nsISupports *aContext) |
|
86 { |
|
87 if (!mOutput) |
|
88 return NS_ERROR_NOT_INITIALIZED; |
|
89 |
|
90 return mOutput->OnStartRequest(aRequest, aContext); |
|
91 } |
|
92 |
|
93 /* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext, |
|
94 * in nsresult aStatusCode); */ |
|
95 NS_IMETHODIMP nsZipDataStream::OnStopRequest(nsIRequest *aRequest, |
|
96 nsISupports *aContext, |
|
97 nsresult aStatusCode) |
|
98 { |
|
99 if (!mOutput) |
|
100 return NS_ERROR_NOT_INITIALIZED; |
|
101 |
|
102 nsresult rv = mOutput->OnStopRequest(aRequest, aContext, aStatusCode); |
|
103 mOutput = nullptr; |
|
104 if (NS_FAILED(rv)) { |
|
105 mWriter->EntryCompleteCallback(mHeader, rv); |
|
106 } |
|
107 else { |
|
108 rv = CompleteEntry(); |
|
109 rv = mWriter->EntryCompleteCallback(mHeader, rv); |
|
110 } |
|
111 |
|
112 mStream = nullptr; |
|
113 mWriter = nullptr; |
|
114 mHeader = nullptr; |
|
115 |
|
116 return rv; |
|
117 } |
|
118 |
|
119 inline nsresult nsZipDataStream::CompleteEntry() |
|
120 { |
|
121 nsresult rv; |
|
122 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream, &rv); |
|
123 NS_ENSURE_SUCCESS(rv, rv); |
|
124 int64_t pos; |
|
125 rv = seekable->Tell(&pos); |
|
126 NS_ENSURE_SUCCESS(rv, rv); |
|
127 |
|
128 mHeader->mCSize = pos - mHeader->mOffset - mHeader->GetFileHeaderLength(); |
|
129 mHeader->mWriteOnClose = true; |
|
130 return NS_OK; |
|
131 } |
|
132 |
|
133 nsresult nsZipDataStream::ProcessData(nsIRequest *aRequest, |
|
134 nsISupports *aContext, char *aBuffer, |
|
135 uint64_t aOffset, uint32_t aCount) |
|
136 { |
|
137 mHeader->mCRC = crc32(mHeader->mCRC, |
|
138 reinterpret_cast<const unsigned char*>(aBuffer), |
|
139 aCount); |
|
140 |
|
141 nsresult rv; |
|
142 nsCOMPtr<nsIStringInputStream> stream = |
|
143 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); |
|
144 NS_ENSURE_SUCCESS(rv, rv); |
|
145 |
|
146 stream->ShareData(aBuffer, aCount); |
|
147 rv = mOutput->OnDataAvailable(aRequest, aContext, stream, aOffset, aCount); |
|
148 mHeader->mUSize += aCount; |
|
149 |
|
150 return rv; |
|
151 } |
|
152 |
|
153 nsresult nsZipDataStream::ReadStream(nsIInputStream *aStream) |
|
154 { |
|
155 if (!mOutput) |
|
156 return NS_ERROR_NOT_INITIALIZED; |
|
157 |
|
158 nsresult rv = OnStartRequest(nullptr, nullptr); |
|
159 NS_ENSURE_SUCCESS(rv, rv); |
|
160 |
|
161 nsAutoArrayPtr<char> buffer(new char[4096]); |
|
162 NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); |
|
163 |
|
164 uint32_t read = 0; |
|
165 uint32_t offset = 0; |
|
166 do |
|
167 { |
|
168 rv = aStream->Read(buffer.get(), 4096, &read); |
|
169 if (NS_FAILED(rv)) { |
|
170 OnStopRequest(nullptr, nullptr, rv); |
|
171 return rv; |
|
172 } |
|
173 |
|
174 if (read > 0) { |
|
175 rv = ProcessData(nullptr, nullptr, buffer.get(), offset, read); |
|
176 if (NS_FAILED(rv)) { |
|
177 OnStopRequest(nullptr, nullptr, rv); |
|
178 return rv; |
|
179 } |
|
180 offset += read; |
|
181 } |
|
182 } while (read > 0); |
|
183 |
|
184 return OnStopRequest(nullptr, nullptr, NS_OK); |
|
185 } |