1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* nsJARInputStream.cpp
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"
21 /*---------------------------------------------
22 * nsISupports implementation
23 *--------------------------------------------*/
25 NS_IMPL_ISUPPORTS1(nsJARInputStream
, nsIInputStream
)
27 /*----------------------------------------------------------
28 * nsJARInputStream implementation
29 *--------------------------------------------------------*/
32 nsJARInputStream::InitFile(nsJAR
*aJar
, nsZipItem
*item
)
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
40 //-- prepare for the compression type
41 switch (item
->Compression()) {
48 NS_ENSURE_SUCCESS(rv
, rv
);
51 mInCrc
= item
->CRC32();
52 mOutCrc
= crc32(0L, Z_NULL
, 0);
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
);
63 return NS_ERROR_FILE_CORRUPTED
;
64 mZs
.avail_in
= item
->Size();
65 mOutSize
= item
->RealSize();
71 nsJARInputStream::InitDirectory(nsJAR
* aJar
,
72 const nsACString
& aJarDirSpec
,
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
81 // Keep the zipReader for getting the actual zipItems
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
96 nsAutoCString escDirName
;
97 const char* curr
= dirName
.BeginReading();
98 const char* end
= dirName
.EndReading();
111 escDirName
.Append('\\');
114 escDirName
.Append(*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
;
125 while ((rv
= find
->FindNext( &name
, &nameLen
)) == NS_OK
) {
126 // Must copy, to make it zero-terminated
127 mArray
.AppendElement(nsCString(name
,nameLen
));
131 if (rv
!= NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
&& NS_FAILED(rv
)) {
132 return NS_ERROR_FAILURE
; // no error translation
138 mBuffer
.AssignLiteral("300: ");
139 mBuffer
.Append(aJarDirSpec
);
140 mBuffer
.AppendLiteral("\n200: filename content-length last-modified file-type\n");
143 mMode
= MODE_DIRECTORY
;
150 nsJARInputStream::Available(uint64_t *_retval
)
152 // A lot of callers don't check the error code.
153 // They just use the _retval value.
161 return NS_BASE_STREAM_CLOSED
;
164 *_retval
= mBuffer
.Length();
169 *_retval
= mOutSize
- mZs
.total_out
;
177 nsJARInputStream::Read(char* aBuffer
, uint32_t aCount
, uint32_t *aBytesRead
)
179 NS_ENSURE_ARG_POINTER(aBuffer
);
180 NS_ENSURE_ARG_POINTER(aBytesRead
);
185 MOZ_WIN_MEM_TRY_BEGIN
191 return NS_BASE_STREAM_CLOSED
;
194 return ReadDirectory(aBuffer
, aCount
, aBytesRead
);
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) {
210 uint32_t count
= std::min(aCount
, mOutSize
- uint32_t(mZs
.total_out
));
212 memcpy(aBuffer
, mZs
.next_in
+ mZs
.total_out
, count
);
213 mZs
.total_out
+= 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
) {
224 MOZ_WIN_MEM_TRY_CATCH(rv
= NS_ERROR_FAILURE
)
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
;
236 nsJARInputStream::IsNonBlocking(bool *aNonBlocking
)
238 *aNonBlocking
= false;
243 nsJARInputStream::Close()
245 if (mMode
== MODE_INFLATE
) {
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
;
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
) {
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!
288 return NS_ERROR_FILE_CORRUPTED
;
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
);
306 // empty the buffer and start writing directory entry lines to it
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
)
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
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 ",
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
,
343 mBuffer
.AppendInt(ze
->RealSize(), 10);
344 mBuffer
.Append(itemLastModTime
); // starts/ends with ' '
345 if (ze
->IsDirectory())
346 mBuffer
.AppendLiteral("DIRECTORY\n");
348 mBuffer
.AppendLiteral("FILE\n");
351 // Copy up to the desired amount of data to buffer
352 numRead
+= CopyDataToBuffer(aBuffer
, aCount
);
355 *aBytesRead
= numRead
;
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