Bumping gaia.json for 3 gaia-central revision(s) a=gaia-bump
[gecko.git] / modules / libjar / nsJARInputStream.cpp
blob7cac781f9bcbc1002e4da3693eed7aeb74c61346
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* nsJARInputStream.cpp
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "nsJARInputStream.h"
9 #include "zipstruct.h" // defines ZIP compression codes
10 #include "nsZipArchive.h"
12 #include "nsNetUtil.h"
13 #include "nsEscape.h"
14 #include "nsIFile.h"
15 #include "nsDebug.h"
16 #include <algorithm>
17 #if defined(XP_WIN)
18 #include <windows.h>
19 #endif
21 /*---------------------------------------------
22 * nsISupports implementation
23 *--------------------------------------------*/
25 NS_IMPL_ISUPPORTS1(nsJARInputStream, nsIInputStream)
27 /*----------------------------------------------------------
28 * nsJARInputStream implementation
29 *--------------------------------------------------------*/
31 nsresult
32 nsJARInputStream::InitFile(nsJAR *aJar, nsZipItem *item)
34 nsresult rv = NS_OK;
35 NS_ABORT_IF_FALSE(aJar, "Argument may not be null");
36 NS_ABORT_IF_FALSE(item, "Argument may not be null");
38 // Mark it as closed, in case something fails in initialisation
39 mMode = MODE_CLOSED;
40 //-- prepare for the compression type
41 switch (item->Compression()) {
42 case STORED:
43 mMode = MODE_COPY;
44 break;
46 case DEFLATED:
47 rv = gZlibInit(&mZs);
48 NS_ENSURE_SUCCESS(rv, rv);
50 mMode = MODE_INFLATE;
51 mInCrc = item->CRC32();
52 mOutCrc = crc32(0L, Z_NULL, 0);
53 break;
55 default:
56 return NS_ERROR_NOT_IMPLEMENTED;
59 // Must keep handle to filepointer and mmap structure as long as we need access to the mmapped data
60 mFd = aJar->mZip->GetFD();
61 mZs.next_in = (Bytef *)aJar->mZip->GetData(item);
62 if (!mZs.next_in)
63 return NS_ERROR_FILE_CORRUPTED;
64 mZs.avail_in = item->Size();
65 mOutSize = item->RealSize();
66 mZs.total_out = 0;
67 return NS_OK;
70 nsresult
71 nsJARInputStream::InitDirectory(nsJAR* aJar,
72 const nsACString& aJarDirSpec,
73 const char* aDir)
75 NS_ABORT_IF_FALSE(aJar, "Argument may not be null");
76 NS_ABORT_IF_FALSE(aDir, "Argument may not be null");
78 // Mark it as closed, in case something fails in initialisation
79 mMode = MODE_CLOSED;
81 // Keep the zipReader for getting the actual zipItems
82 mJar = aJar;
83 nsZipFind *find;
84 nsresult rv;
85 // We can get aDir's contents as strings via FindEntries
86 // with the following pattern (see nsIZipReader.findEntries docs)
87 // assuming dirName is properly escaped:
89 // dirName + "?*~" + dirName + "?*/?*"
90 nsDependentCString dirName(aDir);
91 mNameLen = dirName.Length();
93 // iterate through dirName and copy it to escDirName, escaping chars
94 // which are special at the "top" level of the regexp so FindEntries
95 // works correctly
96 nsAutoCString escDirName;
97 const char* curr = dirName.BeginReading();
98 const char* end = dirName.EndReading();
99 while (curr != end) {
100 switch (*curr) {
101 case '*':
102 case '?':
103 case '$':
104 case '[':
105 case ']':
106 case '^':
107 case '~':
108 case '(':
109 case ')':
110 case '\\':
111 escDirName.Append('\\');
112 // fall through
113 default:
114 escDirName.Append(*curr);
116 ++curr;
118 nsAutoCString pattern = escDirName + NS_LITERAL_CSTRING("?*~") +
119 escDirName + NS_LITERAL_CSTRING("?*/?*");
120 rv = mJar->mZip->FindInit(pattern.get(), &find);
121 if (NS_FAILED(rv)) return rv;
123 const char *name;
124 uint16_t nameLen;
125 while ((rv = find->FindNext( &name, &nameLen )) == NS_OK) {
126 // Must copy, to make it zero-terminated
127 mArray.AppendElement(nsCString(name,nameLen));
129 delete find;
131 if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST && NS_FAILED(rv)) {
132 return NS_ERROR_FAILURE; // no error translation
135 // Sort it
136 mArray.Sort();
138 mBuffer.AssignLiteral("300: ");
139 mBuffer.Append(aJarDirSpec);
140 mBuffer.AppendLiteral("\n200: filename content-length last-modified file-type\n");
142 // Open for reading
143 mMode = MODE_DIRECTORY;
144 mZs.total_out = 0;
145 mArrPos = 0;
146 return NS_OK;
149 NS_IMETHODIMP
150 nsJARInputStream::Available(uint64_t *_retval)
152 // A lot of callers don't check the error code.
153 // They just use the _retval value.
154 *_retval = 0;
156 switch (mMode) {
157 case MODE_NOTINITED:
158 break;
160 case MODE_CLOSED:
161 return NS_BASE_STREAM_CLOSED;
163 case MODE_DIRECTORY:
164 *_retval = mBuffer.Length();
165 break;
167 case MODE_INFLATE:
168 case MODE_COPY:
169 *_retval = mOutSize - mZs.total_out;
170 break;
173 return NS_OK;
176 NS_IMETHODIMP
177 nsJARInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t *aBytesRead)
179 NS_ENSURE_ARG_POINTER(aBuffer);
180 NS_ENSURE_ARG_POINTER(aBytesRead);
182 *aBytesRead = 0;
184 nsresult rv = NS_OK;
185 MOZ_WIN_MEM_TRY_BEGIN
186 switch (mMode) {
187 case MODE_NOTINITED:
188 return NS_OK;
190 case MODE_CLOSED:
191 return NS_BASE_STREAM_CLOSED;
193 case MODE_DIRECTORY:
194 return ReadDirectory(aBuffer, aCount, aBytesRead);
196 case MODE_INFLATE:
197 if (mFd) {
198 rv = ContinueInflate(aBuffer, aCount, aBytesRead);
200 // be aggressive about releasing the file!
201 // note that sometimes, we will release mFd before we've finished
202 // deflating - this is because zlib buffers the input
203 if (mZs.avail_in == 0) {
204 mFd = nullptr;
206 break;
208 case MODE_COPY:
209 if (mFd) {
210 uint32_t count = std::min(aCount, mOutSize - uint32_t(mZs.total_out));
211 if (count) {
212 memcpy(aBuffer, mZs.next_in + mZs.total_out, count);
213 mZs.total_out += count;
215 *aBytesRead = count;
217 // be aggressive about releasing the file!
218 // note that sometimes, we will release mFd before we've finished copying.
219 if (mZs.total_out >= mOutSize) {
220 mFd = nullptr;
222 break;
224 MOZ_WIN_MEM_TRY_CATCH(rv = NS_ERROR_FAILURE)
225 return rv;
228 NS_IMETHODIMP
229 nsJARInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t count, uint32_t *_retval)
231 // don't have a buffer to read from, so this better not be called!
232 return NS_ERROR_NOT_IMPLEMENTED;
235 NS_IMETHODIMP
236 nsJARInputStream::IsNonBlocking(bool *aNonBlocking)
238 *aNonBlocking = false;
239 return NS_OK;
242 NS_IMETHODIMP
243 nsJARInputStream::Close()
245 if (mMode == MODE_INFLATE) {
246 inflateEnd(&mZs);
248 mMode = MODE_CLOSED;
249 mFd = nullptr;
250 return NS_OK;
253 nsresult
254 nsJARInputStream::ContinueInflate(char* aBuffer, uint32_t aCount,
255 uint32_t* aBytesRead)
257 // No need to check the args, ::Read did that, but assert them at least
258 NS_ASSERTION(aBuffer,"aBuffer parameter must not be null");
259 NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null");
261 // Keep old total_out count
262 const uint32_t oldTotalOut = mZs.total_out;
264 // make sure we aren't reading too much
265 mZs.avail_out = std::min(aCount, (mOutSize-oldTotalOut));
266 mZs.next_out = (unsigned char*)aBuffer;
268 // now inflate
269 int zerr = inflate(&mZs, Z_SYNC_FLUSH);
270 if ((zerr != Z_OK) && (zerr != Z_STREAM_END))
271 return NS_ERROR_FILE_CORRUPTED;
273 *aBytesRead = (mZs.total_out - oldTotalOut);
275 // Calculate the CRC on the output
276 mOutCrc = crc32(mOutCrc, (unsigned char*)aBuffer, *aBytesRead);
278 // be aggressive about ending the inflation
279 // for some reason we don't always get Z_STREAM_END
280 if (zerr == Z_STREAM_END || mZs.total_out == mOutSize) {
281 inflateEnd(&mZs);
283 // stop returning valid data as soon as we know we have a bad CRC
284 if (mOutCrc != mInCrc) {
285 // asserting because while this rarely happens, you definitely
286 // want to catch it in debug builds!
287 NS_NOTREACHED(0);
288 return NS_ERROR_FILE_CORRUPTED;
292 return NS_OK;
295 nsresult
296 nsJARInputStream::ReadDirectory(char* aBuffer, uint32_t aCount, uint32_t *aBytesRead)
298 // No need to check the args, ::Read did that, but assert them at least
299 NS_ASSERTION(aBuffer,"aBuffer parameter must not be null");
300 NS_ASSERTION(aBytesRead,"aBytesRead parameter must not be null");
302 // If the buffer contains data, copy what's there up to the desired amount
303 uint32_t numRead = CopyDataToBuffer(aBuffer, aCount);
305 if (aCount > 0) {
306 // empty the buffer and start writing directory entry lines to it
307 mBuffer.Truncate();
308 mCurPos = 0;
309 const uint32_t arrayLen = mArray.Length();
311 for ( ;aCount > mBuffer.Length(); mArrPos++) {
312 // have we consumed all the directory contents?
313 if (arrayLen <= mArrPos)
314 break;
316 const char * entryName = mArray[mArrPos].get();
317 uint32_t entryNameLen = mArray[mArrPos].Length();
318 nsZipItem* ze = mJar->mZip->GetItem(entryName);
319 NS_ENSURE_TRUE(ze, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
321 // Last Modified Time
322 PRExplodedTime tm;
323 PR_ExplodeTime(ze->LastModTime(), PR_GMTParameters, &tm);
324 char itemLastModTime[65];
325 PR_FormatTimeUSEnglish(itemLastModTime,
326 sizeof(itemLastModTime),
327 " %a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ",
328 &tm);
330 // write a 201: line to the buffer for this item
331 // 200: filename content-length last-modified file-type
332 mBuffer.AppendLiteral("201: ");
334 // Names must be escaped and relative, so use the pre-calculated length
335 // of the directory name as the offset into the string
336 // NS_EscapeURL adds the escaped URL to the give string buffer
337 NS_EscapeURL(entryName + mNameLen,
338 entryNameLen - mNameLen,
339 esc_Minimal | esc_AlwaysCopy,
340 mBuffer);
342 mBuffer.Append(' ');
343 mBuffer.AppendInt(ze->RealSize(), 10);
344 mBuffer.Append(itemLastModTime); // starts/ends with ' '
345 if (ze->IsDirectory())
346 mBuffer.AppendLiteral("DIRECTORY\n");
347 else
348 mBuffer.AppendLiteral("FILE\n");
351 // Copy up to the desired amount of data to buffer
352 numRead += CopyDataToBuffer(aBuffer, aCount);
355 *aBytesRead = numRead;
356 return NS_OK;
359 uint32_t
360 nsJARInputStream::CopyDataToBuffer(char* &aBuffer, uint32_t &aCount)
362 const uint32_t writeLength = std::min(aCount, mBuffer.Length() - mCurPos);
364 if (writeLength > 0) {
365 memcpy(aBuffer, mBuffer.get() + mCurPos, writeLength);
366 mCurPos += writeLength;
367 aCount -= writeLength;
368 aBuffer += writeLength;
371 // return number of bytes copied to the buffer so the
372 // Read method can return the number of bytes copied
373 return writeLength;