1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code, released
16 : * March 31, 1998.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Daniel Veditz <dveditz@netscape.com>
25 : * Samir Gehani <sgehani@netscape.com>
26 : * Mitch Stoltz <mstoltz@netscape.com>
27 : * Jeroen Dobbelaere <jeroen.dobbelaere@acunia.com>
28 : * Jeff Walden <jwalden+code@mit.edu>
29 : * Taras Glek <tglek@mozilla.com>
30 : *
31 : * Alternatively, the contents of this file may be used under the terms of
32 : * either the GNU General Public License Version 2 or later (the "GPL"), or
33 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34 : * in which case the provisions of the GPL or the LGPL are applicable instead
35 : * of those above. If you wish to allow use of your version of this file only
36 : * under the terms of either the GPL or the LGPL, and not to allow others to
37 : * use your version of this file under the terms of the MPL, indicate your
38 : * decision by deleting the provisions above and replace them with the notice
39 : * and other provisions required by the GPL or the LGPL. If you do not delete
40 : * the provisions above, a recipient may use your version of this file under
41 : * the terms of any one of the MPL, the GPL or the LGPL.
42 : *
43 : * ***** END LICENSE BLOCK ***** */
44 :
45 : /*
46 : * This module implements a simple archive extractor for the PKZIP format.
47 : *
48 : * The underlying nsZipArchive is NOT thread-safe. Do not pass references
49 : * or pointers to it across thread boundaries.
50 : */
51 :
52 : #define READTYPE PRInt32
53 : #include "zlib.h"
54 : #include "nsISupportsUtils.h"
55 : #include "prio.h"
56 : #include "plstr.h"
57 : #include "prlog.h"
58 : #include "stdlib.h"
59 : #include "nsWildCard.h"
60 : #include "nsZipArchive.h"
61 : #include "nsString.h"
62 : #include "mozilla/FunctionTimer.h"
63 : #include "prenv.h"
64 : #if defined(XP_WIN)
65 : #include <windows.h>
66 : #endif
67 :
68 : // For placement new used for arena allocations of zip file list
69 : #include NEW_H
70 : #define ZIP_ARENABLOCKSIZE (1*1024)
71 :
72 : #ifdef XP_UNIX
73 : #include <sys/types.h>
74 : #include <sys/stat.h>
75 : #include <limits.h>
76 : #include <unistd.h>
77 : #elif defined(XP_WIN) || defined(XP_OS2)
78 : #include <io.h>
79 : #endif
80 :
81 : #ifdef __SYMBIAN32__
82 : #include <sys/syslimits.h>
83 : #endif /*__SYMBIAN32__*/
84 :
85 :
86 : #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
87 : # ifndef S_IFMT
88 : # define S_IFMT 0170000
89 : # endif
90 : # ifndef S_IFLNK
91 : # define S_IFLNK 0120000
92 : # endif
93 : # ifndef PATH_MAX
94 : # define PATH_MAX 1024
95 : # endif
96 : #endif /* XP_UNIX */
97 :
98 :
99 : using namespace mozilla;
100 :
101 : static const PRUint32 kMaxNameLength = PATH_MAX; /* Maximum name length */
102 : // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
103 : static const PRUint16 kSyntheticTime = 0;
104 : static const PRUint16 kSyntheticDate = (1 + (1 << 5) + (0 << 9));
105 :
106 : static PRUint16 xtoint(const PRUint8 *ii);
107 : static PRUint32 xtolong(const PRUint8 *ll);
108 : static PRUint32 HashName(const char* aName, PRUint16 nameLen);
109 : #ifdef XP_UNIX
110 : static nsresult ResolveSymlink(const char *path);
111 : #endif
112 :
113 : //***********************************************************
114 : // For every inflation the following allocations are done:
115 : // malloc(1 * 9520)
116 : // malloc(32768 * 1)
117 : //***********************************************************
118 :
119 1223 : nsresult gZlibInit(z_stream *zs)
120 : {
121 1223 : memset(zs, 0, sizeof(z_stream));
122 1223 : int zerr = inflateInit2(zs, -MAX_WBITS);
123 1223 : if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
124 :
125 1223 : return NS_OK;
126 : }
127 :
128 2428 : nsZipHandle::nsZipHandle()
129 : : mFileData(nsnull)
130 : , mLen(0)
131 : , mMap(nsnull)
132 2428 : , mRefCnt(0)
133 : {
134 2428 : MOZ_COUNT_CTOR(nsZipHandle);
135 2428 : }
136 :
137 6192 : NS_IMPL_THREADSAFE_ADDREF(nsZipHandle)
138 8620 : NS_IMPL_THREADSAFE_RELEASE(nsZipHandle)
139 :
140 2383 : nsresult nsZipHandle::Init(nsILocalFile *file, nsZipHandle **ret)
141 : {
142 4766 : mozilla::AutoFDClose fd;
143 2383 : nsresult rv = file->OpenNSPRFileDesc(PR_RDONLY, 0000, &fd);
144 2383 : if (NS_FAILED(rv))
145 0 : return rv;
146 :
147 2383 : PRInt64 size = PR_Available64(fd);
148 2383 : if (size >= PR_INT32_MAX)
149 0 : return NS_ERROR_FILE_TOO_BIG;
150 :
151 2383 : PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
152 2383 : if (!map)
153 0 : return NS_ERROR_FAILURE;
154 :
155 2383 : PRUint8 *buf = (PRUint8*) PR_MemMap(map, 0, (PRUint32) size);
156 : // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
157 2383 : if (!buf) {
158 1 : PR_CloseFileMap(map);
159 1 : return NS_ERROR_FAILURE;
160 : }
161 :
162 4764 : nsRefPtr<nsZipHandle> handle = new nsZipHandle();
163 2382 : if (!handle) {
164 0 : PR_MemUnmap(buf, (PRUint32) size);
165 0 : PR_CloseFileMap(map);
166 0 : return NS_ERROR_OUT_OF_MEMORY;
167 : }
168 :
169 2382 : handle->mMap = map;
170 2382 : handle->mFile.Init(file);
171 2382 : handle->mLen = (PRUint32) size;
172 2382 : handle->mFileData = buf;
173 2382 : *ret = handle.forget().get();
174 2382 : return NS_OK;
175 : }
176 :
177 46 : nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry,
178 : nsZipHandle **ret)
179 : {
180 92 : nsRefPtr<nsZipHandle> handle = new nsZipHandle();
181 46 : if (!handle)
182 0 : return NS_ERROR_OUT_OF_MEMORY;
183 :
184 46 : handle->mBuf = new nsZipItemPtr<PRUint8>(zip, entry);
185 46 : if (!handle->mBuf)
186 0 : return NS_ERROR_OUT_OF_MEMORY;
187 :
188 46 : if (!handle->mBuf->Buffer())
189 0 : return NS_ERROR_UNEXPECTED;
190 :
191 46 : handle->mMap = nsnull;
192 46 : handle->mFile.Init(zip, entry);
193 46 : handle->mLen = handle->mBuf->Length();
194 46 : handle->mFileData = handle->mBuf->Buffer();
195 46 : *ret = handle.forget().get();
196 46 : return NS_OK;
197 : }
198 :
199 0 : PRInt64 nsZipHandle::SizeOfMapping()
200 : {
201 0 : return mLen;
202 : }
203 :
204 4856 : nsZipHandle::~nsZipHandle()
205 : {
206 2428 : if (mMap) {
207 2382 : PR_MemUnmap((void *)mFileData, mLen);
208 2382 : PR_CloseFileMap(mMap);
209 : }
210 2428 : mFileData = nsnull;
211 2428 : mMap = nsnull;
212 2428 : mBuf = nsnull;
213 2428 : MOZ_COUNT_DTOR(nsZipHandle);
214 2428 : }
215 :
216 : //***********************************************************
217 : // nsZipArchive -- public methods
218 : //***********************************************************
219 :
220 : //---------------------------------------------
221 : // nsZipArchive::OpenArchive
222 : //---------------------------------------------
223 2428 : nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle)
224 : {
225 2428 : mFd = aZipHandle;
226 :
227 : // Initialize our arena
228 2428 : PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
229 :
230 : //-- get table of contents for archive
231 2428 : nsresult rv = BuildFileList();
232 2428 : char *env = PR_GetEnv("MOZ_JAR_LOG_DIR");
233 2428 : if (env && NS_SUCCEEDED(rv) && aZipHandle->mFile) {
234 0 : nsCOMPtr<nsILocalFile> logFile;
235 0 : nsresult rv2 = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(logFile));
236 :
237 0 : if (!NS_SUCCEEDED(rv2))
238 0 : return rv;
239 :
240 : // Create a directory for the log (in case it doesn't exist)
241 0 : logFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
242 :
243 0 : nsAutoString name;
244 0 : nsCOMPtr<nsILocalFile> file = aZipHandle->mFile.GetBaseFile();
245 0 : file->GetLeafName(name);
246 0 : name.Append(NS_LITERAL_STRING(".log"));
247 0 : logFile->Append(name);
248 :
249 : PRFileDesc* fd;
250 0 : rv2 = logFile->OpenNSPRFileDesc(PR_WRONLY|PR_CREATE_FILE|PR_APPEND, 0644, &fd);
251 0 : if (NS_SUCCEEDED(rv2))
252 0 : mLog = fd;
253 : }
254 2428 : return rv;
255 : }
256 :
257 2383 : nsresult nsZipArchive::OpenArchive(nsIFile *aFile)
258 : {
259 : nsresult rv;
260 4766 : nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(aFile, &rv);
261 2383 : NS_ENSURE_SUCCESS(rv, rv);
262 :
263 4766 : nsRefPtr<nsZipHandle> handle;
264 2383 : rv = nsZipHandle::Init(localFile, getter_AddRefs(handle));
265 2383 : if (NS_FAILED(rv))
266 1 : return rv;
267 :
268 2382 : return OpenArchive(handle);
269 : }
270 :
271 : //---------------------------------------------
272 : // nsZipArchive::Test
273 : //---------------------------------------------
274 16 : nsresult nsZipArchive::Test(const char *aEntryName)
275 : {
276 : nsZipItem* currItem;
277 :
278 16 : if (aEntryName) // only test specified item
279 : {
280 14 : currItem = GetItem(aEntryName);
281 14 : if (!currItem)
282 0 : return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
283 : //-- don't test (synthetic) directory items
284 14 : if (currItem->IsDirectory())
285 0 : return NS_OK;
286 14 : return ExtractFile(currItem, 0, 0);
287 : }
288 :
289 : // test all items in archive
290 514 : for (int i = 0; i < ZIP_TABSIZE; i++) {
291 516 : for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
292 : //-- don't test (synthetic) directory items
293 4 : if (currItem->IsDirectory())
294 3 : continue;
295 1 : nsresult rv = ExtractFile(currItem, 0, 0);
296 1 : if (rv != NS_OK)
297 0 : return rv;
298 : }
299 : }
300 :
301 2 : return NS_OK;
302 : }
303 :
304 : //---------------------------------------------
305 : // nsZipArchive::CloseArchive
306 : //---------------------------------------------
307 5728 : nsresult nsZipArchive::CloseArchive()
308 : {
309 5728 : if (mFd) {
310 2428 : PL_FinishArenaPool(&mArena);
311 2428 : mFd = NULL;
312 : }
313 :
314 : // CAUTION:
315 : // We don't need to delete each of the nsZipItem as the memory for
316 : // the zip item and the filename it holds are both allocated from the Arena.
317 : // Hence, destroying the Arena is like destroying all the memory
318 : // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
319 : // anything more than cleaning up memory, we should start calling it.
320 : // Let us also cleanup the mFiles table for re-use on the next 'open' call
321 5728 : memset(mFiles, 0, sizeof(mFiles));
322 5728 : mBuiltSynthetics = false;
323 5728 : return NS_OK;
324 : }
325 :
326 : //---------------------------------------------
327 : // nsZipArchive::GetItem
328 : //---------------------------------------------
329 3932 : nsZipItem* nsZipArchive::GetItem(const char * aEntryName)
330 : {
331 3932 : if (aEntryName) {
332 3932 : PRUint32 len = strlen(aEntryName);
333 : //-- If the request is for a directory, make sure that synthetic entries
334 : //-- are created for the directories without their own entry.
335 3932 : if (!mBuiltSynthetics) {
336 1462 : if ((len > 0) && (aEntryName[len-1] == '/')) {
337 1 : if (BuildSynthetics() != NS_OK)
338 0 : return 0;
339 : }
340 : }
341 : MOZ_WIN_MEM_TRY_BEGIN
342 3932 : nsZipItem* item = mFiles[ HashName(aEntryName, len) ];
343 8006 : while (item) {
344 7700 : if ((len == item->nameLength) &&
345 3850 : (!memcmp(aEntryName, item->Name(), len))) {
346 :
347 3708 : if (mLog) {
348 : // Successful GetItem() is a good indicator that the file is about to be read
349 0 : char *tmp = PL_strdup(aEntryName);
350 0 : tmp[len]='\n';
351 0 : PR_Write(mLog, tmp, len+1);
352 0 : PL_strfree(tmp);
353 : }
354 3708 : return item; //-- found it
355 : }
356 142 : item = item->next;
357 : }
358 : MOZ_WIN_MEM_TRY_CATCH(return nsnull)
359 : }
360 224 : return nsnull;
361 : }
362 :
363 : //---------------------------------------------
364 : // nsZipArchive::ExtractFile
365 : // This extracts the item to the filehandle provided.
366 : // If 'aFd' is null, it only tests the extraction.
367 : // On extraction error(s) it removes the file.
368 : // When needed, it also resolves the symlink.
369 : //---------------------------------------------
370 731 : nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
371 : PRFileDesc* aFd)
372 : {
373 731 : if (!item)
374 0 : return NS_ERROR_ILLEGAL_VALUE;
375 731 : if (!mFd)
376 0 : return NS_ERROR_FAILURE;
377 :
378 : // Directory extraction is handled in nsJAR::Extract,
379 : // so the item to be extracted should never be a directory
380 731 : PR_ASSERT(!item->IsDirectory());
381 :
382 : Bytef outbuf[ZIP_BUFLEN];
383 :
384 1462 : nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
385 :
386 731 : nsresult rv = NS_OK;
387 :
388 468 : while (true) {
389 1199 : PRUint32 count = 0;
390 1199 : PRUint8* buf = cursor.Read(&count);
391 1199 : if (!buf) {
392 0 : rv = NS_ERROR_FILE_CORRUPTED;
393 0 : break;
394 1199 : } else if (count == 0) {
395 731 : break;
396 : }
397 :
398 468 : if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
399 0 : rv = NS_ERROR_FILE_DISK_FULL;
400 0 : break;
401 : }
402 : }
403 :
404 : //-- delete the file on errors, or resolve symlink if needed
405 731 : if (aFd) {
406 716 : PR_Close(aFd);
407 716 : if (rv != NS_OK)
408 0 : PR_Delete(outname);
409 : #ifdef XP_UNIX
410 716 : else if (item->IsSymlink())
411 0 : rv = ResolveSymlink(outname);
412 : #endif
413 : }
414 :
415 731 : return rv;
416 : }
417 :
418 : //---------------------------------------------
419 : // nsZipArchive::FindInit
420 : //---------------------------------------------
421 : PRInt32
422 2144 : nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
423 : {
424 2144 : if (!aFind)
425 0 : return NS_ERROR_ILLEGAL_VALUE;
426 :
427 : // null out param in case an error happens
428 2144 : *aFind = NULL;
429 :
430 2144 : bool regExp = false;
431 2144 : char* pattern = 0;
432 :
433 : // Create synthetic directory entries on demand
434 2144 : nsresult rv = BuildSynthetics();
435 2144 : if (rv != NS_OK)
436 0 : return rv;
437 :
438 : // validate the pattern
439 2144 : if (aPattern)
440 : {
441 792 : switch (NS_WildCardValid((char*)aPattern))
442 : {
443 : case INVALID_SXP:
444 0 : return NS_ERROR_ILLEGAL_VALUE;
445 :
446 : case NON_SXP:
447 0 : regExp = false;
448 0 : break;
449 :
450 : case VALID_SXP:
451 792 : regExp = true;
452 792 : break;
453 :
454 : default:
455 : // undocumented return value from RegExpValid!
456 0 : PR_ASSERT(false);
457 0 : return NS_ERROR_ILLEGAL_VALUE;
458 : }
459 :
460 792 : pattern = PL_strdup(aPattern);
461 792 : if (!pattern)
462 0 : return NS_ERROR_OUT_OF_MEMORY;
463 : }
464 :
465 2144 : *aFind = new nsZipFind(this, pattern, regExp);
466 2144 : if (!*aFind) {
467 0 : PL_strfree(pattern);
468 0 : return NS_ERROR_OUT_OF_MEMORY;
469 : }
470 :
471 2144 : return NS_OK;
472 : }
473 :
474 :
475 :
476 : //---------------------------------------------
477 : // nsZipFind::FindNext
478 : //---------------------------------------------
479 4124 : nsresult nsZipFind::FindNext(const char ** aResult, PRUint16 *aNameLen)
480 : {
481 4124 : if (!mArchive || !aResult || !aNameLen)
482 0 : return NS_ERROR_ILLEGAL_VALUE;
483 :
484 4124 : *aResult = 0;
485 4124 : *aNameLen = 0;
486 : MOZ_WIN_MEM_TRY_BEGIN
487 : // we start from last match, look for next
488 558103 : while (mSlot < ZIP_TABSIZE)
489 : {
490 : // move to next in current chain, or move to new slot
491 551837 : mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
492 :
493 551837 : bool found = false;
494 551837 : if (!mItem)
495 548588 : ++mSlot; // no more in this chain, move to next slot
496 3249 : else if (!mPattern)
497 1910 : found = true; // always match
498 1339 : else if (mRegExp)
499 : {
500 : char buf[kMaxNameLength+1];
501 1339 : memcpy(buf, mItem->Name(), mItem->nameLength);
502 1339 : buf[mItem->nameLength]='\0';
503 1339 : found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
504 : }
505 : else
506 0 : found = ((mItem->nameLength == strlen(mPattern)) &&
507 0 : (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
508 551837 : if (found) {
509 : // Need also to return the name length, as it is NOT zero-terminatdd...
510 1982 : *aResult = mItem->Name();
511 1982 : *aNameLen = mItem->nameLength;
512 1982 : return NS_OK;
513 : }
514 : }
515 : MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
516 2142 : return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
517 : }
518 :
519 : #ifdef XP_UNIX
520 : //---------------------------------------------
521 : // ResolveSymlink
522 : //---------------------------------------------
523 0 : static nsresult ResolveSymlink(const char *path)
524 : {
525 0 : PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
526 0 : if (!fIn)
527 0 : return NS_ERROR_FILE_DISK_FULL;
528 :
529 : char buf[PATH_MAX+1];
530 0 : PRInt32 length = PR_Read(fIn, (void*)buf, PATH_MAX);
531 0 : PR_Close(fIn);
532 :
533 0 : if ( (length <= 0)
534 0 : || ((buf[length] = 0, PR_Delete(path)) != 0)
535 0 : || (symlink(buf, path) != 0))
536 : {
537 0 : return NS_ERROR_FILE_DISK_FULL;
538 : }
539 0 : return NS_OK;
540 : }
541 : #endif
542 :
543 : //***********************************************************
544 : // nsZipArchive -- private implementation
545 : //***********************************************************
546 :
547 : //---------------------------------------------
548 : // nsZipArchive::CreateZipItem
549 : //---------------------------------------------
550 8474 : nsZipItem* nsZipArchive::CreateZipItem()
551 : {
552 : // Arena allocate the nsZipItem
553 : void *mem;
554 8474 : PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
555 8474 : return (nsZipItem*)mem;
556 : }
557 :
558 : //---------------------------------------------
559 : // nsZipArchive::BuildFileList
560 : //---------------------------------------------
561 2428 : nsresult nsZipArchive::BuildFileList()
562 : {
563 : #ifndef XP_WIN
564 : NS_TIME_FUNCTION;
565 : #endif
566 : // Get archive size using end pos
567 : const PRUint8* buf;
568 2428 : const PRUint8* startp = mFd->mFileData;
569 2428 : const PRUint8* endp = startp + mFd->mLen;
570 : MOZ_WIN_MEM_TRY_BEGIN
571 2428 : PRUint32 centralOffset = 4;
572 2428 : if (mFd->mLen > ZIPCENTRAL_SIZE && xtolong(startp + centralOffset) == CENTRALSIG) {
573 : // Success means optimized jar layout from bug 559961 is in effect
574 : } else {
575 7285 : for (buf = endp - ZIPEND_SIZE; buf > startp; buf--)
576 : {
577 7266 : if (xtolong(buf) == ENDSIG) {
578 2408 : centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
579 2408 : break;
580 : }
581 : }
582 : }
583 :
584 2428 : if (!centralOffset)
585 0 : return NS_ERROR_FILE_CORRUPTED;
586 :
587 : //-- Read the central directory headers
588 2428 : buf = startp + centralOffset;
589 2428 : PRUint32 sig = 0;
590 13327 : while (buf + PRInt32(sizeof(PRUint32)) <= endp &&
591 : (sig = xtolong(buf)) == CENTRALSIG) {
592 : // Make sure there is enough data available.
593 8471 : if (endp - buf < ZIPCENTRAL_SIZE)
594 0 : return NS_ERROR_FILE_CORRUPTED;
595 :
596 : // Read the fixed-size data.
597 8471 : ZipCentral* central = (ZipCentral*)buf;
598 :
599 8471 : PRUint16 namelen = xtoint(central->filename_len);
600 8471 : PRUint16 extralen = xtoint(central->extrafield_len);
601 8471 : PRUint16 commentlen = xtoint(central->commentfield_len);
602 :
603 : // Point to the next item at the top of loop
604 8471 : buf += ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
605 :
606 : // Sanity check variable sizes and refuse to deal with
607 : // anything too big: it's likely a corrupt archive.
608 8471 : if (namelen > kMaxNameLength || buf >= endp)
609 0 : return NS_ERROR_FILE_CORRUPTED;
610 :
611 8471 : nsZipItem* item = CreateZipItem();
612 8471 : if (!item)
613 0 : return NS_ERROR_OUT_OF_MEMORY;
614 :
615 8471 : item->central = central;
616 8471 : item->nameLength = namelen;
617 8471 : item->isSynthetic = false;
618 :
619 : // Add item to file table
620 8471 : PRUint32 hash = HashName(item->Name(), namelen);
621 8471 : item->next = mFiles[hash];
622 8471 : mFiles[hash] = item;
623 :
624 8471 : sig = 0;
625 : } /* while reading central directory records */
626 :
627 2428 : if (sig != ENDSIG)
628 20 : return NS_ERROR_FILE_CORRUPTED;
629 :
630 : // Make the comment available for consumers.
631 2408 : if (endp - buf >= ZIPEND_SIZE) {
632 2408 : ZipEnd *zipend = (ZipEnd *)buf;
633 :
634 2408 : buf += ZIPEND_SIZE;
635 2408 : PRUint16 commentlen = xtoint(zipend->commentfield_len);
636 2408 : if (endp - buf >= commentlen) {
637 2408 : mCommentPtr = (const char *)buf;
638 2408 : mCommentLen = commentlen;
639 : }
640 : }
641 :
642 : MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
643 2408 : return NS_OK;
644 : }
645 :
646 : //---------------------------------------------
647 : // nsZipArchive::BuildSynthetics
648 : //---------------------------------------------
649 2145 : nsresult nsZipArchive::BuildSynthetics()
650 : {
651 2145 : if (mBuiltSynthetics)
652 787 : return NS_OK;
653 1358 : mBuiltSynthetics = true;
654 :
655 : MOZ_WIN_MEM_TRY_BEGIN
656 : // Create synthetic entries for any missing directories.
657 : // Do this when all ziptable has scanned to prevent double entries.
658 349006 : for (int i = 0; i < ZIP_TABSIZE; ++i)
659 : {
660 349567 : for (nsZipItem* item = mFiles[i]; item != 0; item = item->next)
661 : {
662 1919 : if (item->isSynthetic)
663 1 : continue;
664 :
665 : //-- add entries for directories in the current item's path
666 : //-- go from end to beginning, because then we can stop trying
667 : //-- to create diritems if we find that the diritem we want to
668 : //-- create already exists
669 : //-- start just before the last char so as to not add the item
670 : //-- twice if it's a directory
671 1918 : PRUint16 namelen = item->nameLength;
672 1918 : const char *name = item->Name();
673 20987 : for (PRUint16 dirlen = namelen - 1; dirlen > 0; dirlen--)
674 : {
675 19125 : if (name[dirlen-1] != '/')
676 19066 : continue;
677 :
678 : // Is the directory already in the file table?
679 59 : PRUint32 hash = HashName(item->Name(), dirlen);
680 59 : bool found = false;
681 59 : for (nsZipItem* zi = mFiles[hash]; zi != NULL; zi = zi->next)
682 : {
683 112 : if ((dirlen == zi->nameLength) &&
684 56 : (0 == memcmp(item->Name(), zi->Name(), dirlen)))
685 : {
686 : // we've already added this dir and all its parents
687 56 : found = true;
688 56 : break;
689 : }
690 : }
691 : // if the directory was found, break out of the directory
692 : // creation loop now that we know all implicit directories
693 : // are there -- otherwise, start creating the zip item
694 59 : if (found)
695 56 : break;
696 :
697 3 : nsZipItem* diritem = CreateZipItem();
698 3 : if (!diritem)
699 0 : return NS_ERROR_OUT_OF_MEMORY;
700 :
701 : // Point to the central record of the original item for the name part.
702 3 : diritem->central = item->central;
703 3 : diritem->nameLength = dirlen;
704 3 : diritem->isSynthetic = true;
705 :
706 : // add diritem to the file table
707 3 : diritem->next = mFiles[hash];
708 3 : mFiles[hash] = diritem;
709 : } /* end processing of dirs in item's name */
710 : }
711 : }
712 : MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
713 1358 : return NS_OK;
714 : }
715 :
716 1336 : nsZipHandle* nsZipArchive::GetFD()
717 : {
718 1336 : if (!mFd)
719 0 : return NULL;
720 1336 : return mFd.get();
721 : }
722 :
723 : //---------------------------------------------
724 : // nsZipArchive::GetData
725 : //---------------------------------------------
726 2072 : const PRUint8* nsZipArchive::GetData(nsZipItem* aItem)
727 : {
728 2072 : PR_ASSERT (aItem);
729 : MOZ_WIN_MEM_TRY_BEGIN
730 : //-- read local header to get variable length values and calculate
731 : //-- the real data offset
732 2072 : PRUint32 len = mFd->mLen;
733 2072 : const PRUint8* data = mFd->mFileData;
734 2072 : PRUint32 offset = aItem->LocalOffset();
735 2072 : if (offset + ZIPLOCAL_SIZE > len)
736 0 : return nsnull;
737 :
738 : // -- check signature before using the structure, in case the zip file is corrupt
739 2072 : ZipLocal* Local = (ZipLocal*)(data + offset);
740 2072 : if ((xtolong(Local->signature) != LOCALSIG))
741 1 : return nsnull;
742 :
743 : //-- NOTE: extralen is different in central header and local header
744 : //-- for archives created using the Unix "zip" utility. To set
745 : //-- the offset accurately we need the _local_ extralen.
746 : offset += ZIPLOCAL_SIZE +
747 2071 : xtoint(Local->filename_len) +
748 2071 : xtoint(Local->extrafield_len);
749 :
750 : // -- check if there is enough source data in the file
751 2071 : if (offset + aItem->Size() > len)
752 0 : return nsnull;
753 :
754 2071 : return data + offset;
755 : MOZ_WIN_MEM_TRY_CATCH(return nsnull)
756 : }
757 :
758 : // nsZipArchive::GetComment
759 2 : bool nsZipArchive::GetComment(nsACString &aComment)
760 : {
761 : MOZ_WIN_MEM_TRY_BEGIN
762 2 : aComment.Assign(mCommentPtr, mCommentLen);
763 : MOZ_WIN_MEM_TRY_CATCH(return false)
764 2 : return true;
765 : }
766 :
767 : //---------------------------------------------
768 : // nsZipArchive::SizeOfMapping
769 : //---------------------------------------------
770 0 : PRInt64 nsZipArchive::SizeOfMapping()
771 : {
772 0 : return mFd ? mFd->SizeOfMapping() : 0;
773 : }
774 :
775 : //------------------------------------------
776 : // nsZipArchive constructor and destructor
777 : //------------------------------------------
778 :
779 2431 : nsZipArchive::nsZipArchive()
780 : : mRefCnt(0)
781 2431 : , mBuiltSynthetics(false)
782 : {
783 2431 : MOZ_COUNT_CTOR(nsZipArchive);
784 :
785 : // initialize the table to NULL
786 2431 : memset(mFiles, 0, sizeof(mFiles));
787 2431 : }
788 :
789 4621 : NS_IMPL_THREADSAFE_ADDREF(nsZipArchive)
790 7052 : NS_IMPL_THREADSAFE_RELEASE(nsZipArchive)
791 :
792 4862 : nsZipArchive::~nsZipArchive()
793 : {
794 2431 : CloseArchive();
795 :
796 2431 : MOZ_COUNT_DTOR(nsZipArchive);
797 2431 : }
798 :
799 :
800 : //------------------------------------------
801 : // nsZipFind constructor and destructor
802 : //------------------------------------------
803 :
804 2144 : nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp) :
805 : mArchive(aZip),
806 : mPattern(aPattern),
807 : mItem(0),
808 : mSlot(0),
809 2144 : mRegExp(aRegExp)
810 : {
811 2144 : MOZ_COUNT_CTOR(nsZipFind);
812 2144 : }
813 :
814 4288 : nsZipFind::~nsZipFind()
815 : {
816 2144 : PL_strfree(mPattern);
817 :
818 2144 : MOZ_COUNT_DTOR(nsZipFind);
819 2144 : }
820 :
821 : //------------------------------------------
822 : // helper functions
823 : //------------------------------------------
824 :
825 : /*
826 : * HashName
827 : *
828 : * returns a hash key for the entry name
829 : */
830 12462 : static PRUint32 HashName(const char* aName, PRUint16 len)
831 : {
832 12462 : PR_ASSERT(aName != 0);
833 :
834 12462 : const PRUint8* p = (const PRUint8*)aName;
835 12462 : const PRUint8* endp = p + len;
836 12462 : PRUint32 val = 0;
837 371354 : while (p != endp) {
838 346430 : val = val*37 + *p++;
839 : }
840 :
841 12462 : return (val % ZIP_TABSIZE);
842 : }
843 :
844 : /*
845 : * x t o i n t
846 : *
847 : * Converts a two byte ugly endianed integer
848 : * to our platform's integer.
849 : */
850 44982 : static PRUint16 xtoint (const PRUint8 *ii)
851 : {
852 44982 : return (PRUint16) ((ii [0]) | (ii [1] << 8));
853 : }
854 :
855 : /*
856 : * x t o l o n g
857 : *
858 : * Converts a four byte ugly endianed integer
859 : * to our platform's integer.
860 : */
861 41369 : static PRUint32 xtolong (const PRUint8 *ll)
862 : {
863 41369 : return (PRUint32)( (ll [0] << 0) |
864 41369 : (ll [1] << 8) |
865 41369 : (ll [2] << 16) |
866 124107 : (ll [3] << 24) );
867 : }
868 :
869 : /*
870 : * GetModTime
871 : *
872 : * returns last modification time in microseconds
873 : */
874 0 : static PRTime GetModTime(PRUint16 aDate, PRUint16 aTime)
875 : {
876 : // Note that on DST shift we can't handle correctly the hour that is valid
877 : // in both DST zones
878 : PRExplodedTime time;
879 :
880 0 : time.tm_usec = 0;
881 :
882 0 : time.tm_hour = (aTime >> 11) & 0x1F;
883 0 : time.tm_min = (aTime >> 5) & 0x3F;
884 0 : time.tm_sec = (aTime & 0x1F) * 2;
885 :
886 0 : time.tm_year = (aDate >> 9) + 1980;
887 0 : time.tm_month = ((aDate >> 5) & 0x0F) - 1;
888 0 : time.tm_mday = aDate & 0x1F;
889 :
890 0 : time.tm_params.tp_gmt_offset = 0;
891 0 : time.tm_params.tp_dst_offset = 0;
892 :
893 0 : PR_NormalizeTime(&time, PR_GMTParameters);
894 0 : time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
895 0 : PR_NormalizeTime(&time, PR_GMTParameters);
896 0 : time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
897 :
898 0 : return PR_ImplodeTime(&time);
899 : }
900 :
901 2072 : PRUint32 nsZipItem::LocalOffset()
902 : {
903 2072 : return xtolong(central->localhdr_offset);
904 : }
905 :
906 5642 : PRUint32 nsZipItem::Size()
907 : {
908 5642 : return isSynthetic ? 0 : xtolong(central->size);
909 : }
910 :
911 2847 : PRUint32 nsZipItem::RealSize()
912 : {
913 2847 : return isSynthetic ? 0 : xtolong(central->orglen);
914 : }
915 :
916 3540 : PRUint32 nsZipItem::CRC32()
917 : {
918 3540 : return isSynthetic ? 0 : xtolong(central->crc32);
919 : }
920 :
921 0 : PRUint16 nsZipItem::Date()
922 : {
923 0 : return isSynthetic ? kSyntheticDate : xtoint(central->date);
924 : }
925 :
926 0 : PRUint16 nsZipItem::Time()
927 : {
928 0 : return isSynthetic ? kSyntheticTime : xtoint(central->time);
929 : }
930 :
931 5655 : PRUint16 nsZipItem::Compression()
932 : {
933 5655 : return isSynthetic ? STORED : xtoint(central->method);
934 : }
935 :
936 4254 : bool nsZipItem::IsDirectory()
937 : {
938 4254 : return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
939 : }
940 :
941 716 : PRUint16 nsZipItem::Mode()
942 : {
943 716 : if (isSynthetic) return 0755;
944 716 : return ((PRUint16)(central->external_attributes[2]) | 0x100);
945 : }
946 :
947 2216 : const PRUint8 * nsZipItem::GetExtraField(PRUint16 aTag, PRUint16 *aBlockSize)
948 : {
949 2216 : if (isSynthetic) return nsnull;
950 : MOZ_WIN_MEM_TRY_BEGIN
951 : const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
952 2216 : nameLength;
953 2216 : PRUint32 buflen = (PRUint32)xtoint(central->extrafield_len);
954 2216 : PRUint32 pos = 0;
955 : PRUint16 tag, blocksize;
956 :
957 4432 : while (buf && (pos + 4) <= buflen) {
958 2216 : tag = xtoint(buf + pos);
959 2216 : blocksize = xtoint(buf + pos + 2);
960 :
961 2216 : if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
962 2216 : *aBlockSize = blocksize;
963 2216 : return buf + pos;
964 : }
965 :
966 0 : pos += blocksize + 4;
967 : }
968 :
969 : MOZ_WIN_MEM_TRY_CATCH(return nsnull)
970 0 : return nsnull;
971 : }
972 :
973 :
974 2216 : PRTime nsZipItem::LastModTime()
975 : {
976 2216 : if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
977 :
978 : // Try to read timestamp from extra field
979 : PRUint16 blocksize;
980 2216 : const PRUint8 *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
981 2216 : if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
982 2216 : return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
983 : }
984 :
985 0 : return GetModTime(Date(), Time());
986 : }
987 :
988 : #ifdef XP_UNIX
989 716 : bool nsZipItem::IsSymlink()
990 : {
991 716 : if (isSynthetic) return false;
992 716 : return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
993 : }
994 : #endif
995 :
996 784 : nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, PRUint8* aBuf, PRUint32 aBufSize, bool doCRC) :
997 : mItem(item),
998 : mBuf(aBuf),
999 : mBufSize(aBufSize),
1000 784 : mDoCRC(doCRC)
1001 : {
1002 784 : if (mItem->Compression() == DEFLATED) {
1003 : #ifdef DEBUG
1004 : nsresult status =
1005 : #endif
1006 389 : gZlibInit(&mZs);
1007 389 : NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
1008 389 : NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
1009 : }
1010 :
1011 784 : mZs.avail_in = item->Size();
1012 784 : mZs.next_in = (Bytef*)aZip->GetData(item);
1013 :
1014 784 : if (doCRC)
1015 738 : mCRC = crc32(0L, Z_NULL, 0);
1016 784 : }
1017 :
1018 784 : nsZipCursor::~nsZipCursor()
1019 : {
1020 784 : if (mItem->Compression() == DEFLATED) {
1021 389 : inflateEnd(&mZs);
1022 : }
1023 784 : }
1024 :
1025 1252 : PRUint8* nsZipCursor::ReadOrCopy(PRUint32 *aBytesRead, bool aCopy) {
1026 : int zerr;
1027 1252 : PRUint8 *buf = nsnull;
1028 1252 : bool verifyCRC = true;
1029 :
1030 1252 : if (!mZs.next_in)
1031 0 : return nsnull;
1032 : MOZ_WIN_MEM_TRY_BEGIN
1033 1252 : switch (mItem->Compression()) {
1034 : case STORED:
1035 487 : if (!aCopy) {
1036 485 : *aBytesRead = mZs.avail_in;
1037 485 : buf = mZs.next_in;
1038 485 : mZs.next_in += mZs.avail_in;
1039 485 : mZs.avail_in = 0;
1040 : } else {
1041 2 : *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
1042 2 : memcpy(mBuf, mZs.next_in, *aBytesRead);
1043 2 : mZs.avail_in -= *aBytesRead;
1044 2 : mZs.next_in += *aBytesRead;
1045 : }
1046 487 : break;
1047 : case DEFLATED:
1048 765 : buf = mBuf;
1049 765 : mZs.next_out = buf;
1050 765 : mZs.avail_out = mBufSize;
1051 :
1052 765 : zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
1053 765 : if (zerr != Z_OK && zerr != Z_STREAM_END)
1054 0 : return nsnull;
1055 :
1056 765 : *aBytesRead = mZs.next_out - buf;
1057 765 : verifyCRC = (zerr == Z_STREAM_END);
1058 765 : break;
1059 : default:
1060 0 : return nsnull;
1061 : }
1062 :
1063 1252 : if (mDoCRC) {
1064 1206 : mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead);
1065 1206 : if (verifyCRC && mCRC != mItem->CRC32())
1066 2 : return nsnull;
1067 : }
1068 : MOZ_WIN_MEM_TRY_CATCH(return nsnull)
1069 1250 : return buf;
1070 : }
1071 :
1072 48 : nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip, const char * aEntryName, bool doCRC) :
1073 48 : mReturnBuf(nsnull)
1074 : {
1075 : // make sure the ziparchive hangs around
1076 48 : mZipHandle = aZip->GetFD();
1077 :
1078 48 : nsZipItem* item = aZip->GetItem(aEntryName);
1079 48 : if (!item)
1080 1 : return;
1081 :
1082 47 : PRUint32 size = 0;
1083 47 : if (item->Compression() == DEFLATED) {
1084 7 : size = item->RealSize();
1085 14 : mAutoBuf = new PRUint8[size];
1086 : }
1087 :
1088 94 : nsZipCursor cursor(item, aZip, mAutoBuf, size, doCRC);
1089 47 : mReturnBuf = cursor.Read(&mReadlen);
1090 47 : if (!mReturnBuf) {
1091 : return;
1092 : }
1093 :
1094 47 : if (mReadlen != item->RealSize()) {
1095 0 : NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
1096 0 : mReturnBuf = nsnull;
1097 : return;
1098 : }
1099 : }
|