2000-02-14 04:57:01 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2001-09-20 04:02:59 +04:00
|
|
|
/*
|
|
|
|
* The contents of this file are subject to the Netscape Public
|
1999-11-06 06:43:54 +03:00
|
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy of
|
2001-09-20 04:02:59 +04:00
|
|
|
* the License at http://www.mozilla.org/NPL/
|
1999-06-02 01:08:32 +04:00
|
|
|
*
|
1999-11-06 06:43:54 +03:00
|
|
|
* Software distributed under the License is distributed on an "AS
|
|
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
|
|
* implied. See the License for the specific language governing
|
|
|
|
* rights and limitations under the License.
|
1999-06-02 01:08:32 +04:00
|
|
|
*
|
2001-09-20 04:02:59 +04:00
|
|
|
* The Original Code is Mozilla Communicator client code,
|
|
|
|
* released March 31, 1998.
|
1999-06-02 01:08:32 +04:00
|
|
|
*
|
2001-09-20 04:02:59 +04:00
|
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
|
|
* Corporation. Portions created by Netscape are
|
|
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
1999-11-06 06:43:54 +03:00
|
|
|
* Rights Reserved.
|
1999-06-02 01:08:32 +04:00
|
|
|
*
|
2001-09-20 04:02:59 +04:00
|
|
|
* Contributor(s):
|
1999-06-02 01:08:32 +04:00
|
|
|
* Daniel Veditz <dveditz@netscape.com>
|
1999-06-23 10:16:28 +04:00
|
|
|
* Samir Gehani <sgehani@netscape.com>
|
1999-10-26 23:43:26 +04:00
|
|
|
* Mitch Stoltz <mstoltz@netsape.com>
|
2000-01-11 23:49:15 +03:00
|
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
2001-09-20 04:02:59 +04:00
|
|
|
*/
|
1999-06-02 01:08:32 +04:00
|
|
|
#include <string.h>
|
2000-01-29 03:03:57 +03:00
|
|
|
#include "nsJARInputStream.h"
|
|
|
|
#include "nsJAR.h"
|
2002-04-27 09:33:09 +04:00
|
|
|
#include "nsILocalFile.h"
|
2000-01-29 03:03:57 +03:00
|
|
|
#include "nsXPIDLString.h"
|
2001-09-29 12:28:41 +04:00
|
|
|
#include "nsReadableUtils.h"
|
2000-03-21 07:21:28 +03:00
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "plbase64.h"
|
2000-04-26 07:50:07 +04:00
|
|
|
#include "nsIConsoleService.h"
|
2001-02-23 03:15:04 +03:00
|
|
|
#include "nscore.h"
|
2002-05-15 22:55:21 +04:00
|
|
|
#include "nsCRT.h"
|
2000-03-21 07:21:28 +03:00
|
|
|
|
2000-02-21 23:19:16 +03:00
|
|
|
#ifdef XP_UNIX
|
|
|
|
#include <sys/stat.h>
|
2003-04-03 02:45:08 +04:00
|
|
|
#elif defined (XP_WIN) || defined(XP_OS2)
|
2000-02-21 23:19:16 +03:00
|
|
|
#include <io.h>
|
|
|
|
#endif
|
|
|
|
|
2000-02-14 04:57:01 +03:00
|
|
|
//----------------------------------------------
|
|
|
|
// Errors and other utility definitions
|
|
|
|
//----------------------------------------------
|
1999-11-12 09:13:13 +03:00
|
|
|
#ifndef __gen_nsIFile_h__
|
2000-03-28 07:38:06 +04:00
|
|
|
#define NS_ERROR_FILE_UNRECOGNIZED_PATH NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 1)
|
1999-11-12 09:13:13 +03:00
|
|
|
#define NS_ERROR_FILE_UNRESOLVABLE_SYMLINK NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 2)
|
|
|
|
#define NS_ERROR_FILE_EXECUTION_FAILED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 3)
|
|
|
|
#define NS_ERROR_FILE_UNKNOWN_TYPE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 4)
|
|
|
|
#define NS_ERROR_FILE_DESTINATION_NOT_DIR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 5)
|
|
|
|
#define NS_ERROR_FILE_TARGET_DOES_NOT_EXIST NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 6)
|
|
|
|
#define NS_ERROR_FILE_COPY_OR_MOVE_FAILED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 7)
|
|
|
|
#define NS_ERROR_FILE_ALREADY_EXISTS NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 8)
|
|
|
|
#define NS_ERROR_FILE_INVALID_PATH NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 9)
|
|
|
|
#define NS_ERROR_FILE_DISK_FULL NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 10)
|
|
|
|
#define NS_ERROR_FILE_CORRUPTED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_FILES, 11)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static nsresult
|
|
|
|
ziperr2nsresult(PRInt32 ziperr)
|
|
|
|
{
|
|
|
|
switch (ziperr) {
|
|
|
|
case ZIP_OK: return NS_OK;
|
|
|
|
case ZIP_ERR_MEMORY: return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
case ZIP_ERR_DISK: return NS_ERROR_FILE_DISK_FULL;
|
|
|
|
case ZIP_ERR_CORRUPT: return NS_ERROR_FILE_CORRUPTED;
|
|
|
|
case ZIP_ERR_PARAM: return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
case ZIP_ERR_FNF: return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
|
|
|
|
case ZIP_ERR_UNSUPPORTED: return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
default: return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
1999-06-02 01:08:32 +04:00
|
|
|
|
2000-01-29 03:03:57 +03:00
|
|
|
//-- PR_Free doesn't null the pointer.
|
|
|
|
// This macro takes care of that.
|
|
|
|
#define JAR_NULLFREE(_ptr) \
|
|
|
|
{ \
|
|
|
|
PR_FREEIF(_ptr); \
|
2000-02-02 04:58:37 +03:00
|
|
|
_ptr = nsnull; \
|
2000-01-29 03:03:57 +03:00
|
|
|
}
|
|
|
|
|
2000-02-14 04:57:01 +03:00
|
|
|
//----------------------------------------------
|
|
|
|
// nsJARManifestItem declaration
|
|
|
|
//----------------------------------------------
|
|
|
|
/*
|
|
|
|
* nsJARManifestItem contains meta-information pertaining
|
|
|
|
* to an individual JAR entry, taken from the
|
|
|
|
* META-INF/MANIFEST.MF and META-INF/ *.SF files.
|
|
|
|
* This is security-critical information, defined here so it is not
|
|
|
|
* accessible from anywhere else.
|
|
|
|
*/
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
JAR_INVALID = 1,
|
2000-03-21 07:21:28 +03:00
|
|
|
JAR_INTERNAL = 2,
|
|
|
|
JAR_EXTERNAL = 3
|
2000-02-14 04:57:01 +03:00
|
|
|
} JARManifestItemType;
|
|
|
|
|
|
|
|
class nsJARManifestItem
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
JARManifestItemType mType;
|
|
|
|
|
2000-03-21 07:21:28 +03:00
|
|
|
// True if the second step of verification (VerifyEntry)
|
2000-02-14 04:57:01 +03:00
|
|
|
// has taken place:
|
2000-07-12 07:10:33 +04:00
|
|
|
PRBool entryVerified;
|
2000-02-14 04:57:01 +03:00
|
|
|
|
2000-03-21 07:21:28 +03:00
|
|
|
// Not signed, valid, or failure code
|
|
|
|
PRInt16 status;
|
2000-02-14 04:57:01 +03:00
|
|
|
|
|
|
|
// Internal storage of digests
|
|
|
|
char* calculatedSectionDigest;
|
|
|
|
char* storedEntryDigest;
|
|
|
|
|
|
|
|
nsJARManifestItem();
|
|
|
|
virtual ~nsJARManifestItem();
|
|
|
|
};
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
|
|
// nsJARManifestItem constructors and destructor
|
|
|
|
//-------------------------------------------------
|
|
|
|
nsJARManifestItem::nsJARManifestItem(): mType(JAR_INTERNAL),
|
2000-07-12 07:10:33 +04:00
|
|
|
entryVerified(PR_FALSE),
|
2001-02-23 03:15:04 +03:00
|
|
|
status(nsIJAR::NOT_SIGNED),
|
2000-02-14 04:57:01 +03:00
|
|
|
calculatedSectionDigest(nsnull),
|
|
|
|
storedEntryDigest(nsnull)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsJARManifestItem::~nsJARManifestItem()
|
|
|
|
{
|
|
|
|
// Delete digests if necessary
|
|
|
|
PR_FREEIF(calculatedSectionDigest);
|
|
|
|
PR_FREEIF(storedEntryDigest);
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------
|
|
|
|
// nsJAR constructor/destructor
|
|
|
|
//----------------------------------------------
|
2000-01-29 03:03:57 +03:00
|
|
|
PR_STATIC_CALLBACK(PRBool)
|
|
|
|
DeleteManifestEntry(nsHashKey* aKey, void* aData, void* closure)
|
1999-06-02 01:08:32 +04:00
|
|
|
{
|
2000-02-14 04:57:01 +03:00
|
|
|
//-- deletes an entry in mManifestData.
|
2000-01-29 03:03:57 +03:00
|
|
|
PR_FREEIF(aData);
|
|
|
|
return PR_TRUE;
|
1999-06-02 01:08:32 +04:00
|
|
|
}
|
|
|
|
|
2000-04-26 07:50:07 +04:00
|
|
|
// The following initialization makes a guess of 10 entries per jarfile.
|
|
|
|
nsJAR::nsJAR(): mManifestData(nsnull, nsnull, DeleteManifestEntry, nsnull, 10),
|
2001-02-23 03:15:04 +03:00
|
|
|
mParsedManifest(PR_FALSE), mGlobalStatus(nsIJAR::NOT_SIGNED),
|
2003-06-25 05:13:36 +04:00
|
|
|
mReleaseTime(PR_INTERVAL_NO_TIMEOUT),
|
|
|
|
mCache(nsnull),
|
|
|
|
mLock(nsnull),
|
|
|
|
mTotalItemsInManifest(0),
|
|
|
|
mFd(nsnull)
|
1999-11-12 09:13:13 +03:00
|
|
|
{
|
|
|
|
}
|
1999-06-02 01:08:32 +04:00
|
|
|
|
|
|
|
nsJAR::~nsJAR()
|
2000-06-07 13:05:22 +04:00
|
|
|
{
|
|
|
|
Close();
|
2000-09-16 01:56:20 +04:00
|
|
|
if (mLock)
|
2003-03-14 21:59:51 +03:00
|
|
|
PR_DestroyLock(mLock);
|
1999-06-02 01:08:32 +04:00
|
|
|
}
|
|
|
|
|
2001-02-23 03:15:04 +03:00
|
|
|
NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsJAR, nsIZipReader, nsIJAR)
|
2000-08-23 07:18:53 +04:00
|
|
|
NS_IMPL_THREADSAFE_ADDREF(nsJAR)
|
|
|
|
|
|
|
|
// Custom Release method works with nsZipReaderCache...
|
|
|
|
nsrefcnt nsJAR::Release(void)
|
|
|
|
{
|
|
|
|
nsrefcnt count;
|
|
|
|
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
|
|
|
count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
|
|
|
|
NS_LOG_RELEASE(this, count, "nsJAR");
|
|
|
|
if (0 == count) {
|
|
|
|
mRefCnt = 1; /* stabilize */
|
|
|
|
/* enable this to find non-threadsafe destructors: */
|
|
|
|
/* NS_ASSERT_OWNINGTHREAD(_class); */
|
|
|
|
NS_DELETEXPCOM(this);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (1 == count && mCache) {
|
|
|
|
nsresult rv = mCache->ReleaseZip(this);
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to release zip file");
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
1999-06-02 01:08:32 +04:00
|
|
|
|
2000-02-14 04:57:01 +03:00
|
|
|
//----------------------------------------------
|
2001-02-23 03:15:04 +03:00
|
|
|
// nsIZipReader implementation
|
2000-02-14 04:57:01 +03:00
|
|
|
//----------------------------------------------
|
2000-04-12 11:58:24 +04:00
|
|
|
|
1999-06-02 01:08:32 +04:00
|
|
|
NS_IMETHODIMP
|
2000-01-25 00:28:28 +03:00
|
|
|
nsJAR::Init(nsIFile* zipFile)
|
1999-06-02 01:08:32 +04:00
|
|
|
{
|
1999-11-12 09:13:13 +03:00
|
|
|
mZipFile = zipFile;
|
2000-09-16 01:56:20 +04:00
|
|
|
mLock = PR_NewLock();
|
|
|
|
return mLock ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
1999-06-02 01:08:32 +04:00
|
|
|
}
|
|
|
|
|
2000-04-12 11:58:24 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJAR::GetFile(nsIFile* *result)
|
|
|
|
{
|
|
|
|
*result = mZipFile;
|
|
|
|
NS_ADDREF(*result);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-06-02 01:08:32 +04:00
|
|
|
NS_IMETHODIMP
|
1999-11-12 09:13:13 +03:00
|
|
|
nsJAR::Open()
|
|
|
|
{
|
2000-01-25 00:28:28 +03:00
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(mZipFile, &rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2003-03-14 21:59:51 +03:00
|
|
|
rv = localFile->OpenNSPRFileDesc(PR_RDONLY, 0664, &mFd);
|
2000-01-25 00:28:28 +03:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2003-03-14 21:59:51 +03:00
|
|
|
PRInt32 err = mZip.OpenArchiveWithFileDesc(mFd);
|
2000-01-25 23:41:47 +03:00
|
|
|
|
1999-11-12 09:13:13 +03:00
|
|
|
return ziperr2nsresult(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJAR::Close()
|
|
|
|
{
|
2003-06-24 02:58:28 +04:00
|
|
|
#ifdef STANDALONE
|
2003-03-14 21:59:51 +03:00
|
|
|
// nsZipReadState::CloseArchive closes the file descriptor
|
2003-06-24 02:58:28 +04:00
|
|
|
#else
|
|
|
|
if (mFd)
|
|
|
|
PR_Close(mFd);
|
|
|
|
#endif
|
2003-03-14 21:59:51 +03:00
|
|
|
mFd = nsnull;
|
1999-11-12 09:13:13 +03:00
|
|
|
PRInt32 err = mZip.CloseArchive();
|
|
|
|
return ziperr2nsresult(err);
|
|
|
|
}
|
|
|
|
|
2000-12-27 10:05:55 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJAR::Test(const char *aEntryName)
|
|
|
|
{
|
2003-03-14 21:59:51 +03:00
|
|
|
NS_ASSERTION(mFd, "File isn't open!");
|
|
|
|
|
|
|
|
PRInt32 err = mZip.Test(aEntryName, mFd);
|
2000-12-27 10:05:55 +03:00
|
|
|
return ziperr2nsresult(err);
|
|
|
|
}
|
|
|
|
|
1999-11-12 09:13:13 +03:00
|
|
|
NS_IMETHODIMP
|
2000-01-25 00:28:28 +03:00
|
|
|
nsJAR::Extract(const char *zipEntry, nsIFile* outFile)
|
1999-06-02 01:08:32 +04:00
|
|
|
{
|
2000-09-16 01:56:20 +04:00
|
|
|
// nsZipArchive and zlib are not thread safe
|
|
|
|
// we need to use a lock to prevent bug #51267
|
|
|
|
nsAutoLock lock(mLock);
|
|
|
|
|
2000-01-25 00:28:28 +03:00
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(outFile, &rv);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
PRFileDesc* fd;
|
2000-01-25 23:41:47 +03:00
|
|
|
rv = localFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 0664, &fd);
|
2001-03-14 08:15:55 +03:00
|
|
|
if (NS_FAILED(rv)) return NS_ERROR_FILE_ACCESS_DENIED;
|
2000-01-25 00:28:28 +03:00
|
|
|
|
2000-03-28 07:38:06 +04:00
|
|
|
nsZipItem *item = 0;
|
2003-03-14 21:59:51 +03:00
|
|
|
PRInt32 err = mZip.ExtractFileToFileDesc(zipEntry, fd, &item, mFd);
|
2000-03-28 07:38:06 +04:00
|
|
|
PR_Close(fd);
|
2000-02-21 23:19:16 +03:00
|
|
|
|
|
|
|
if (err != ZIP_OK)
|
2001-07-24 22:38:25 +04:00
|
|
|
outFile->Remove(PR_FALSE);
|
2000-02-21 23:19:16 +03:00
|
|
|
else
|
|
|
|
{
|
2000-03-28 07:38:06 +04:00
|
|
|
#if defined(XP_UNIX)
|
2002-04-27 09:33:09 +04:00
|
|
|
nsCAutoString path;
|
|
|
|
rv = outFile->GetNativePath(path);
|
2001-09-21 03:33:23 +04:00
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
2001-12-07 05:08:57 +03:00
|
|
|
if (item->flags & ZIFLAG_SYMLINK)
|
2001-09-21 03:33:23 +04:00
|
|
|
{
|
2002-04-27 09:33:09 +04:00
|
|
|
err = mZip.ResolveSymlink(path.get(),item);
|
2001-09-21 03:33:23 +04:00
|
|
|
}
|
2002-04-27 09:33:09 +04:00
|
|
|
chmod(path.get(), item->mode);
|
2001-09-21 03:33:23 +04:00
|
|
|
}
|
2000-02-21 23:19:16 +03:00
|
|
|
#endif
|
|
|
|
|
2003-07-16 02:18:04 +04:00
|
|
|
PRTime prtime = item->GetModTime();
|
|
|
|
// nsIFile needs usecs.
|
|
|
|
PRTime conversion = LL_ZERO;
|
|
|
|
PRTime newTime = LL_ZERO;
|
|
|
|
LL_I2L(conversion, PR_USEC_PER_MSEC);
|
|
|
|
LL_DIV(newTime, prtime, conversion);
|
|
|
|
// non-fatal if this fails, ignore errors
|
|
|
|
outFile->SetLastModifiedTime(newTime);
|
2000-03-28 07:38:06 +04:00
|
|
|
}
|
|
|
|
|
1999-11-12 09:13:13 +03:00
|
|
|
return ziperr2nsresult(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJAR::GetEntry(const char *zipEntry, nsIZipEntry* *result)
|
|
|
|
{
|
2000-01-29 03:03:57 +03:00
|
|
|
nsZipItem* zipItem;
|
|
|
|
PRInt32 err = mZip.GetItem(zipEntry, &zipItem);
|
|
|
|
if (err != ZIP_OK) return ziperr2nsresult(err);
|
1999-11-12 09:13:13 +03:00
|
|
|
|
|
|
|
nsJARItem* jarItem = new nsJARItem();
|
|
|
|
if (jarItem == nsnull)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(jarItem);
|
2000-01-29 03:03:57 +03:00
|
|
|
jarItem->Init(zipItem);
|
1999-11-12 09:13:13 +03:00
|
|
|
*result = jarItem;
|
1999-06-02 01:08:32 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
1999-06-23 10:16:28 +04:00
|
|
|
NS_IMETHODIMP
|
2000-02-14 04:57:01 +03:00
|
|
|
nsJAR::FindEntries(const char *aPattern, nsISimpleEnumerator **result)
|
1999-06-23 10:16:28 +04:00
|
|
|
{
|
2000-02-14 04:57:01 +03:00
|
|
|
if (!result)
|
1999-11-12 09:13:13 +03:00
|
|
|
return NS_ERROR_INVALID_POINTER;
|
1999-06-23 10:16:28 +04:00
|
|
|
|
1999-11-12 09:13:13 +03:00
|
|
|
nsZipFind *find = mZip.FindInit(aPattern);
|
|
|
|
if (!find)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
1999-06-23 10:16:28 +04:00
|
|
|
|
1999-11-12 09:13:13 +03:00
|
|
|
nsISimpleEnumerator *zipEnum = new nsJAREnumerator(find);
|
|
|
|
if (!zipEnum)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF( zipEnum );
|
1999-06-23 10:16:28 +04:00
|
|
|
|
2000-02-14 04:57:01 +03:00
|
|
|
*result = zipEnum;
|
1999-11-12 09:13:13 +03:00
|
|
|
return NS_OK;
|
1999-06-23 10:16:28 +04:00
|
|
|
}
|
|
|
|
|
1999-10-26 23:43:26 +04:00
|
|
|
NS_IMETHODIMP
|
2000-07-12 07:10:33 +04:00
|
|
|
nsJAR::GetInputStream(const char* aFilename, nsIInputStream** result)
|
1999-10-26 23:43:26 +04:00
|
|
|
{
|
2000-09-16 01:56:20 +04:00
|
|
|
// nsZipArchive and zlib are not thread safe
|
|
|
|
// we need to use a lock to prevent bug #51267
|
|
|
|
nsAutoLock lock(mLock);
|
|
|
|
|
2000-07-12 07:10:33 +04:00
|
|
|
NS_ENSURE_ARG_POINTER(result);
|
2003-03-14 21:59:51 +03:00
|
|
|
nsJARInputStream* jis = new nsJARInputStream();
|
2000-07-12 07:10:33 +04:00
|
|
|
if (!jis) return NS_ERROR_FAILURE;
|
|
|
|
|
2003-03-14 21:59:51 +03:00
|
|
|
// addref now so we can delete if the Init() fails
|
|
|
|
*result = NS_STATIC_CAST(nsIInputStream*,jis);
|
|
|
|
NS_ADDREF(*result);
|
|
|
|
|
|
|
|
nsresult rv;
|
2000-07-12 07:10:33 +04:00
|
|
|
rv = jis->Init(this, aFilename);
|
2003-03-14 21:59:51 +03:00
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
NS_RELEASE(*result);
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2000-07-12 07:10:33 +04:00
|
|
|
|
|
|
|
return NS_OK;
|
2000-01-29 03:03:57 +03:00
|
|
|
}
|
|
|
|
|
2001-02-23 03:15:04 +03:00
|
|
|
//----------------------------------------------
|
|
|
|
// nsIJAR implementation
|
|
|
|
//----------------------------------------------
|
|
|
|
|
2000-04-26 07:50:07 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJAR::GetCertificatePrincipal(const char* aFilename, nsIPrincipal** aPrincipal)
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2000-02-14 04:57:01 +03:00
|
|
|
//-- Parameter check
|
2000-03-21 07:21:28 +03:00
|
|
|
if (!aPrincipal)
|
2000-01-29 03:03:57 +03:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
2000-03-21 07:21:28 +03:00
|
|
|
*aPrincipal = nsnull;
|
2000-01-29 03:03:57 +03:00
|
|
|
|
2000-07-12 07:10:33 +04:00
|
|
|
//-- Get the signature verifier service
|
|
|
|
nsresult rv;
|
2001-07-25 11:54:28 +04:00
|
|
|
nsCOMPtr<nsISignatureVerifier> verifier =
|
|
|
|
do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
|
2000-07-12 07:10:33 +04:00
|
|
|
if (NS_FAILED(rv)) // No signature verifier available
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
//-- Parse the manifest
|
|
|
|
rv = ParseManifest(verifier);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
2001-02-23 03:15:04 +03:00
|
|
|
if (mGlobalStatus == nsIJAR::NO_MANIFEST)
|
2000-07-12 07:10:33 +04:00
|
|
|
return NS_OK;
|
|
|
|
|
2000-04-26 07:50:07 +04:00
|
|
|
PRInt16 requestedStatus;
|
|
|
|
if (aFilename)
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2000-04-26 07:50:07 +04:00
|
|
|
//-- Find the item
|
2000-08-10 10:19:37 +04:00
|
|
|
nsCStringKey key(aFilename);
|
2001-02-23 03:15:04 +03:00
|
|
|
nsJARManifestItem* manItem = NS_STATIC_CAST(nsJARManifestItem*, mManifestData.Get(&key));
|
2000-04-26 07:50:07 +04:00
|
|
|
if (!manItem)
|
|
|
|
return NS_OK;
|
2000-07-12 07:10:33 +04:00
|
|
|
//-- Verify the item against the manifest
|
|
|
|
if (!manItem->entryVerified)
|
2000-04-26 07:50:07 +04:00
|
|
|
{
|
2000-07-12 07:10:33 +04:00
|
|
|
nsXPIDLCString entryData;
|
|
|
|
PRUint32 entryDataLen;
|
|
|
|
rv = LoadEntry(aFilename, getter_Copies(entryData), &entryDataLen);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = VerifyEntry(verifier, manItem, entryData, entryDataLen);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
2000-04-26 07:50:07 +04:00
|
|
|
}
|
|
|
|
requestedStatus = manItem->status;
|
2000-01-29 03:03:57 +03:00
|
|
|
}
|
2000-04-26 07:50:07 +04:00
|
|
|
else // User wants identity of signer w/o verifying any entries
|
|
|
|
requestedStatus = mGlobalStatus;
|
2000-01-29 03:03:57 +03:00
|
|
|
|
2001-02-23 03:15:04 +03:00
|
|
|
if (requestedStatus != nsIJAR::VALID)
|
2000-04-26 07:50:07 +04:00
|
|
|
ReportError(aFilename, requestedStatus);
|
|
|
|
else // Valid signature
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2000-04-26 07:50:07 +04:00
|
|
|
*aPrincipal = mPrincipal;
|
2000-03-21 07:21:28 +03:00
|
|
|
NS_IF_ADDREF(*aPrincipal);
|
2000-02-14 04:57:01 +03:00
|
|
|
}
|
2000-03-21 07:21:28 +03:00
|
|
|
return NS_OK;
|
2000-05-10 05:49:33 +04:00
|
|
|
}
|
|
|
|
|
2002-12-14 01:24:12 +03:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJAR::GetManifestEntriesCount(PRUint32* count)
|
|
|
|
{
|
|
|
|
*count = mTotalItemsInManifest;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2003-03-14 21:59:51 +03:00
|
|
|
PRFileDesc*
|
|
|
|
nsJAR::OpenFile()
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(mZipFile, &rv);
|
|
|
|
if (NS_FAILED(rv)) return nsnull;
|
|
|
|
|
|
|
|
PRFileDesc* fd;
|
|
|
|
rv = localFile->OpenNSPRFileDesc(PR_RDONLY, 0664, &fd);
|
|
|
|
if (NS_FAILED(rv)) return nsnull;
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2000-01-29 03:03:57 +03:00
|
|
|
//----------------------------------------------
|
|
|
|
// nsJAR private implementation
|
|
|
|
//----------------------------------------------
|
2000-02-14 04:57:01 +03:00
|
|
|
nsresult
|
2000-03-30 12:09:45 +04:00
|
|
|
nsJAR::LoadEntry(const char* aFilename, char** aBuf, PRUint32* aBufLen)
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2000-07-12 07:10:33 +04:00
|
|
|
//-- Get a stream for reading the file
|
2000-01-29 03:03:57 +03:00
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIInputStream> manifestStream;
|
2000-07-12 07:10:33 +04:00
|
|
|
rv = GetInputStream(aFilename, getter_AddRefs(manifestStream));
|
2000-01-29 03:03:57 +03:00
|
|
|
if (NS_FAILED(rv)) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
|
|
|
|
|
|
|
|
//-- Read the manifest file into memory
|
|
|
|
char* buf;
|
|
|
|
PRUint32 len;
|
|
|
|
rv = manifestStream->Available(&len);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
2002-10-31 09:23:52 +03:00
|
|
|
if (len == PRUint32(-1))
|
|
|
|
return NS_ERROR_FILE_CORRUPTED; // bug 164695
|
2000-02-14 04:57:01 +03:00
|
|
|
buf = (char*)PR_MALLOC(len+1);
|
2000-01-29 03:03:57 +03:00
|
|
|
if (!buf) return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
PRUint32 bytesRead;
|
|
|
|
rv = manifestStream->Read(buf, len, &bytesRead);
|
|
|
|
if (bytesRead != len)
|
|
|
|
rv = NS_ERROR_FILE_CORRUPTED;
|
2002-10-31 09:23:52 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
PR_FREEIF(buf);
|
|
|
|
return rv;
|
|
|
|
}
|
2000-02-14 04:57:01 +03:00
|
|
|
buf[len] = '\0'; //Null-terminate the buffer
|
2000-01-29 03:03:57 +03:00
|
|
|
*aBuf = buf;
|
|
|
|
if (aBufLen)
|
|
|
|
*aBufLen = len;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-02-14 04:57:01 +03:00
|
|
|
PRInt32
|
|
|
|
nsJAR::ReadLine(const char** src)
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
|
|
|
//--Moves pointer to beginning of next line and returns line length
|
|
|
|
// not including CR/LF.
|
|
|
|
PRInt32 length;
|
|
|
|
char* eol = PL_strpbrk(*src, "\r\n");
|
|
|
|
|
|
|
|
if (eol == nsnull) // Probably reached end of file before newline
|
|
|
|
{
|
|
|
|
length = PL_strlen(*src);
|
|
|
|
if (length == 0) // immediate end-of-file
|
|
|
|
*src = nsnull;
|
|
|
|
else // some data left on this line
|
|
|
|
*src += length;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
length = eol - *src;
|
|
|
|
if (eol[0] == '\r' && eol[1] == '\n') // CR LF, so skip 2
|
|
|
|
*src = eol+2;
|
|
|
|
else // Either CR or LF, so skip 1
|
|
|
|
*src = eol+1;
|
|
|
|
}
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2000-04-26 07:50:07 +04:00
|
|
|
//-- The following #defines are used by ParseManifest()
|
|
|
|
// and ParseOneFile(). The header strings are defined in the JAR specification.
|
|
|
|
#define JAR_MF 1
|
|
|
|
#define JAR_SF 2
|
|
|
|
#define JAR_MF_SEARCH_STRING "(M|/M)ETA-INF/(M|m)(ANIFEST|anifest).(MF|mf)$"
|
|
|
|
#define JAR_SF_SEARCH_STRING "(M|/M)ETA-INF/*.(SF|sf)$"
|
|
|
|
#define JAR_MF_HEADER (const char*)"Manifest-Version: 1.0"
|
|
|
|
#define JAR_SF_HEADER (const char*)"Signature-Version: 1.0"
|
|
|
|
|
|
|
|
nsresult
|
2000-05-10 05:49:33 +04:00
|
|
|
nsJAR::ParseManifest(nsISignatureVerifier* verifier)
|
2000-04-26 07:50:07 +04:00
|
|
|
{
|
|
|
|
//-- Verification Step 1
|
|
|
|
if (mParsedManifest)
|
|
|
|
return NS_OK;
|
|
|
|
//-- (1)Manifest (MF) file
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsISimpleEnumerator> files;
|
|
|
|
rv = FindEntries(JAR_MF_SEARCH_STRING, getter_AddRefs(files));
|
|
|
|
if (!files) rv = NS_ERROR_FAILURE;
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
//-- Load the file into memory
|
|
|
|
nsCOMPtr<nsJARItem> file;
|
|
|
|
rv = files->GetNext(getter_AddRefs(file));
|
2000-07-12 07:10:33 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!file)
|
|
|
|
{
|
2001-02-23 03:15:04 +03:00
|
|
|
mGlobalStatus = nsIJAR::NO_MANIFEST;
|
2000-07-12 07:10:33 +04:00
|
|
|
mParsedManifest = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2000-04-26 07:50:07 +04:00
|
|
|
PRBool more;
|
|
|
|
rv = files->HasMoreElements(&more);
|
2000-07-12 07:10:33 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (more)
|
|
|
|
{
|
|
|
|
mParsedManifest = PR_TRUE;
|
|
|
|
return NS_ERROR_FILE_CORRUPTED; // More than one MF file
|
|
|
|
}
|
2000-04-26 07:50:07 +04:00
|
|
|
nsXPIDLCString manifestFilename;
|
|
|
|
rv = file->GetName(getter_Copies(manifestFilename));
|
|
|
|
if (!manifestFilename || NS_FAILED(rv)) return rv;
|
|
|
|
nsXPIDLCString manifestBuffer;
|
|
|
|
rv = LoadEntry(manifestFilename, getter_Copies(manifestBuffer));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
//-- Parse it
|
2000-05-10 05:49:33 +04:00
|
|
|
rv = ParseOneFile(verifier, manifestBuffer, JAR_MF);
|
2000-04-26 07:50:07 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
//-- (2)Signature (SF) file
|
|
|
|
// If there are multiple signatures, we select one.
|
|
|
|
rv = FindEntries(JAR_SF_SEARCH_STRING, getter_AddRefs(files));
|
|
|
|
if (!files) rv = NS_ERROR_FAILURE;
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
//-- Get an SF file
|
|
|
|
rv = files->GetNext(getter_AddRefs(file));
|
2000-07-12 07:10:33 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!file)
|
|
|
|
{
|
2001-02-23 03:15:04 +03:00
|
|
|
mGlobalStatus = nsIJAR::NO_MANIFEST;
|
2000-07-12 07:10:33 +04:00
|
|
|
mParsedManifest = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2000-04-26 07:50:07 +04:00
|
|
|
rv = file->GetName(getter_Copies(manifestFilename));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
PRUint32 manifestLen;
|
|
|
|
rv = LoadEntry(manifestFilename, getter_Copies(manifestBuffer), &manifestLen);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
//-- Get its corresponding signature file
|
2000-05-13 04:22:38 +04:00
|
|
|
nsCAutoString sigFilename( NS_STATIC_CAST(const char*, manifestFilename) );
|
2000-04-26 07:50:07 +04:00
|
|
|
PRInt32 extension = sigFilename.RFindChar('.') + 1;
|
|
|
|
NS_ASSERTION(extension != 0, "Manifest Parser: Missing file extension.");
|
|
|
|
(void)sigFilename.Cut(extension, 2);
|
|
|
|
nsXPIDLCString sigBuffer;
|
|
|
|
PRUint32 sigLen;
|
2000-05-12 11:53:02 +04:00
|
|
|
{
|
|
|
|
nsCAutoString tempFilename(sigFilename); tempFilename.Append("rsa", 3);
|
2001-10-25 10:13:52 +04:00
|
|
|
rv = LoadEntry(tempFilename.get(), getter_Copies(sigBuffer), &sigLen);
|
2000-05-12 11:53:02 +04:00
|
|
|
}
|
2000-04-26 07:50:07 +04:00
|
|
|
if (NS_FAILED(rv))
|
Bit checkin for bug 68045, r/sr=waterson&shaver, second attempt. It all works
for me on optimized and debug gcc2.96, rh7.1.
- Better failure codes from nsXULPrototypeScript::Deserialize.
- Call nsXULDocument::AbortFastLoads after nsXULPrototypeScript::Serialize
failure, instead of just nulling the FastLoad service's output stream.
- Expose nsXULDocument::AbortFastLoads via nsIXULPrototypeCache, for use from
nsChromeProtocolHandler.cpp. AbortFastLoads flushes the XUL cache now, for
good measure.
- The needless "Current" adjective in nsIFastLoadService attribute and method
names is no more.
- Add a do_GetFastLoadService() helper, to use CID instead of contractid, and
to let the compiler consolidate the static inline CID.
- Add "nglayout.debug.checksum_xul_fastload_file" pref so people can do without
the checksum verification step when reading a FastLoad file.
- Verify the FastLoad file checksum, by default. Also, cache it in the FastLoad
service so we don't recompute it when re-opening the FastLoad file as mailnews
and other top-levels start up. Fill the checksum cache in EndFastLoad, when
the last pseudo-concurrent top-level finishes loading.
My hope to compute the checksum while writing the FastLoad file ran afoul of
misordered writes. The old code to checksum the in-memory nsFastLoadHeader
also was broken on little endian platforms. Now all checksumming is done via
a separate read pass over the complete file, save for the header's checksum
field, which is summed as if it contained zero.
- Track and check FastLoad file dependencies. This required groveling with a
bunch of Necko interfaces in nsChromeProtocolHandler::NewChannel -- read it
and weep. Dependency checking, as well as checksum access and computation,
use better-factored nsIFastLoad{File,Read,Write}Control interfaces.
- nsBufferedStream::Seek wasn't flushing the buffer when seeking backward
within the buffer, but it must, because mCursor bounds the amount to write
if the buffer contains the end of file.
- Add an unbufferedStream readonly attribute to nsIStreamBufferAccess, so we
don't have to screw around with the bufferying layer when checksumming. Also
implement nsIStreamBufferAccess in nsBufferedOutputStream.
- nsISeekableOutputStream was bogus, based on a bad state I had put the
nsBufferedOutputStream code in on its way from being completely broken when
you seek backwards outside of the buffer. Removing this interface required
using nsIFastLoadFileIO in nsFastLoadFileWriter, and it also required careful
ordering of Close calls (the Reader must close after the Writer or Updater,
so that the Reader's underlying, unbuffered input stream can be read by
nsFastLoadFileWriter::Close to compute the checksum.
- Miscellaneous tab/indentation, comment typo, bracing, if( => if ( style,
nsnull vs. 0, useless variable elimination, tortured control flow,
AutoString instead of String, and gratuitous ; after nsISupportsUtils.h
macro call cleanups.
2001-08-22 00:51:34 +04:00
|
|
|
{
|
|
|
|
nsCAutoString tempFilename(sigFilename); tempFilename.Append("RSA", 3);
|
2001-10-25 10:13:52 +04:00
|
|
|
rv = LoadEntry(tempFilename.get(), getter_Copies(sigBuffer), &sigLen);
|
Bit checkin for bug 68045, r/sr=waterson&shaver, second attempt. It all works
for me on optimized and debug gcc2.96, rh7.1.
- Better failure codes from nsXULPrototypeScript::Deserialize.
- Call nsXULDocument::AbortFastLoads after nsXULPrototypeScript::Serialize
failure, instead of just nulling the FastLoad service's output stream.
- Expose nsXULDocument::AbortFastLoads via nsIXULPrototypeCache, for use from
nsChromeProtocolHandler.cpp. AbortFastLoads flushes the XUL cache now, for
good measure.
- The needless "Current" adjective in nsIFastLoadService attribute and method
names is no more.
- Add a do_GetFastLoadService() helper, to use CID instead of contractid, and
to let the compiler consolidate the static inline CID.
- Add "nglayout.debug.checksum_xul_fastload_file" pref so people can do without
the checksum verification step when reading a FastLoad file.
- Verify the FastLoad file checksum, by default. Also, cache it in the FastLoad
service so we don't recompute it when re-opening the FastLoad file as mailnews
and other top-levels start up. Fill the checksum cache in EndFastLoad, when
the last pseudo-concurrent top-level finishes loading.
My hope to compute the checksum while writing the FastLoad file ran afoul of
misordered writes. The old code to checksum the in-memory nsFastLoadHeader
also was broken on little endian platforms. Now all checksumming is done via
a separate read pass over the complete file, save for the header's checksum
field, which is summed as if it contained zero.
- Track and check FastLoad file dependencies. This required groveling with a
bunch of Necko interfaces in nsChromeProtocolHandler::NewChannel -- read it
and weep. Dependency checking, as well as checksum access and computation,
use better-factored nsIFastLoad{File,Read,Write}Control interfaces.
- nsBufferedStream::Seek wasn't flushing the buffer when seeking backward
within the buffer, but it must, because mCursor bounds the amount to write
if the buffer contains the end of file.
- Add an unbufferedStream readonly attribute to nsIStreamBufferAccess, so we
don't have to screw around with the bufferying layer when checksumming. Also
implement nsIStreamBufferAccess in nsBufferedOutputStream.
- nsISeekableOutputStream was bogus, based on a bad state I had put the
nsBufferedOutputStream code in on its way from being completely broken when
you seek backwards outside of the buffer. Removing this interface required
using nsIFastLoadFileIO in nsFastLoadFileWriter, and it also required careful
ordering of Close calls (the Reader must close after the Writer or Updater,
so that the Reader's underlying, unbuffered input stream can be read by
nsFastLoadFileWriter::Close to compute the checksum.
- Miscellaneous tab/indentation, comment typo, bracing, if( => if ( style,
nsnull vs. 0, useless variable elimination, tortured control flow,
AutoString instead of String, and gratuitous ; after nsISupportsUtils.h
macro call cleanups.
2001-08-22 00:51:34 +04:00
|
|
|
}
|
2000-07-12 07:10:33 +04:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
{
|
2001-02-23 03:15:04 +03:00
|
|
|
mGlobalStatus = nsIJAR::NO_MANIFEST;
|
2000-07-12 07:10:33 +04:00
|
|
|
mParsedManifest = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2002-12-14 01:24:12 +03:00
|
|
|
|
2000-04-26 07:50:07 +04:00
|
|
|
//-- Verify that the signature file is a valid signature of the SF file
|
2000-05-10 05:49:33 +04:00
|
|
|
PRInt32 verifyError;
|
|
|
|
rv = verifier->VerifySignature(sigBuffer, sigLen, manifestBuffer, manifestLen,
|
|
|
|
&verifyError, getter_AddRefs(mPrincipal));
|
2000-04-26 07:50:07 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
2002-12-14 01:24:12 +03:00
|
|
|
if (mPrincipal && verifyError == 0)
|
2001-02-23 03:15:04 +03:00
|
|
|
mGlobalStatus = nsIJAR::VALID;
|
2000-05-10 05:49:33 +04:00
|
|
|
else if (verifyError == nsISignatureVerifier::VERIFY_ERROR_UNKNOWN_CA)
|
2001-02-23 03:15:04 +03:00
|
|
|
mGlobalStatus = nsIJAR::INVALID_UNKNOWN_CA;
|
2000-05-10 05:49:33 +04:00
|
|
|
else
|
2001-02-23 03:15:04 +03:00
|
|
|
mGlobalStatus = nsIJAR::INVALID_SIG;
|
2000-05-10 05:49:33 +04:00
|
|
|
|
2000-04-26 07:50:07 +04:00
|
|
|
//-- Parse the SF file. If the verification above failed, principal
|
|
|
|
// is null, and ParseOneFile will mark the relevant entries as invalid.
|
|
|
|
// if ParseOneFile fails, then it has no effect, and we can safely
|
|
|
|
// continue to the next SF file, or return.
|
2000-05-10 05:49:33 +04:00
|
|
|
ParseOneFile(verifier, manifestBuffer, JAR_SF);
|
2000-07-12 07:10:33 +04:00
|
|
|
mParsedManifest = PR_TRUE;
|
2000-04-26 07:50:07 +04:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-02-14 04:57:01 +03:00
|
|
|
nsresult
|
2000-05-10 05:49:33 +04:00
|
|
|
nsJAR::ParseOneFile(nsISignatureVerifier* verifier,
|
|
|
|
const char* filebuf, PRInt16 aFileType)
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2000-03-21 07:21:28 +03:00
|
|
|
//-- Check file header
|
2000-01-29 03:03:57 +03:00
|
|
|
const char* nextLineStart = filebuf;
|
|
|
|
nsCAutoString curLine;
|
|
|
|
PRInt32 linelen;
|
|
|
|
linelen = ReadLine(&nextLineStart);
|
2000-03-21 07:21:28 +03:00
|
|
|
curLine.Assign(filebuf, linelen);
|
2000-09-03 09:54:31 +04:00
|
|
|
|
|
|
|
if ( ((aFileType == JAR_MF) && !curLine.Equals(JAR_MF_HEADER) ) ||
|
|
|
|
((aFileType == JAR_SF) && !curLine.Equals(JAR_SF_HEADER) ) )
|
2000-01-29 03:03:57 +03:00
|
|
|
return NS_ERROR_FILE_CORRUPTED;
|
|
|
|
|
2000-03-21 07:21:28 +03:00
|
|
|
//-- Skip header section
|
|
|
|
do {
|
|
|
|
linelen = ReadLine(&nextLineStart);
|
|
|
|
} while (linelen > 0);
|
|
|
|
|
|
|
|
//-- Set up parsing variables
|
|
|
|
const char* curPos;
|
|
|
|
const char* sectionStart = nextLineStart;
|
|
|
|
|
2002-08-06 04:51:46 +04:00
|
|
|
nsJARManifestItem* curItemMF = nsnull;
|
2000-03-21 07:21:28 +03:00
|
|
|
PRBool foundName = PR_FALSE;
|
|
|
|
if (aFileType == JAR_MF)
|
|
|
|
curItemMF = new nsJARManifestItem();
|
|
|
|
nsCAutoString curItemName;
|
|
|
|
nsCAutoString storedSectionDigest;
|
|
|
|
|
2000-01-29 03:03:57 +03:00
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
curPos = nextLineStart;
|
|
|
|
linelen = ReadLine(&nextLineStart);
|
|
|
|
curLine.Assign(curPos, linelen);
|
|
|
|
if (linelen == 0)
|
|
|
|
// end of section (blank line or end-of-file)
|
|
|
|
{
|
|
|
|
if (aFileType == JAR_MF)
|
|
|
|
{
|
2002-12-14 01:24:12 +03:00
|
|
|
mTotalItemsInManifest++;
|
2000-03-21 07:21:28 +03:00
|
|
|
if (curItemMF->mType != JAR_INVALID)
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2000-02-14 04:57:01 +03:00
|
|
|
//-- Did this section have a name: line?
|
2000-01-29 03:03:57 +03:00
|
|
|
if(!foundName)
|
2000-03-21 07:21:28 +03:00
|
|
|
curItemMF->mType = JAR_INVALID;
|
2000-01-29 03:03:57 +03:00
|
|
|
else
|
|
|
|
{
|
2000-03-21 07:21:28 +03:00
|
|
|
if (curItemMF->mType == JAR_INTERNAL)
|
|
|
|
{
|
2000-01-29 03:03:57 +03:00
|
|
|
//-- If it's an internal item, it must correspond
|
|
|
|
// to a valid jar entry
|
|
|
|
nsIZipEntry* entry;
|
2001-10-25 10:13:52 +04:00
|
|
|
PRInt32 result = GetEntry(curItemName.get(), &entry);
|
2000-01-29 03:03:57 +03:00
|
|
|
if (result != ZIP_OK || !entry)
|
2000-03-21 07:21:28 +03:00
|
|
|
curItemMF->mType = JAR_INVALID;
|
2000-01-29 03:03:57 +03:00
|
|
|
}
|
|
|
|
//-- Check for duplicates
|
2000-08-10 10:19:37 +04:00
|
|
|
nsCStringKey key(curItemName);
|
2000-01-29 03:03:57 +03:00
|
|
|
if (mManifestData.Exists(&key))
|
2000-03-21 07:21:28 +03:00
|
|
|
curItemMF->mType = JAR_INVALID;
|
2000-01-29 03:03:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-03-21 07:21:28 +03:00
|
|
|
if (curItemMF->mType == JAR_INVALID)
|
|
|
|
delete curItemMF;
|
2000-02-14 04:57:01 +03:00
|
|
|
else //-- calculate section digest
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
|
|
|
PRUint32 sectionLength = curPos - sectionStart;
|
2000-05-10 05:49:33 +04:00
|
|
|
CalculateDigest(verifier, sectionStart, sectionLength,
|
2000-03-21 07:21:28 +03:00
|
|
|
&(curItemMF->calculatedSectionDigest));
|
2000-01-29 03:03:57 +03:00
|
|
|
//-- Save item in the hashtable
|
2000-08-10 10:19:37 +04:00
|
|
|
nsCStringKey itemKey(curItemName);
|
2000-03-21 07:21:28 +03:00
|
|
|
mManifestData.Put(&itemKey, (void*)curItemMF);
|
2000-01-29 03:03:57 +03:00
|
|
|
}
|
|
|
|
if (nextLineStart == nsnull) // end-of-file
|
|
|
|
break;
|
|
|
|
|
|
|
|
sectionStart = nextLineStart;
|
2000-03-21 07:21:28 +03:00
|
|
|
curItemMF = new nsJARManifestItem();
|
2000-01-29 03:03:57 +03:00
|
|
|
} // (aFileType == JAR_MF)
|
|
|
|
else
|
|
|
|
//-- file type is SF, compare digest with calculated
|
|
|
|
// section digests from MF file.
|
|
|
|
{
|
|
|
|
if (foundName)
|
|
|
|
{
|
2000-03-21 07:21:28 +03:00
|
|
|
nsJARManifestItem* curItemSF;
|
2000-08-10 10:19:37 +04:00
|
|
|
nsCStringKey key(curItemName);
|
2000-03-21 07:21:28 +03:00
|
|
|
curItemSF = (nsJARManifestItem*)mManifestData.Get(&key);
|
|
|
|
if(curItemSF)
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2000-03-21 07:21:28 +03:00
|
|
|
NS_ASSERTION(curItemSF->status == nsJAR::NOT_SIGNED,
|
|
|
|
"SECURITY ERROR: nsJARManifestItem not correctly initialized");
|
2000-04-26 07:50:07 +04:00
|
|
|
curItemSF->status = mGlobalStatus;
|
2001-02-23 03:15:04 +03:00
|
|
|
if (curItemSF->status == nsIJAR::VALID)
|
2000-03-21 07:21:28 +03:00
|
|
|
{ // Compare digests
|
2003-05-24 01:34:47 +04:00
|
|
|
if (storedSectionDigest.IsEmpty())
|
2001-02-23 03:15:04 +03:00
|
|
|
curItemSF->status = nsIJAR::NOT_SIGNED;
|
2000-03-21 07:21:28 +03:00
|
|
|
else
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2000-09-03 09:54:31 +04:00
|
|
|
if (!storedSectionDigest.Equals((const char*)curItemSF->calculatedSectionDigest))
|
2001-02-23 03:15:04 +03:00
|
|
|
curItemSF->status = nsIJAR::INVALID_MANIFEST;
|
2000-03-21 07:21:28 +03:00
|
|
|
JAR_NULLFREE(curItemSF->calculatedSectionDigest)
|
2000-02-14 04:57:01 +03:00
|
|
|
storedSectionDigest = "";
|
2000-01-29 03:03:57 +03:00
|
|
|
}
|
2000-02-14 04:57:01 +03:00
|
|
|
} // (aPrincipal != nsnull)
|
2000-03-21 07:21:28 +03:00
|
|
|
} // if(curItemSF)
|
2000-01-29 03:03:57 +03:00
|
|
|
} // if(foundName)
|
|
|
|
|
|
|
|
if(nextLineStart == nsnull) // end-of-file
|
|
|
|
break;
|
|
|
|
} // aFileType == JAR_SF
|
|
|
|
foundName = PR_FALSE;
|
|
|
|
continue;
|
|
|
|
} // if(linelen == 0)
|
|
|
|
|
|
|
|
//-- Look for continuations (beginning with a space) on subsequent lines
|
|
|
|
// and append them to the current line.
|
|
|
|
while(*nextLineStart == ' ')
|
|
|
|
{
|
|
|
|
curPos = nextLineStart;
|
|
|
|
PRInt32 continuationLen = ReadLine(&nextLineStart) - 1;
|
2000-02-14 04:57:01 +03:00
|
|
|
nsCAutoString continuation(curPos+1, continuationLen);
|
2000-01-29 03:03:57 +03:00
|
|
|
curLine += continuation;
|
|
|
|
linelen += continuationLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-- Find colon in current line, this separates name from value
|
|
|
|
PRInt32 colonPos = curLine.FindChar(':');
|
|
|
|
if (colonPos == -1) // No colon on line, ignore line
|
|
|
|
continue;
|
|
|
|
//-- Break down the line
|
|
|
|
nsCAutoString lineName;
|
|
|
|
curLine.Left(lineName, colonPos);
|
|
|
|
nsCAutoString lineData;
|
|
|
|
curLine.Mid(lineData, colonPos+2, linelen - (colonPos+2));
|
|
|
|
|
|
|
|
//-- Lines to look for:
|
2000-02-14 04:57:01 +03:00
|
|
|
// (1) Digest:
|
2002-02-10 18:49:46 +03:00
|
|
|
if (lineName.Equals(NS_LITERAL_CSTRING("SHA1-Digest"),
|
|
|
|
nsCaseInsensitiveCStringComparator()))
|
2000-01-29 03:03:57 +03:00
|
|
|
//-- This is a digest line, save the data in the appropriate place
|
|
|
|
{
|
|
|
|
if(aFileType == JAR_MF)
|
|
|
|
{
|
2000-03-21 07:21:28 +03:00
|
|
|
curItemMF->storedEntryDigest = (char*)PR_MALLOC(lineData.Length()+1);
|
|
|
|
if (!(curItemMF->storedEntryDigest))
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
2001-10-25 10:13:52 +04:00
|
|
|
PL_strcpy(curItemMF->storedEntryDigest, lineData.get());
|
2000-01-29 03:03:57 +03:00
|
|
|
}
|
|
|
|
else
|
2000-02-14 04:57:01 +03:00
|
|
|
storedSectionDigest = lineData;
|
2000-01-29 03:03:57 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// (2) Name: associates this manifest section with a file in the jar.
|
2002-02-10 18:49:46 +03:00
|
|
|
if (!foundName && lineName.Equals(NS_LITERAL_CSTRING("Name"),
|
|
|
|
nsCaseInsensitiveCStringComparator()))
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2000-03-21 07:21:28 +03:00
|
|
|
curItemName = lineData;
|
2000-01-29 03:03:57 +03:00
|
|
|
foundName = PR_TRUE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// (3) Magic: this may be an inline Javascript.
|
|
|
|
// We can't do any other kind of magic.
|
|
|
|
if ( aFileType == JAR_MF &&
|
2002-02-10 18:49:46 +03:00
|
|
|
lineName.Equals(NS_LITERAL_CSTRING("Magic"),
|
|
|
|
nsCaseInsensitiveCStringComparator()))
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
2002-02-10 18:49:46 +03:00
|
|
|
if(lineData.Equals(NS_LITERAL_CSTRING("javascript"),
|
|
|
|
nsCaseInsensitiveCStringComparator()))
|
2000-03-21 07:21:28 +03:00
|
|
|
curItemMF->mType = JAR_EXTERNAL;
|
2000-01-29 03:03:57 +03:00
|
|
|
else
|
2000-03-21 07:21:28 +03:00
|
|
|
curItemMF->mType = JAR_INVALID;
|
2000-01-29 03:03:57 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // for (;;)
|
1999-10-26 23:43:26 +04:00
|
|
|
return NS_OK;
|
2000-01-29 03:03:57 +03:00
|
|
|
} //ParseOneFile()
|
|
|
|
|
2000-02-14 04:57:01 +03:00
|
|
|
nsresult
|
2000-07-12 07:10:33 +04:00
|
|
|
nsJAR::VerifyEntry(nsISignatureVerifier* verifier,
|
|
|
|
nsJARManifestItem* aManItem, const char* aEntryData,
|
2000-02-14 04:57:01 +03:00
|
|
|
PRUint32 aLen)
|
|
|
|
{
|
2001-02-23 03:15:04 +03:00
|
|
|
if (aManItem->status == nsIJAR::VALID)
|
2000-02-14 04:57:01 +03:00
|
|
|
{
|
2000-07-12 07:10:33 +04:00
|
|
|
if(!aManItem->storedEntryDigest)
|
2000-03-21 07:21:28 +03:00
|
|
|
// No entry digests in manifest file. Entry is unsigned.
|
2001-02-23 03:15:04 +03:00
|
|
|
aManItem->status = nsIJAR::NOT_SIGNED;
|
2000-02-14 04:57:01 +03:00
|
|
|
else
|
|
|
|
{ //-- Calculate and compare digests
|
|
|
|
char* calculatedEntryDigest;
|
2000-07-12 07:10:33 +04:00
|
|
|
nsresult rv = CalculateDigest(verifier, aEntryData, aLen, &calculatedEntryDigest);
|
2000-05-10 05:49:33 +04:00
|
|
|
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
|
2000-07-12 07:10:33 +04:00
|
|
|
if (PL_strcmp(aManItem->storedEntryDigest, calculatedEntryDigest) != 0)
|
2001-02-23 03:15:04 +03:00
|
|
|
aManItem->status = nsIJAR::INVALID_ENTRY;
|
2000-02-14 04:57:01 +03:00
|
|
|
JAR_NULLFREE(calculatedEntryDigest)
|
2000-07-12 07:10:33 +04:00
|
|
|
JAR_NULLFREE(aManItem->storedEntryDigest)
|
2000-02-14 04:57:01 +03:00
|
|
|
}
|
|
|
|
}
|
2000-07-12 07:10:33 +04:00
|
|
|
aManItem->entryVerified = PR_TRUE;
|
|
|
|
return NS_OK;
|
2000-03-28 07:38:06 +04:00
|
|
|
}
|
|
|
|
|
2000-04-26 07:50:07 +04:00
|
|
|
void nsJAR::ReportError(const char* aFilename, PRInt16 errorCode)
|
|
|
|
{
|
|
|
|
//-- Generate error message
|
2000-05-10 05:49:33 +04:00
|
|
|
nsAutoString message;
|
2001-12-16 14:58:03 +03:00
|
|
|
message.Assign(NS_LITERAL_STRING("Signature Verification Error: the signature on "));
|
2000-04-26 07:50:07 +04:00
|
|
|
if (aFilename)
|
2000-04-27 23:49:53 +04:00
|
|
|
message.AppendWithConversion(aFilename);
|
2000-04-26 07:50:07 +04:00
|
|
|
else
|
2001-12-16 14:58:03 +03:00
|
|
|
message.Append(NS_LITERAL_STRING("this .jar archive"));
|
|
|
|
message.Append(NS_LITERAL_STRING(" is invalid because "));
|
2000-04-26 07:50:07 +04:00
|
|
|
switch(errorCode)
|
|
|
|
{
|
2001-02-23 03:15:04 +03:00
|
|
|
case nsIJAR::NOT_SIGNED:
|
2001-12-16 14:58:03 +03:00
|
|
|
message.Append(NS_LITERAL_STRING("the archive did not contain a valid PKCS7 signature."));
|
2000-04-26 07:50:07 +04:00
|
|
|
break;
|
2001-02-23 03:15:04 +03:00
|
|
|
case nsIJAR::INVALID_SIG:
|
2001-12-16 14:58:03 +03:00
|
|
|
message.Append(NS_LITERAL_STRING("the digital signature (*.RSA) file is not a valid signature of the signature instruction file (*.SF)."));
|
2000-04-26 07:50:07 +04:00
|
|
|
break;
|
2001-02-23 03:15:04 +03:00
|
|
|
case nsIJAR::INVALID_UNKNOWN_CA:
|
2001-12-16 14:58:03 +03:00
|
|
|
message.Append(NS_LITERAL_STRING("the certificate used to sign this file has an unrecognized issuer."));
|
2000-04-26 07:50:07 +04:00
|
|
|
break;
|
2001-02-23 03:15:04 +03:00
|
|
|
case nsIJAR::INVALID_MANIFEST:
|
2001-12-16 14:58:03 +03:00
|
|
|
message.Append(NS_LITERAL_STRING("the signature instruction file (*.SF) does not contain a valid hash of the MANIFEST.MF file."));
|
2000-04-26 07:50:07 +04:00
|
|
|
break;
|
2001-02-23 03:15:04 +03:00
|
|
|
case nsIJAR::INVALID_ENTRY:
|
2001-12-16 14:58:03 +03:00
|
|
|
message.Append(NS_LITERAL_STRING("the MANIFEST.MF file does not contain a valid hash of the file being verified."));
|
2000-04-26 07:50:07 +04:00
|
|
|
break;
|
|
|
|
default:
|
2001-12-16 14:58:03 +03:00
|
|
|
message.Append(NS_LITERAL_STRING("of an unknown problem."));
|
2000-04-26 07:50:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Report error in JS console
|
2000-09-15 03:20:49 +04:00
|
|
|
nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
|
2000-04-26 07:50:07 +04:00
|
|
|
if (console)
|
|
|
|
{
|
2001-09-29 12:28:41 +04:00
|
|
|
console->LogStringMessage(message.get());
|
2000-04-26 07:50:07 +04:00
|
|
|
}
|
2001-05-15 10:43:03 +04:00
|
|
|
#ifdef DEBUG
|
2001-09-29 12:28:41 +04:00
|
|
|
char* messageCstr = ToNewCString(message);
|
2001-05-15 10:43:03 +04:00
|
|
|
if (!messageCstr) return;
|
|
|
|
fprintf(stderr, "%s\n", messageCstr);
|
|
|
|
nsMemory::Free(messageCstr);
|
2000-05-10 05:49:33 +04:00
|
|
|
#endif
|
2000-04-26 07:50:07 +04:00
|
|
|
}
|
|
|
|
|
2000-03-21 07:21:28 +03:00
|
|
|
|
2000-05-10 05:49:33 +04:00
|
|
|
nsresult nsJAR::CalculateDigest(nsISignatureVerifier* verifier,
|
|
|
|
const char* aInBuf, PRUint32 aLen,
|
|
|
|
char** digest)
|
2000-03-21 07:21:28 +03:00
|
|
|
{
|
|
|
|
*digest = nsnull;
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
//-- Calculate the digest
|
2001-05-24 03:22:25 +04:00
|
|
|
HASHContextStr* id;
|
2000-05-10 05:49:33 +04:00
|
|
|
rv = verifier->HashBegin(nsISignatureVerifier::SHA1, &id);
|
2000-03-21 07:21:28 +03:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2000-05-10 05:49:33 +04:00
|
|
|
rv = verifier->HashUpdate(id, aInBuf, aLen);
|
2000-03-21 07:21:28 +03:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
PRUint32 len;
|
2000-05-10 05:49:33 +04:00
|
|
|
unsigned char* rawDigest = (unsigned char*)PR_MALLOC(nsISignatureVerifier::SHA1_LENGTH);
|
2000-03-21 07:21:28 +03:00
|
|
|
if (rawDigest == nsnull) return NS_ERROR_OUT_OF_MEMORY;
|
2000-05-10 05:49:33 +04:00
|
|
|
rv = verifier->HashEnd(id, &rawDigest, &len, nsISignatureVerifier::SHA1_LENGTH);
|
2000-03-21 07:21:28 +03:00
|
|
|
if (NS_FAILED(rv)) { PR_FREEIF(rawDigest); return rv; }
|
|
|
|
|
|
|
|
//-- Encode the digest in base64
|
2000-05-10 05:49:33 +04:00
|
|
|
*digest = PL_Base64Encode((char*)rawDigest, len, *digest);
|
2000-03-21 07:21:28 +03:00
|
|
|
if (!(*digest)) { PR_FREEIF(rawDigest); return NS_ERROR_OUT_OF_MEMORY; }
|
|
|
|
|
|
|
|
PR_FREEIF(rawDigest);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-01-29 03:03:57 +03:00
|
|
|
//----------------------------------------------
|
|
|
|
// Debugging functions
|
|
|
|
//----------------------------------------------
|
|
|
|
#if 0
|
|
|
|
PR_STATIC_CALLBACK(PRBool)
|
|
|
|
PrintManItem(nsHashKey* aKey, void* aData, void* closure)
|
|
|
|
{
|
|
|
|
nsJARManifestItem* manItem = (nsJARManifestItem*)aData;
|
|
|
|
if (manItem)
|
|
|
|
{
|
2000-08-10 10:19:37 +04:00
|
|
|
nsCStringKey* key2 = (nsCStringKey*)aKey;
|
2001-09-29 12:28:41 +04:00
|
|
|
char* name = ToNewCString(key2->GetString());
|
2000-07-12 07:10:33 +04:00
|
|
|
if (!(PL_strcmp(name, "") == 0))
|
2000-10-29 02:17:53 +04:00
|
|
|
printf("%s s=%i\n",name, manItem->status);
|
2000-01-29 03:03:57 +03:00
|
|
|
}
|
|
|
|
return PR_TRUE;
|
1999-10-26 23:43:26 +04:00
|
|
|
}
|
2000-01-29 03:03:57 +03:00
|
|
|
#endif
|
|
|
|
|
2000-02-14 04:57:01 +03:00
|
|
|
void nsJAR::DumpMetadata(const char* aMessage)
|
2000-01-29 03:03:57 +03:00
|
|
|
{
|
|
|
|
#if 0
|
2000-10-29 02:17:53 +04:00
|
|
|
printf("### nsJAR::DumpMetadata at %s ###\n", aMessage);
|
2000-07-12 07:10:33 +04:00
|
|
|
if (mPrincipal)
|
|
|
|
{
|
|
|
|
char* toStr;
|
|
|
|
mPrincipal->ToString(&toStr);
|
2000-10-29 02:17:53 +04:00
|
|
|
printf("Principal: %s.\n", toStr);
|
2000-07-12 07:10:33 +04:00
|
|
|
PR_FREEIF(toStr);
|
|
|
|
}
|
|
|
|
else
|
2000-10-29 02:17:53 +04:00
|
|
|
printf("No Principal. \n");
|
2000-01-29 03:03:57 +03:00
|
|
|
mManifestData.Enumerate(PrintManItem);
|
2000-10-29 02:17:53 +04:00
|
|
|
printf("\n");
|
2000-01-29 03:03:57 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
1999-06-23 10:16:28 +04:00
|
|
|
//----------------------------------------------
|
|
|
|
// nsJAREnumerator constructor and destructor
|
|
|
|
//----------------------------------------------
|
|
|
|
nsJAREnumerator::nsJAREnumerator(nsZipFind *aFind)
|
|
|
|
: mFind(aFind),
|
|
|
|
mCurr(nsnull),
|
|
|
|
mIsCurrStale(PR_TRUE)
|
|
|
|
{
|
|
|
|
mArchive = mFind->GetArchive();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsJAREnumerator::~nsJAREnumerator()
|
|
|
|
{
|
|
|
|
mArchive->FindFree(mFind);
|
|
|
|
}
|
|
|
|
|
2001-08-21 05:48:11 +04:00
|
|
|
NS_IMPL_ISUPPORTS1(nsJAREnumerator, nsISimpleEnumerator)
|
1999-06-23 10:16:28 +04:00
|
|
|
|
|
|
|
//----------------------------------------------
|
|
|
|
// nsJAREnumerator::HasMoreElements
|
|
|
|
//----------------------------------------------
|
1999-06-02 01:08:32 +04:00
|
|
|
NS_IMETHODIMP
|
1999-06-23 10:16:28 +04:00
|
|
|
nsJAREnumerator::HasMoreElements(PRBool* aResult)
|
1999-06-02 01:08:32 +04:00
|
|
|
{
|
1999-06-23 10:16:28 +04:00
|
|
|
PRInt32 err;
|
|
|
|
|
|
|
|
if (!mFind)
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
|
|
|
|
// try to get the next element
|
|
|
|
if (mIsCurrStale)
|
|
|
|
{
|
|
|
|
err = mArchive->FindNext( mFind, &mCurr );
|
|
|
|
if (err == ZIP_ERR_FNF)
|
|
|
|
{
|
|
|
|
*aResult = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
if (err != ZIP_OK)
|
|
|
|
return NS_ERROR_FAILURE; // no error translation
|
|
|
|
|
|
|
|
mIsCurrStale = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aResult = PR_TRUE;
|
|
|
|
return NS_OK;
|
1999-06-02 01:08:32 +04:00
|
|
|
}
|
|
|
|
|
1999-06-23 10:16:28 +04:00
|
|
|
//----------------------------------------------
|
|
|
|
// nsJAREnumerator::GetNext
|
|
|
|
//----------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJAREnumerator::GetNext(nsISupports** aResult)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
PRBool bMore;
|
|
|
|
|
|
|
|
// check if the current item is "stale"
|
|
|
|
if (mIsCurrStale)
|
|
|
|
{
|
|
|
|
rv = HasMoreElements( &bMore );
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
if (bMore == PR_FALSE)
|
|
|
|
{
|
|
|
|
*aResult = nsnull; // null return value indicates no more elements
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// pack into an nsIJARItem
|
1999-11-12 09:13:13 +03:00
|
|
|
nsJARItem* jarItem = new nsJARItem();
|
1999-07-16 03:06:52 +04:00
|
|
|
if(jarItem)
|
|
|
|
{
|
1999-11-12 09:13:13 +03:00
|
|
|
NS_ADDREF(jarItem);
|
2000-01-29 03:03:57 +03:00
|
|
|
jarItem->Init(mCurr);
|
1999-07-16 03:06:52 +04:00
|
|
|
*aResult = jarItem;
|
|
|
|
mIsCurrStale = PR_TRUE; // we just gave this one away
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
1999-06-23 10:16:28 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------
|
|
|
|
// nsJARItem constructors and destructor
|
|
|
|
//-------------------------------------------------
|
|
|
|
nsJARItem::nsJARItem()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsJARItem::~nsJARItem()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
1999-11-12 09:13:13 +03:00
|
|
|
NS_IMPL_ISUPPORTS1(nsJARItem, nsIZipEntry);
|
1999-06-23 10:16:28 +04:00
|
|
|
|
2000-01-29 03:03:57 +03:00
|
|
|
void nsJARItem::Init(nsZipItem* aZipItem)
|
|
|
|
{
|
|
|
|
mZipItem = aZipItem;
|
|
|
|
}
|
|
|
|
|
1999-06-23 10:16:28 +04:00
|
|
|
//------------------------------------------
|
|
|
|
// nsJARItem::GetName
|
|
|
|
//------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJARItem::GetName(char * *aName)
|
|
|
|
{
|
|
|
|
char *namedup;
|
|
|
|
|
|
|
|
if ( !aName )
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
2000-01-29 03:03:57 +03:00
|
|
|
if ( !mZipItem->name )
|
1999-06-23 10:16:28 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2001-12-07 04:16:20 +03:00
|
|
|
namedup = PL_strdup( mZipItem->name );
|
1999-06-23 10:16:28 +04:00
|
|
|
if ( !namedup )
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
|
|
*aName = namedup;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
// nsJARItem::GetCompression
|
|
|
|
//------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJARItem::GetCompression(PRUint16 *aCompression)
|
|
|
|
{
|
|
|
|
if (!aCompression)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
2000-01-29 03:03:57 +03:00
|
|
|
if (!mZipItem->compression)
|
1999-06-23 10:16:28 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2000-01-29 03:03:57 +03:00
|
|
|
*aCompression = mZipItem->compression;
|
1999-06-23 10:16:28 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
// nsJARItem::GetSize
|
|
|
|
//------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsJARItem::GetSize(PRUint32 *aSize)
|
|
|
|
{
|
|
|
|
if (!aSize)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
2000-01-29 03:03:57 +03:00
|
|
|
if (!mZipItem->size)
|
1999-06-23 10:16:28 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2000-01-29 03:03:57 +03:00
|
|
|
*aSize = mZipItem->size;
|
1999-06-23 10:16:28 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
// nsJARItem::GetRealSize
|
|
|
|
//------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
1999-11-12 09:13:13 +03:00
|
|
|
nsJARItem::GetRealSize(PRUint32 *aRealsize)
|
1999-06-23 10:16:28 +04:00
|
|
|
{
|
|
|
|
if (!aRealsize)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
2000-01-29 03:03:57 +03:00
|
|
|
if (!mZipItem->realsize)
|
1999-06-23 10:16:28 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2000-01-29 03:03:57 +03:00
|
|
|
*aRealsize = mZipItem->realsize;
|
1999-06-23 10:16:28 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------
|
|
|
|
// nsJARItem::GetCrc32
|
|
|
|
//------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
1999-11-12 09:13:13 +03:00
|
|
|
nsJARItem::GetCRC32(PRUint32 *aCrc32)
|
1999-06-23 10:16:28 +04:00
|
|
|
{
|
|
|
|
if (!aCrc32)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
2000-01-29 03:03:57 +03:00
|
|
|
if (!mZipItem->crc32)
|
1999-06-23 10:16:28 +04:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2000-01-29 03:03:57 +03:00
|
|
|
*aCrc32 = mZipItem->crc32;
|
1999-06-23 10:16:28 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2000-01-29 03:03:57 +03:00
|
|
|
|
2000-04-12 11:58:24 +04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIZipReaderCache
|
|
|
|
|
2000-11-01 01:44:20 +03:00
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS3(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference)
|
2000-04-12 11:58:24 +04:00
|
|
|
|
|
|
|
nsZipReaderCache::nsZipReaderCache()
|
2000-04-27 01:27:52 +04:00
|
|
|
: mLock(nsnull),
|
2000-11-01 01:44:20 +03:00
|
|
|
mZips(16)
|
2000-08-24 11:38:41 +04:00
|
|
|
#ifdef ZIP_CACHE_HIT_RATE
|
2000-11-01 01:44:20 +03:00
|
|
|
,
|
2000-08-24 11:38:41 +04:00
|
|
|
mZipCacheLookups(0),
|
|
|
|
mZipCacheHits(0),
|
|
|
|
mZipCacheFlushes(0),
|
2000-11-01 01:44:20 +03:00
|
|
|
mZipSyncMisses(0)
|
2000-08-24 11:38:41 +04:00
|
|
|
#endif
|
2000-04-12 11:58:24 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsZipReaderCache::Init(PRUint32 cacheSize)
|
|
|
|
{
|
2000-11-01 01:44:20 +03:00
|
|
|
nsresult rv;
|
|
|
|
mCacheSize = cacheSize;
|
|
|
|
|
|
|
|
// Register as a memory pressure observer
|
2001-07-25 11:54:28 +04:00
|
|
|
nsCOMPtr<nsIObserverService> os =
|
2001-10-23 02:01:27 +04:00
|
|
|
do_GetService("@mozilla.org/observer-service;1", &rv);
|
2000-11-01 01:44:20 +03:00
|
|
|
if (NS_SUCCEEDED(rv))
|
|
|
|
{
|
2001-10-26 04:09:22 +04:00
|
|
|
rv = os->AddObserver(this, "memory-pressure", PR_TRUE);
|
2000-11-01 01:44:20 +03:00
|
|
|
}
|
|
|
|
// ignore failure of the observer registration.
|
|
|
|
|
2000-04-12 11:58:24 +04:00
|
|
|
mLock = PR_NewLock();
|
|
|
|
return mLock ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
2000-08-24 00:39:17 +04:00
|
|
|
static PRBool PR_CALLBACK
|
2000-08-23 07:18:53 +04:00
|
|
|
DropZipReaderCache(nsHashKey *aKey, void *aData, void* closure)
|
|
|
|
{
|
|
|
|
nsJAR* zip = (nsJAR*)aData;
|
|
|
|
zip->SetZipReaderCache(nsnull);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2000-04-12 11:58:24 +04:00
|
|
|
nsZipReaderCache::~nsZipReaderCache()
|
|
|
|
{
|
|
|
|
if (mLock)
|
|
|
|
PR_DestroyLock(mLock);
|
2000-08-23 07:18:53 +04:00
|
|
|
mZips.Enumerate(DropZipReaderCache, nsnull);
|
2000-08-24 11:38:41 +04:00
|
|
|
|
|
|
|
#ifdef ZIP_CACHE_HIT_RATE
|
2000-11-01 01:44:20 +03:00
|
|
|
printf("nsZipReaderCache size=%d hits=%d lookups=%d rate=%f%% flushes=%d missed %d\n",
|
|
|
|
mCacheSize, mZipCacheHits, mZipCacheLookups,
|
|
|
|
(float)mZipCacheHits / mZipCacheLookups,
|
|
|
|
mZipCacheFlushes, mZipSyncMisses);
|
2000-08-24 11:38:41 +04:00
|
|
|
#endif
|
2000-04-12 11:58:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader* *result)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
nsAutoLock lock(mLock);
|
|
|
|
|
2000-08-24 11:38:41 +04:00
|
|
|
#ifdef ZIP_CACHE_HIT_RATE
|
|
|
|
mZipCacheLookups++;
|
|
|
|
#endif
|
|
|
|
|
2002-04-27 09:33:09 +04:00
|
|
|
nsCAutoString path;
|
|
|
|
rv = zipFile->GetNativePath(path);
|
2000-05-25 12:30:29 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2001-06-30 03:16:52 +04:00
|
|
|
nsCStringKey key(path);
|
2001-02-23 03:15:04 +03:00
|
|
|
nsJAR* zip = NS_STATIC_CAST(nsJAR*, NS_STATIC_CAST(nsIZipReader*,mZips.Get(&key))); // AddRefs
|
2000-08-23 07:18:53 +04:00
|
|
|
if (zip) {
|
2000-08-24 11:38:41 +04:00
|
|
|
#ifdef ZIP_CACHE_HIT_RATE
|
|
|
|
mZipCacheHits++;
|
|
|
|
#endif
|
2000-11-01 01:44:20 +03:00
|
|
|
zip->ClearReleaseTime();
|
2000-04-12 11:58:24 +04:00
|
|
|
}
|
2000-08-23 07:18:53 +04:00
|
|
|
else {
|
|
|
|
zip = new nsJAR();
|
|
|
|
if (zip == nsnull)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(zip);
|
|
|
|
zip->SetZipReaderCache(this);
|
2000-04-12 11:58:24 +04:00
|
|
|
|
2000-08-23 07:18:53 +04:00
|
|
|
rv = zip->Init(zipFile);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
NS_RELEASE(zip);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
rv = zip->Open();
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
NS_RELEASE(zip);
|
|
|
|
return rv;
|
|
|
|
}
|
2000-04-12 11:58:24 +04:00
|
|
|
|
2001-02-23 03:15:04 +03:00
|
|
|
PRBool collision = mZips.Put(&key, NS_STATIC_CAST(nsIZipReader*, zip)); // AddRefs to 2
|
2000-08-23 07:18:53 +04:00
|
|
|
NS_ASSERTION(!collision, "horked");
|
2000-04-12 11:58:24 +04:00
|
|
|
}
|
|
|
|
*result = zip;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2000-08-24 00:39:17 +04:00
|
|
|
static PRBool PR_CALLBACK
|
2000-08-23 07:18:53 +04:00
|
|
|
FindOldestZip(nsHashKey *aKey, void *aData, void* closure)
|
|
|
|
{
|
|
|
|
nsJAR** oldestPtr = (nsJAR**)closure;
|
|
|
|
nsJAR* oldest = *oldestPtr;
|
|
|
|
nsJAR* current = (nsJAR*)aData;
|
|
|
|
PRIntervalTime currentReleaseTime = current->GetReleaseTime();
|
|
|
|
if (currentReleaseTime != PR_INTERVAL_NO_TIMEOUT) {
|
|
|
|
if (oldest == nsnull ||
|
|
|
|
currentReleaseTime < oldest->GetReleaseTime()) {
|
|
|
|
*oldestPtr = current;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2000-11-01 01:44:20 +03:00
|
|
|
struct ZipFindData {nsJAR* zip; PRBool found;};
|
|
|
|
|
|
|
|
static PRBool PR_CALLBACK
|
|
|
|
FindZip(nsHashKey *aKey, void *aData, void* closure)
|
|
|
|
{
|
|
|
|
ZipFindData* find_data = (ZipFindData*)closure;
|
|
|
|
|
|
|
|
if (find_data->zip == (nsJAR*)aData) {
|
|
|
|
find_data->found = PR_TRUE;
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2000-08-23 07:18:53 +04:00
|
|
|
nsresult
|
|
|
|
nsZipReaderCache::ReleaseZip(nsJAR* zip)
|
2000-04-12 11:58:24 +04:00
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
nsAutoLock lock(mLock);
|
|
|
|
|
2000-11-01 01:44:20 +03:00
|
|
|
// It is possible that two thread compete for this zip. The dangerous
|
|
|
|
// case is where one thread Releases the zip and discovers that the ref
|
|
|
|
// count has gone to one. Before it can call this ReleaseZip method
|
|
|
|
// another thread calls our GetZip method. The ref count goes to two. That
|
|
|
|
// second thread then Releases the zip and the ref coutn goes to one. It
|
|
|
|
// Then tries to enter this ReleaseZip method and blocks while the first
|
|
|
|
// thread is still here. The first thread continues and remove the zip from
|
|
|
|
// the cache and calls its Release method sending the ref count to 0 and
|
|
|
|
// deleting the zip. However, the second thread is still blocked at the
|
|
|
|
// start of ReleaseZip, but the 'zip' param now hold a reference to a
|
|
|
|
// deleted zip!
|
|
|
|
//
|
|
|
|
// So, we are going to try safegaurding here by searching our hashtable while
|
|
|
|
// locked here for the zip. We return fast if it is not found.
|
|
|
|
|
|
|
|
ZipFindData find_data = {zip, PR_FALSE};
|
|
|
|
mZips.Enumerate(FindZip, &find_data);
|
|
|
|
if (!find_data.found) {
|
|
|
|
#ifdef ZIP_CACHE_HIT_RATE
|
|
|
|
mZipSyncMisses++;
|
|
|
|
#endif
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2000-09-18 11:08:22 +04:00
|
|
|
zip->SetReleaseTime();
|
|
|
|
|
2000-08-23 07:18:53 +04:00
|
|
|
if (mZips.Count() <= mCacheSize)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsJAR* oldest = nsnull;
|
2000-11-01 01:44:20 +03:00
|
|
|
mZips.Enumerate(FindOldestZip, &oldest);
|
|
|
|
|
|
|
|
// Because of the craziness above it is possible that there is no zip that
|
|
|
|
// needs removing.
|
|
|
|
if (!oldest)
|
|
|
|
return NS_OK;
|
2000-08-23 07:18:53 +04:00
|
|
|
|
2000-08-24 11:38:41 +04:00
|
|
|
#ifdef ZIP_CACHE_HIT_RATE
|
|
|
|
mZipCacheFlushes++;
|
|
|
|
#endif
|
|
|
|
|
2000-11-01 01:44:20 +03:00
|
|
|
// Clear the cache pointer in case we gave out this oldest guy while
|
|
|
|
// his Release call was being made. Otherwise we could nest on ReleaseZip
|
|
|
|
// when the second owner calls Release and we are still here in this lock.
|
|
|
|
oldest->SetZipReaderCache(nsnull);
|
|
|
|
|
2000-08-23 07:18:53 +04:00
|
|
|
// remove from hashtable
|
2000-04-12 11:58:24 +04:00
|
|
|
nsCOMPtr<nsIFile> zipFile;
|
2000-08-23 07:18:53 +04:00
|
|
|
rv = oldest->GetFile(getter_AddRefs(zipFile));
|
2000-04-12 11:58:24 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2002-04-27 09:33:09 +04:00
|
|
|
nsCAutoString path;
|
|
|
|
rv = zipFile->GetNativePath(path);
|
2000-05-25 12:30:29 +04:00
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
2001-06-30 03:16:52 +04:00
|
|
|
nsCStringKey key(path);
|
2000-08-23 07:18:53 +04:00
|
|
|
PRBool removed = mZips.Remove(&key); // Releases
|
|
|
|
NS_ASSERTION(removed, "botched");
|
2000-04-12 11:58:24 +04:00
|
|
|
|
2000-11-01 01:44:20 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool PR_CALLBACK
|
|
|
|
FindFlushableZip(nsHashKey *aKey, void *aData, void* closure)
|
|
|
|
{
|
|
|
|
nsHashKey** flushableKeyPtr = (nsHashKey**)closure;
|
|
|
|
nsJAR* current = (nsJAR*)aData;
|
|
|
|
|
|
|
|
if (current->GetReleaseTime() != PR_INTERVAL_NO_TIMEOUT) {
|
|
|
|
*flushableKeyPtr = aKey;
|
|
|
|
current->SetZipReaderCache(nsnull);
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsZipReaderCache::Observe(nsISupports *aSubject,
|
2001-10-20 00:52:59 +04:00
|
|
|
const char *aTopic,
|
2000-11-01 01:44:20 +03:00
|
|
|
const PRUnichar *aSomeData)
|
|
|
|
{
|
2001-10-26 04:09:22 +04:00
|
|
|
if (nsCRT::strcmp(aTopic, "memory-pressure") == 0) {
|
2000-11-01 01:44:20 +03:00
|
|
|
nsAutoLock lock(mLock);
|
|
|
|
while (PR_TRUE) {
|
|
|
|
nsHashKey* flushable = nsnull;
|
|
|
|
mZips.Enumerate(FindFlushableZip, &flushable);
|
|
|
|
if ( ! flushable )
|
|
|
|
break;
|
|
|
|
PRBool removed = mZips.Remove(flushable); // Releases
|
|
|
|
NS_ASSERTION(removed, "botched");
|
|
|
|
|
|
|
|
#ifdef xDEBUG_jband
|
|
|
|
printf("flushed something from the jar cache\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
2000-04-12 11:58:24 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|