summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/nsCertTree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/nsCertTree.cpp')
-rw-r--r--security/manager/ssl/nsCertTree.cpp1451
1 files changed, 1451 insertions, 0 deletions
diff --git a/security/manager/ssl/nsCertTree.cpp b/security/manager/ssl/nsCertTree.cpp
new file mode 100644
index 000000000..1ad4e296b
--- /dev/null
+++ b/security/manager/ssl/nsCertTree.cpp
@@ -0,0 +1,1451 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCertTree.h"
+
+#include "ScopedNSSTypes.h"
+#include "mozilla/Logging.h"
+#include "nsArray.h"
+#include "nsArrayUtils.h"
+#include "nsHashKeys.h"
+#include "nsISupportsPrimitives.h"
+#include "nsITreeColumns.h"
+#include "nsIX509CertDB.h"
+#include "nsIX509Cert.h"
+#include "nsIX509CertValidity.h"
+#include "nsNSSCertHelper.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSComponent.h" // for PIPNSS string bundle calls.
+#include "nsNSSHelper.h"
+#include "nsReadableUtils.h"
+#include "nsTHashtable.h"
+#include "nsUnicharUtils.h"
+#include "nsXPCOMCID.h"
+#include "nsXPIDLString.h"
+#include "pkix/pkixtypes.h"
+
+using namespace mozilla;
+
+extern LazyLogModule gPIPNSSLog;
+
+static NS_DEFINE_CID(kCertOverrideCID, NS_CERTOVERRIDE_CID);
+
+// treeArrayElStr
+//
+// structure used to hold map of tree. Each thread (an organization
+// field from a cert) has an element in the array. The numChildren field
+// stores the number of certs corresponding to that thread.
+struct treeArrayElStr {
+ nsString orgName; /* heading for thread */
+ bool open; /* toggle open state for thread */
+ int32_t certIndex; /* index into cert array for 1st cert */
+ int32_t numChildren; /* number of chidren (certs) for thread */
+};
+
+CompareCacheHashEntryPtr::CompareCacheHashEntryPtr()
+{
+ entry = new CompareCacheHashEntry;
+}
+
+CompareCacheHashEntryPtr::~CompareCacheHashEntryPtr()
+{
+ delete entry;
+}
+
+CompareCacheHashEntry::CompareCacheHashEntry()
+:key(nullptr)
+{
+ for (int i = 0; i < max_criterions; ++i) {
+ mCritInit[i] = false;
+ }
+}
+
+static bool
+CompareCacheMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
+{
+ const CompareCacheHashEntryPtr *entryPtr = static_cast<const CompareCacheHashEntryPtr*>(hdr);
+ return entryPtr->entry->key == key;
+}
+
+static void
+CompareCacheInitEntry(PLDHashEntryHdr *hdr, const void *key)
+{
+ new (hdr) CompareCacheHashEntryPtr();
+ CompareCacheHashEntryPtr *entryPtr = static_cast<CompareCacheHashEntryPtr*>(hdr);
+ entryPtr->entry->key = (void*)key;
+}
+
+static void
+CompareCacheClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
+{
+ CompareCacheHashEntryPtr *entryPtr = static_cast<CompareCacheHashEntryPtr*>(hdr);
+ entryPtr->~CompareCacheHashEntryPtr();
+}
+
+static const PLDHashTableOps gMapOps = {
+ PLDHashTable::HashVoidPtrKeyStub,
+ CompareCacheMatchEntry,
+ PLDHashTable::MoveEntryStub,
+ CompareCacheClearEntry,
+ CompareCacheInitEntry
+};
+
+NS_IMPL_ISUPPORTS0(nsCertAddonInfo)
+NS_IMPL_ISUPPORTS(nsCertTreeDispInfo, nsICertTreeItem)
+
+nsCertTreeDispInfo::nsCertTreeDispInfo()
+:mAddonInfo(nullptr)
+,mTypeOfEntry(direct_db)
+,mPort(-1)
+,mOverrideBits(nsCertOverride::ob_None)
+,mIsTemporary(true)
+{
+}
+
+nsCertTreeDispInfo::nsCertTreeDispInfo(nsCertTreeDispInfo &other)
+{
+ mAddonInfo = other.mAddonInfo;
+ mTypeOfEntry = other.mTypeOfEntry;
+ mAsciiHost = other.mAsciiHost;
+ mPort = other.mPort;
+ mOverrideBits = other.mOverrideBits;
+ mIsTemporary = other.mIsTemporary;
+ mCert = other.mCert;
+}
+
+nsCertTreeDispInfo::~nsCertTreeDispInfo()
+{
+}
+
+NS_IMETHODIMP
+nsCertTreeDispInfo::GetCert(nsIX509Cert **_cert)
+{
+ NS_ENSURE_ARG(_cert);
+ if (mCert) {
+ // we may already have the cert for temporary overrides
+ *_cert = mCert;
+ NS_IF_ADDREF(*_cert);
+ return NS_OK;
+ }
+ if (mAddonInfo) {
+ *_cert = mAddonInfo->mCert.get();
+ NS_IF_ADDREF(*_cert);
+ }
+ else {
+ *_cert = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTreeDispInfo::GetHostPort(nsAString &aHostPort)
+{
+ nsAutoCString hostPort;
+ nsCertOverrideService::GetHostWithPort(mAsciiHost, mPort, hostPort);
+ aHostPort = NS_ConvertUTF8toUTF16(hostPort);
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsCertTree, nsICertTree, nsITreeView)
+
+nsCertTree::nsCertTree()
+ : mTreeArray(nullptr)
+ , mCompareCache(&gMapOps, sizeof(CompareCacheHashEntryPtr),
+ kInitialCacheLength)
+{
+ static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
+
+ mNSSComponent = do_GetService(kNSSComponentCID);
+ mOverrideService = do_GetService("@mozilla.org/security/certoverride;1");
+ // Might be a different service if someone is overriding the contract
+ nsCOMPtr<nsICertOverrideService> origCertOverride =
+ do_GetService(kCertOverrideCID);
+ mOriginalOverrideService =
+ static_cast<nsCertOverrideService*>(origCertOverride.get());
+ mCellText = nullptr;
+}
+
+void nsCertTree::ClearCompareHash()
+{
+ mCompareCache.ClearAndPrepareForLength(kInitialCacheLength);
+}
+
+nsCertTree::~nsCertTree()
+{
+ delete [] mTreeArray;
+}
+
+void
+nsCertTree::FreeCertArray()
+{
+ mDispInfo.Clear();
+}
+
+CompareCacheHashEntry*
+nsCertTree::getCacheEntry(void* cache, void* aCert)
+{
+ PLDHashTable& aCompareCache = *static_cast<PLDHashTable*>(cache);
+ auto entryPtr = static_cast<CompareCacheHashEntryPtr*>
+ (aCompareCache.Add(aCert, fallible));
+ return entryPtr ? entryPtr->entry : nullptr;
+}
+
+void nsCertTree::RemoveCacheEntry(void *key)
+{
+ mCompareCache.Remove(key);
+}
+
+// CountOrganizations
+//
+// Count the number of different organizations encountered in the cert
+// list.
+int32_t
+nsCertTree::CountOrganizations()
+{
+ uint32_t i, certCount;
+ certCount = mDispInfo.Length();
+ if (certCount == 0) return 0;
+ nsCOMPtr<nsIX509Cert> orgCert = nullptr;
+ nsCertAddonInfo *addonInfo = mDispInfo.ElementAt(0)->mAddonInfo;
+ if (addonInfo) {
+ orgCert = addonInfo->mCert;
+ }
+ nsCOMPtr<nsIX509Cert> nextCert = nullptr;
+ int32_t orgCount = 1;
+ for (i=1; i<certCount; i++) {
+ nextCert = nullptr;
+ addonInfo = mDispInfo.SafeElementAt(i, nullptr)->mAddonInfo;
+ if (addonInfo) {
+ nextCert = addonInfo->mCert;
+ }
+ // XXX we assume issuer org is always criterion 1
+ if (CmpBy(&mCompareCache, orgCert, nextCert, sort_IssuerOrg, sort_None, sort_None) != 0) {
+ orgCert = nextCert;
+ orgCount++;
+ }
+ }
+ return orgCount;
+}
+
+// GetThreadDescAtIndex
+//
+// If the row at index is an organization thread, return the collection
+// associated with that thread. Otherwise, return null.
+treeArrayEl *
+nsCertTree::GetThreadDescAtIndex(int32_t index)
+{
+ int i, idx=0;
+ if (index < 0) return nullptr;
+ for (i=0; i<mNumOrgs; i++) {
+ if (index == idx) {
+ return &mTreeArray[i];
+ }
+ if (mTreeArray[i].open) {
+ idx += mTreeArray[i].numChildren;
+ }
+ idx++;
+ if (idx > index) break;
+ }
+ return nullptr;
+}
+
+// GetCertAtIndex
+//
+// If the row at index is a cert, return that cert. Otherwise, return null.
+already_AddRefed<nsIX509Cert>
+nsCertTree::GetCertAtIndex(int32_t index, int32_t *outAbsoluteCertOffset)
+{
+ RefPtr<nsCertTreeDispInfo> certdi(
+ GetDispInfoAtIndex(index, outAbsoluteCertOffset));
+ if (!certdi)
+ return nullptr;
+
+ nsCOMPtr<nsIX509Cert> ret;
+ if (certdi->mCert) {
+ ret = certdi->mCert;
+ } else if (certdi->mAddonInfo) {
+ ret = certdi->mAddonInfo->mCert;
+ }
+ return ret.forget();
+}
+
+// If the row at index is a cert, return that cert. Otherwise, return null.
+already_AddRefed<nsCertTreeDispInfo>
+nsCertTree::GetDispInfoAtIndex(int32_t index,
+ int32_t *outAbsoluteCertOffset)
+{
+ int i, idx = 0, cIndex = 0, nc;
+ if (index < 0) return nullptr;
+ // Loop over the threads
+ for (i=0; i<mNumOrgs; i++) {
+ if (index == idx) return nullptr; // index is for thread
+ idx++; // get past the thread
+ nc = (mTreeArray[i].open) ? mTreeArray[i].numChildren : 0;
+ if (index < idx + nc) { // cert is within range of this thread
+ int32_t certIndex = cIndex + index - idx;
+ if (outAbsoluteCertOffset)
+ *outAbsoluteCertOffset = certIndex;
+ RefPtr<nsCertTreeDispInfo> certdi(mDispInfo.SafeElementAt(certIndex,
+ nullptr));
+ if (certdi) {
+ return certdi.forget();
+ }
+ break;
+ }
+ if (mTreeArray[i].open)
+ idx += mTreeArray[i].numChildren;
+ cIndex += mTreeArray[i].numChildren;
+ if (idx > index) break;
+ }
+ return nullptr;
+}
+
+nsCertTree::nsCertCompareFunc
+nsCertTree::GetCompareFuncFromCertType(uint32_t aType)
+{
+ switch (aType) {
+ case nsIX509Cert::ANY_CERT:
+ case nsIX509Cert::USER_CERT:
+ return CmpUserCert;
+ case nsIX509Cert::CA_CERT:
+ return CmpCACert;
+ case nsIX509Cert::EMAIL_CERT:
+ return CmpEmailCert;
+ case nsIX509Cert::SERVER_CERT:
+ default:
+ return CmpWebSiteCert;
+ }
+}
+
+struct nsCertAndArrayAndPositionAndCounterAndTracker
+{
+ RefPtr<nsCertAddonInfo> certai;
+ nsTArray< RefPtr<nsCertTreeDispInfo> > *array;
+ int position;
+ int counter;
+ nsTHashtable<nsCStringHashKey> *tracker;
+};
+
+// Used to enumerate host:port overrides that match a stored
+// certificate, creates and adds a display-info-object to the
+// provided array. Increments insert position and entry counter.
+// We remove the given key from the tracker, which is used to
+// track entries that have not yet been handled.
+// The created display-info references the cert, so make a note
+// of that by incrementing the cert usage counter.
+static void
+MatchingCertOverridesCallback(const nsCertOverride &aSettings,
+ void *aUserData)
+{
+ nsCertAndArrayAndPositionAndCounterAndTracker *cap =
+ (nsCertAndArrayAndPositionAndCounterAndTracker*)aUserData;
+ if (!cap)
+ return;
+
+ nsCertTreeDispInfo *certdi = new nsCertTreeDispInfo;
+ if (certdi) {
+ if (cap->certai)
+ cap->certai->mUsageCount++;
+ certdi->mAddonInfo = cap->certai;
+ certdi->mTypeOfEntry = nsCertTreeDispInfo::host_port_override;
+ certdi->mAsciiHost = aSettings.mAsciiHost;
+ certdi->mPort = aSettings.mPort;
+ certdi->mOverrideBits = aSettings.mOverrideBits;
+ certdi->mIsTemporary = aSettings.mIsTemporary;
+ certdi->mCert = aSettings.mCert;
+ cap->array->InsertElementAt(cap->position, certdi);
+ cap->position++;
+ cap->counter++;
+ }
+
+ // this entry is now associated to a displayed cert, remove
+ // it from the list of remaining entries
+ nsAutoCString hostPort;
+ nsCertOverrideService::GetHostWithPort(aSettings.mAsciiHost, aSettings.mPort, hostPort);
+ cap->tracker->RemoveEntry(hostPort);
+}
+
+// Used to collect a list of the (unique) host:port keys
+// for all stored overrides.
+static void
+CollectAllHostPortOverridesCallback(const nsCertOverride &aSettings,
+ void *aUserData)
+{
+ nsTHashtable<nsCStringHashKey> *collectorTable =
+ (nsTHashtable<nsCStringHashKey> *)aUserData;
+ if (!collectorTable)
+ return;
+
+ nsAutoCString hostPort;
+ nsCertOverrideService::GetHostWithPort(aSettings.mAsciiHost, aSettings.mPort, hostPort);
+ collectorTable->PutEntry(hostPort);
+}
+
+struct nsArrayAndPositionAndCounterAndTracker
+{
+ nsTArray< RefPtr<nsCertTreeDispInfo> > *array;
+ int position;
+ int counter;
+ nsTHashtable<nsCStringHashKey> *tracker;
+};
+
+// Used when enumerating the stored host:port overrides where
+// no associated certificate was found in the NSS database.
+static void
+AddRemaningHostPortOverridesCallback(const nsCertOverride &aSettings,
+ void *aUserData)
+{
+ nsArrayAndPositionAndCounterAndTracker *cap =
+ (nsArrayAndPositionAndCounterAndTracker*)aUserData;
+ if (!cap)
+ return;
+
+ nsAutoCString hostPort;
+ nsCertOverrideService::GetHostWithPort(aSettings.mAsciiHost, aSettings.mPort, hostPort);
+ if (!cap->tracker->GetEntry(hostPort))
+ return;
+
+ // This entry is not associated to any stored cert,
+ // so we still need to display it.
+
+ nsCertTreeDispInfo *certdi = new nsCertTreeDispInfo;
+ if (certdi) {
+ certdi->mAddonInfo = nullptr;
+ certdi->mTypeOfEntry = nsCertTreeDispInfo::host_port_override;
+ certdi->mAsciiHost = aSettings.mAsciiHost;
+ certdi->mPort = aSettings.mPort;
+ certdi->mOverrideBits = aSettings.mOverrideBits;
+ certdi->mIsTemporary = aSettings.mIsTemporary;
+ certdi->mCert = aSettings.mCert;
+ cap->array->InsertElementAt(cap->position, certdi);
+ cap->position++;
+ cap->counter++;
+ }
+}
+
+nsresult
+nsCertTree::GetCertsByTypeFromCertList(CERTCertList *aCertList,
+ uint32_t aWantedType,
+ nsCertCompareFunc aCertCmpFn,
+ void *aCertCmpFnArg)
+{
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetCertsByTypeFromCertList"));
+ if (!aCertList)
+ return NS_ERROR_FAILURE;
+
+ if (!mOriginalOverrideService)
+ return NS_ERROR_FAILURE;
+
+ nsTHashtable<nsCStringHashKey> allHostPortOverrideKeys;
+
+ if (aWantedType == nsIX509Cert::SERVER_CERT) {
+ mOriginalOverrideService->
+ EnumerateCertOverrides(nullptr,
+ CollectAllHostPortOverridesCallback,
+ &allHostPortOverrideKeys);
+ }
+
+ CERTCertListNode *node;
+ int count = 0;
+ for (node = CERT_LIST_HEAD(aCertList);
+ !CERT_LIST_END(node, aCertList);
+ node = CERT_LIST_NEXT(node)) {
+
+ bool wantThisCert = (aWantedType == nsIX509Cert::ANY_CERT);
+ bool wantThisCertIfNoOverrides = false;
+ bool wantThisCertIfHaveOverrides = false;
+ bool addOverrides = false;
+
+ if (!wantThisCert) {
+ uint32_t thisCertType = getCertType(node->cert);
+
+ // The output from getCertType is a "guess", which can be wrong.
+ // The guess is based on stored trust flags, but for the host:port
+ // overrides, we are storing certs without any trust flags associated.
+ // So we must check whether the cert really belongs to the
+ // server, email or unknown tab. We will lookup the cert in the override
+ // list to come to the decision. Unfortunately, the lookup in the
+ // override list is quite expensive. Therefore we are using this
+ // lengthy if/else statement to minimize
+ // the number of override-list-lookups.
+
+ if (aWantedType == nsIX509Cert::SERVER_CERT
+ && thisCertType == nsIX509Cert::UNKNOWN_CERT) {
+ // This unknown cert was stored without trust
+ // Are there host:port based overrides stored?
+ // If yes, display them.
+ addOverrides = true;
+ }
+ else
+ if (aWantedType == nsIX509Cert::UNKNOWN_CERT
+ && thisCertType == nsIX509Cert::UNKNOWN_CERT) {
+ // This unknown cert was stored without trust.
+ // If there are associated overrides, do not show as unknown.
+ // If there are no associated overrides, display as unknown.
+ wantThisCertIfNoOverrides = true;
+ }
+ else
+ if (aWantedType == nsIX509Cert::SERVER_CERT
+ && thisCertType == nsIX509Cert::SERVER_CERT) {
+ // This server cert is explicitly marked as a web site peer,
+ // with or without trust, but editable, so show it
+ wantThisCert = true;
+ // Are there host:port based overrides stored?
+ // If yes, display them.
+ addOverrides = true;
+ }
+ else
+ if (aWantedType == nsIX509Cert::SERVER_CERT
+ && thisCertType == nsIX509Cert::EMAIL_CERT) {
+ // This cert might have been categorized as an email cert
+ // because it carries an email address. But is it really one?
+ // Our cert categorization is uncertain when it comes to
+ // distinguish between email certs and web site certs.
+ // So, let's see if we have an override for that cert
+ // and if there is, conclude it's really a web site cert.
+ addOverrides = true;
+ }
+ else
+ if (aWantedType == nsIX509Cert::EMAIL_CERT
+ && thisCertType == nsIX509Cert::EMAIL_CERT) {
+ // This cert might have been categorized as an email cert
+ // because it carries an email address. But is it really one?
+ // Our cert categorization is uncertain when it comes to
+ // distinguish between email certs and web site certs.
+ // So, let's see if we have an override for that cert
+ // and if there is, conclude it's really a web site cert.
+ wantThisCertIfNoOverrides = true;
+ }
+ else
+ if (thisCertType == aWantedType) {
+ wantThisCert = true;
+ }
+ }
+
+ nsCOMPtr<nsIX509Cert> pipCert = nsNSSCertificate::Create(node->cert);
+ if (!pipCert)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (wantThisCertIfNoOverrides || wantThisCertIfHaveOverrides) {
+ uint32_t ocount = 0;
+ nsresult rv =
+ mOverrideService->IsCertUsedForOverrides(pipCert,
+ true, // we want temporaries
+ true, // we want permanents
+ &ocount);
+ if (wantThisCertIfNoOverrides) {
+ if (NS_FAILED(rv) || ocount == 0) {
+ // no overrides for this cert
+ wantThisCert = true;
+ }
+ }
+
+ if (wantThisCertIfHaveOverrides) {
+ if (NS_SUCCEEDED(rv) && ocount > 0) {
+ // there are overrides for this cert
+ wantThisCert = true;
+ }
+ }
+ }
+
+ RefPtr<nsCertAddonInfo> certai(new nsCertAddonInfo);
+ certai->mCert = pipCert;
+ certai->mUsageCount = 0;
+
+ if (wantThisCert || addOverrides) {
+ int InsertPosition = 0;
+ for (; InsertPosition < count; ++InsertPosition) {
+ nsCOMPtr<nsIX509Cert> cert = nullptr;
+ RefPtr<nsCertTreeDispInfo> elem(
+ mDispInfo.SafeElementAt(InsertPosition, nullptr));
+ if (elem && elem->mAddonInfo) {
+ cert = elem->mAddonInfo->mCert;
+ }
+ if ((*aCertCmpFn)(aCertCmpFnArg, pipCert, cert) < 0) {
+ break;
+ }
+ }
+ if (wantThisCert) {
+ nsCertTreeDispInfo *certdi = new nsCertTreeDispInfo;
+ certdi->mAddonInfo = certai;
+ certai->mUsageCount++;
+ certdi->mTypeOfEntry = nsCertTreeDispInfo::direct_db;
+ // not necessary: certdi->mAsciiHost.Clear(); certdi->mPort = -1;
+ certdi->mOverrideBits = nsCertOverride::ob_None;
+ certdi->mIsTemporary = false;
+ mDispInfo.InsertElementAt(InsertPosition, certdi);
+ ++count;
+ ++InsertPosition;
+ }
+ if (addOverrides) {
+ nsCertAndArrayAndPositionAndCounterAndTracker cap;
+ cap.certai = certai;
+ cap.array = &mDispInfo;
+ cap.position = InsertPosition;
+ cap.counter = 0;
+ cap.tracker = &allHostPortOverrideKeys;
+
+ mOriginalOverrideService->
+ EnumerateCertOverrides(pipCert, MatchingCertOverridesCallback, &cap);
+ count += cap.counter;
+ }
+ }
+ }
+
+ if (aWantedType == nsIX509Cert::SERVER_CERT) {
+ nsArrayAndPositionAndCounterAndTracker cap;
+ cap.array = &mDispInfo;
+ cap.position = 0;
+ cap.counter = 0;
+ cap.tracker = &allHostPortOverrideKeys;
+ mOriginalOverrideService->
+ EnumerateCertOverrides(nullptr, AddRemaningHostPortOverridesCallback, &cap);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsCertTree::GetCertsByType(uint32_t aType,
+ nsCertCompareFunc aCertCmpFn,
+ void *aCertCmpFnArg)
+{
+ nsNSSShutDownPreventionLock locker;
+ nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
+ UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, cxt));
+ return GetCertsByTypeFromCertList(certList.get(), aType, aCertCmpFn,
+ aCertCmpFnArg);
+}
+
+nsresult
+nsCertTree::GetCertsByTypeFromCache(nsIX509CertList *aCache,
+ uint32_t aType,
+ nsCertCompareFunc aCertCmpFn,
+ void *aCertCmpFnArg)
+{
+ NS_ENSURE_ARG_POINTER(aCache);
+ // GetRawCertList checks for NSS shutdown since we can't do it ourselves here
+ // easily. We still have to acquire a shutdown prevention lock to prevent NSS
+ // shutting down after GetRawCertList has returned. While cumbersome, this is
+ // at least mostly correct. The rest of this implementation doesn't even go
+ // this far in attempting to check for or prevent NSS shutdown at the
+ // appropriate times. If this were reimplemented at a higher level using
+ // more encapsulated types that handled NSS shutdown themselves, we wouldn't
+ // be having these kinds of problems.
+ nsNSSShutDownPreventionLock locker;
+ CERTCertList* certList = aCache->GetRawCertList();
+ if (!certList)
+ return NS_ERROR_FAILURE;
+ return GetCertsByTypeFromCertList(certList, aType, aCertCmpFn, aCertCmpFnArg);
+}
+
+// LoadCerts
+//
+// Load all of the certificates in the DB for this type. Sort them
+// by token, organization, then common name.
+NS_IMETHODIMP
+nsCertTree::LoadCertsFromCache(nsIX509CertList *aCache, uint32_t aType)
+{
+ if (mTreeArray) {
+ FreeCertArray();
+ delete [] mTreeArray;
+ mTreeArray = nullptr;
+ mNumRows = 0;
+ }
+ ClearCompareHash();
+
+ nsresult rv = GetCertsByTypeFromCache(aCache, aType,
+ GetCompareFuncFromCertType(aType),
+ &mCompareCache);
+ if (NS_FAILED(rv)) return rv;
+ return UpdateUIContents();
+}
+
+NS_IMETHODIMP
+nsCertTree::LoadCerts(uint32_t aType)
+{
+ if (mTreeArray) {
+ FreeCertArray();
+ delete [] mTreeArray;
+ mTreeArray = nullptr;
+ mNumRows = 0;
+ }
+ ClearCompareHash();
+
+ nsresult rv = GetCertsByType(aType, GetCompareFuncFromCertType(aType),
+ &mCompareCache);
+ if (NS_FAILED(rv)) return rv;
+ return UpdateUIContents();
+}
+
+nsresult
+nsCertTree::UpdateUIContents()
+{
+ uint32_t count = mDispInfo.Length();
+ mNumOrgs = CountOrganizations();
+ mTreeArray = new treeArrayEl[mNumOrgs];
+
+ mCellText = nsArrayBase::Create();
+
+if (count) {
+ uint32_t j = 0;
+ nsCOMPtr<nsIX509Cert> orgCert = nullptr;
+ nsCertAddonInfo *addonInfo = mDispInfo.ElementAt(j)->mAddonInfo;
+ if (addonInfo) {
+ orgCert = addonInfo->mCert;
+ }
+ for (int32_t i=0; i<mNumOrgs; i++) {
+ nsString &orgNameRef = mTreeArray[i].orgName;
+ if (!orgCert) {
+ mNSSComponent->GetPIPNSSBundleString("CertOrgUnknown", orgNameRef);
+ }
+ else {
+ orgCert->GetIssuerOrganization(orgNameRef);
+ if (orgNameRef.IsEmpty())
+ orgCert->GetCommonName(orgNameRef);
+ }
+ mTreeArray[i].open = true;
+ mTreeArray[i].certIndex = j;
+ mTreeArray[i].numChildren = 1;
+ if (++j >= count) break;
+ nsCOMPtr<nsIX509Cert> nextCert = nullptr;
+ nsCertAddonInfo *addonInfo = mDispInfo.SafeElementAt(j, nullptr)->mAddonInfo;
+ if (addonInfo) {
+ nextCert = addonInfo->mCert;
+ }
+ while (0 == CmpBy(&mCompareCache, orgCert, nextCert, sort_IssuerOrg, sort_None, sort_None)) {
+ mTreeArray[i].numChildren++;
+ if (++j >= count) break;
+ nextCert = nullptr;
+ addonInfo = mDispInfo.SafeElementAt(j, nullptr)->mAddonInfo;
+ if (addonInfo) {
+ nextCert = addonInfo->mCert;
+ }
+ }
+ orgCert = nextCert;
+ }
+}
+ if (mTree) {
+ mTree->BeginUpdateBatch();
+ mTree->RowCountChanged(0, -mNumRows);
+ }
+ mNumRows = count + mNumOrgs;
+ if (mTree)
+ mTree->EndUpdateBatch();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::DeleteEntryObject(uint32_t index)
+{
+ if (!mTreeArray) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIX509CertDB> certdb =
+ do_GetService("@mozilla.org/security/x509certdb;1");
+ if (!certdb) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int i;
+ uint32_t idx = 0, cIndex = 0, nc;
+ // Loop over the threads
+ for (i=0; i<mNumOrgs; i++) {
+ if (index == idx)
+ return NS_OK; // index is for thread
+ idx++; // get past the thread
+ nc = (mTreeArray[i].open) ? mTreeArray[i].numChildren : 0;
+ if (index < idx + nc) { // cert is within range of this thread
+ int32_t certIndex = cIndex + index - idx;
+
+ bool canRemoveEntry = false;
+ RefPtr<nsCertTreeDispInfo> certdi(mDispInfo.SafeElementAt(certIndex,
+ nullptr));
+
+ // We will remove the element from the visual tree.
+ // Only if we have a certdi, then we can check for additional actions.
+ nsCOMPtr<nsIX509Cert> cert = nullptr;
+ if (certdi) {
+ if (certdi->mAddonInfo) {
+ cert = certdi->mAddonInfo->mCert;
+ }
+ nsCertAddonInfo* addonInfo =
+ certdi->mAddonInfo ? certdi->mAddonInfo.get() : nullptr;
+ if (certdi->mTypeOfEntry == nsCertTreeDispInfo::host_port_override) {
+ mOverrideService->ClearValidityOverride(certdi->mAsciiHost, certdi->mPort);
+ if (addonInfo) {
+ addonInfo->mUsageCount--;
+ if (addonInfo->mUsageCount == 0) {
+ // The certificate stored in the database is no longer
+ // referenced by any other object displayed.
+ // That means we no longer need to keep it around
+ // and really can remove it.
+ canRemoveEntry = true;
+ }
+ }
+ }
+ else {
+ if (addonInfo && addonInfo->mUsageCount > 1) {
+ // user is trying to delete a perm trusted cert,
+ // although there are still overrides stored,
+ // so, we keep the cert, but remove the trust
+
+ UniqueCERTCertificate nsscert(cert->GetCert());
+
+ if (nsscert) {
+ CERTCertTrust trust;
+ memset((void*)&trust, 0, sizeof(trust));
+
+ SECStatus srv = CERT_DecodeTrustString(&trust, ""); // no override
+ if (srv == SECSuccess) {
+ CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nsscert.get(),
+ &trust);
+ }
+ }
+ }
+ else {
+ canRemoveEntry = true;
+ }
+ }
+ }
+
+ mDispInfo.RemoveElementAt(certIndex);
+
+ if (canRemoveEntry) {
+ RemoveCacheEntry(cert);
+ certdb->DeleteCertificate(cert);
+ }
+
+ delete [] mTreeArray;
+ mTreeArray = nullptr;
+ return UpdateUIContents();
+ }
+ if (mTreeArray[i].open)
+ idx += mTreeArray[i].numChildren;
+ cIndex += mTreeArray[i].numChildren;
+ if (idx > index)
+ break;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Begin nsITreeView methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+nsCertTree::GetCert(uint32_t aIndex, nsIX509Cert **_cert)
+{
+ NS_ENSURE_ARG(_cert);
+ *_cert = GetCertAtIndex(aIndex).take();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetTreeItem(uint32_t aIndex, nsICertTreeItem **_treeitem)
+{
+ NS_ENSURE_ARG(_treeitem);
+
+ RefPtr<nsCertTreeDispInfo> certdi(GetDispInfoAtIndex(aIndex));
+ if (!certdi)
+ return NS_ERROR_FAILURE;
+
+ *_treeitem = certdi;
+ NS_IF_ADDREF(*_treeitem);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetRowCount(int32_t *aRowCount)
+{
+ if (!mTreeArray)
+ return NS_ERROR_NOT_INITIALIZED;
+ uint32_t count = 0;
+ for (int32_t i=0; i<mNumOrgs; i++) {
+ if (mTreeArray[i].open) {
+ count += mTreeArray[i].numChildren;
+ }
+ count++;
+ }
+ *aRowCount = count;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetSelection(nsITreeSelection * *aSelection)
+{
+ *aSelection = mSelection;
+ NS_IF_ADDREF(*aSelection);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::SetSelection(nsITreeSelection * aSelection)
+{
+ mSelection = aSelection;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetRowProperties(int32_t index, nsAString& aProps)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetCellProperties(int32_t row, nsITreeColumn* col,
+ nsAString& aProps)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetColumnProperties(nsITreeColumn* col, nsAString& aProps)
+{
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsCertTree::IsContainer(int32_t index, bool *_retval)
+{
+ if (!mTreeArray)
+ return NS_ERROR_NOT_INITIALIZED;
+ treeArrayEl *el = GetThreadDescAtIndex(index);
+ if (el) {
+ *_retval = true;
+ } else {
+ *_retval = false;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::IsContainerOpen(int32_t index, bool *_retval)
+{
+ if (!mTreeArray)
+ return NS_ERROR_NOT_INITIALIZED;
+ treeArrayEl *el = GetThreadDescAtIndex(index);
+ if (el && el->open) {
+ *_retval = true;
+ } else {
+ *_retval = false;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::IsContainerEmpty(int32_t index, bool *_retval)
+{
+ *_retval = !mTreeArray;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::IsSeparator(int32_t index, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetParentIndex(int32_t rowIndex, int32_t *_retval)
+{
+ if (!mTreeArray)
+ return NS_ERROR_NOT_INITIALIZED;
+ int i, idx = 0;
+ for (i = 0; i < mNumOrgs && idx < rowIndex; i++, idx++) {
+ if (mTreeArray[i].open) {
+ if (rowIndex <= idx + mTreeArray[i].numChildren) {
+ *_retval = idx;
+ return NS_OK;
+ }
+ idx += mTreeArray[i].numChildren;
+ }
+ }
+ *_retval = -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::HasNextSibling(int32_t rowIndex, int32_t afterIndex,
+ bool *_retval)
+{
+ if (!mTreeArray)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ int i, idx = 0;
+ for (i = 0; i < mNumOrgs && idx <= rowIndex; i++, idx++) {
+ if (mTreeArray[i].open) {
+ idx += mTreeArray[i].numChildren;
+ if (afterIndex <= idx) {
+ *_retval = afterIndex < idx;
+ return NS_OK;
+ }
+ }
+ }
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetLevel(int32_t index, int32_t *_retval)
+{
+ if (!mTreeArray)
+ return NS_ERROR_NOT_INITIALIZED;
+ treeArrayEl *el = GetThreadDescAtIndex(index);
+ if (el) {
+ *_retval = 0;
+ } else {
+ *_retval = 1;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetImageSrc(int32_t row, nsITreeColumn* col,
+ nsAString& _retval)
+{
+ _retval.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetProgressMode(int32_t row, nsITreeColumn* col, int32_t* _retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetCellValue(int32_t row, nsITreeColumn* col,
+ nsAString& _retval)
+{
+ _retval.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::GetCellText(int32_t row, nsITreeColumn* col,
+ nsAString& _retval)
+{
+ if (!mTreeArray)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = NS_OK;
+ _retval.Truncate();
+
+ const char16_t* colID;
+ col->GetIdConst(&colID);
+
+ treeArrayEl *el = GetThreadDescAtIndex(row);
+ if (el) {
+ if (NS_LITERAL_STRING("certcol").Equals(colID))
+ _retval.Assign(el->orgName);
+ else
+ _retval.Truncate();
+ return NS_OK;
+ }
+
+ int32_t absoluteCertOffset;
+ RefPtr<nsCertTreeDispInfo> certdi(GetDispInfoAtIndex(row, &absoluteCertOffset));
+ if (!certdi)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIX509Cert> cert = certdi->mCert;
+ if (!cert && certdi->mAddonInfo) {
+ cert = certdi->mAddonInfo->mCert;
+ }
+
+ int32_t colIndex;
+ col->GetIndex(&colIndex);
+ uint32_t arrayIndex=absoluteCertOffset+colIndex*(mNumRows-mNumOrgs);
+ uint32_t arrayLength=0;
+ if (mCellText) {
+ mCellText->GetLength(&arrayLength);
+ }
+ if (arrayIndex < arrayLength) {
+ nsCOMPtr<nsISupportsString> myString(do_QueryElementAt(mCellText, arrayIndex));
+ if (myString) {
+ myString->GetData(_retval);
+ return NS_OK;
+ }
+ }
+
+ if (NS_LITERAL_STRING("certcol").Equals(colID)) {
+ if (!cert) {
+ mNSSComponent->GetPIPNSSBundleString("CertNotStored", _retval);
+ }
+ else {
+ rv = cert->GetCommonName(_retval);
+ if (NS_FAILED(rv) || _retval.IsEmpty()) {
+ // kaie: I didn't invent the idea to cut off anything before
+ // the first colon. :-)
+ nsAutoString nick;
+ rv = cert->GetNickname(nick);
+
+ nsAString::const_iterator start, end, end2;
+ nick.BeginReading(start);
+ nick.EndReading(end);
+ end2 = end;
+
+ if (FindInReadable(NS_LITERAL_STRING(":"), start, end)) {
+ // found. end points to the first char after the colon,
+ // that's what we want.
+ _retval = Substring(end, end2);
+ }
+ else {
+ _retval = nick;
+ }
+ }
+ }
+ } else if (NS_LITERAL_STRING("tokencol").Equals(colID) && cert) {
+ rv = cert->GetTokenName(_retval);
+ } else if (NS_LITERAL_STRING("emailcol").Equals(colID) && cert) {
+ rv = cert->GetEmailAddress(_retval);
+ } else if (NS_LITERAL_STRING("issuedcol").Equals(colID) && cert) {
+ nsCOMPtr<nsIX509CertValidity> validity;
+
+ rv = cert->GetValidity(getter_AddRefs(validity));
+ if (NS_SUCCEEDED(rv)) {
+ validity->GetNotBeforeLocalDay(_retval);
+ }
+ } else if (NS_LITERAL_STRING("expiredcol").Equals(colID) && cert) {
+ nsCOMPtr<nsIX509CertValidity> validity;
+
+ rv = cert->GetValidity(getter_AddRefs(validity));
+ if (NS_SUCCEEDED(rv)) {
+ validity->GetNotAfterLocalDay(_retval);
+ }
+ } else if (NS_LITERAL_STRING("serialnumcol").Equals(colID) && cert) {
+ rv = cert->GetSerialNumber(_retval);
+ } else if (NS_LITERAL_STRING("sitecol").Equals(colID)) {
+ if (certdi->mTypeOfEntry == nsCertTreeDispInfo::host_port_override) {
+ nsAutoCString hostPort;
+ nsCertOverrideService::GetHostWithPort(certdi->mAsciiHost, certdi->mPort, hostPort);
+ _retval = NS_ConvertUTF8toUTF16(hostPort);
+ }
+ else {
+ _retval = NS_LITERAL_STRING("*");
+ }
+ } else if (NS_LITERAL_STRING("lifetimecol").Equals(colID)) {
+ const char *stringID =
+ (certdi->mIsTemporary) ? "CertExceptionTemporary" : "CertExceptionPermanent";
+ rv = mNSSComponent->GetPIPNSSBundleString(stringID, _retval);
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+ if (mCellText) {
+ nsCOMPtr<nsISupportsString> text(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ text->SetData(_retval);
+ mCellText->ReplaceElementAt(text, arrayIndex, false);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsCertTree::SetTree(nsITreeBoxObject *tree)
+{
+ mTree = tree;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::ToggleOpenState(int32_t index)
+{
+ if (!mTreeArray)
+ return NS_ERROR_NOT_INITIALIZED;
+ treeArrayEl *el = GetThreadDescAtIndex(index);
+ if (el) {
+ el->open = !el->open;
+ int32_t newChildren = (el->open) ? el->numChildren : -el->numChildren;
+ if (mTree) mTree->RowCountChanged(index + 1, newChildren);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::CycleHeader(nsITreeColumn* col)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::SelectionChanged()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsCertTree::CycleCell(int32_t row, nsITreeColumn* col)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::IsEditable(int32_t row, nsITreeColumn* col, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::IsSelectable(int32_t row, nsITreeColumn* col, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::SetCellValue(int32_t row, nsITreeColumn* col,
+ const nsAString& value)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::SetCellText(int32_t row, nsITreeColumn* col,
+ const nsAString& value)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::PerformAction(const char16_t *action)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::PerformActionOnRow(const char16_t *action, int32_t row)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCertTree::PerformActionOnCell(const char16_t *action, int32_t row,
+ nsITreeColumn* col)
+{
+ return NS_OK;
+}
+
+#ifdef DEBUG_CERT_TREE
+void
+nsCertTree::dumpMap()
+{
+ for (int i=0; i<mNumOrgs; i++) {
+ nsAutoString org(mTreeArray[i].orgName);
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("ORG[%s]", NS_LossyConvertUTF16toASCII(org).get()));
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("OPEN[%d]", mTreeArray[i].open));
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("INDEX[%d]", mTreeArray[i].certIndex));
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("NCHILD[%d]", mTreeArray[i].numChildren));
+ }
+ for (int i=0; i<mNumRows; i++) {
+ treeArrayEl *el = GetThreadDescAtIndex(i);
+ if (el) {
+ nsAutoString td(el->orgName);
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("thread desc[%d]: %s", i, NS_LossyConvertUTF16toASCII(td).get()));
+ }
+ nsCOMPtr<nsIX509Cert> ct = GetCertAtIndex(i);
+ if (ct) {
+ char16_t *goo;
+ ct->GetCommonName(&goo);
+ nsAutoString doo(goo);
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("cert [%d]: %s", i, NS_LossyConvertUTF16toASCII(doo).get()));
+ }
+ }
+}
+#endif
+
+//
+// CanDrop
+//
+NS_IMETHODIMP nsCertTree::CanDrop(int32_t index, int32_t orientation,
+ nsIDOMDataTransfer* aDataTransfer, bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = false;
+
+ return NS_OK;
+}
+
+
+//
+// Drop
+//
+NS_IMETHODIMP nsCertTree::Drop(int32_t row, int32_t orient, nsIDOMDataTransfer* aDataTransfer)
+{
+ return NS_OK;
+}
+
+
+//
+// IsSorted
+//
+// ...
+//
+NS_IMETHODIMP nsCertTree::IsSorted(bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+#define RETURN_NOTHING
+
+void
+nsCertTree::CmpInitCriterion(nsIX509Cert *cert, CompareCacheHashEntry *entry,
+ sortCriterion crit, int32_t level)
+{
+ NS_ENSURE_TRUE(cert && entry, RETURN_NOTHING);
+
+ entry->mCritInit[level] = true;
+ nsXPIDLString &str = entry->mCrit[level];
+
+ switch (crit) {
+ case sort_IssuerOrg:
+ cert->GetIssuerOrganization(str);
+ if (str.IsEmpty())
+ cert->GetCommonName(str);
+ break;
+ case sort_Org:
+ cert->GetOrganization(str);
+ break;
+ case sort_Token:
+ cert->GetTokenName(str);
+ break;
+ case sort_CommonName:
+ cert->GetCommonName(str);
+ break;
+ case sort_IssuedDateDescending:
+ {
+ nsresult rv;
+ nsCOMPtr<nsIX509CertValidity> validity;
+ PRTime notBefore;
+
+ rv = cert->GetValidity(getter_AddRefs(validity));
+ if (NS_SUCCEEDED(rv)) {
+ rv = validity->GetNotBefore(&notBefore);
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ PRExplodedTime explodedTime;
+ PR_ExplodeTime(notBefore, PR_GMTParameters, &explodedTime);
+ char datebuf[20]; // 4 + 2 + 2 + 2 + 2 + 2 + 1 = 15
+ if (0 != PR_FormatTime(datebuf, sizeof(datebuf), "%Y%m%d%H%M%S", &explodedTime)) {
+ str = NS_ConvertASCIItoUTF16(nsDependentCString(datebuf));
+ }
+ }
+ }
+ break;
+ case sort_Email:
+ cert->GetEmailAddress(str);
+ break;
+ case sort_None:
+ default:
+ break;
+ }
+}
+
+int32_t
+nsCertTree::CmpByCrit(nsIX509Cert *a, CompareCacheHashEntry *ace,
+ nsIX509Cert *b, CompareCacheHashEntry *bce,
+ sortCriterion crit, int32_t level)
+{
+ NS_ENSURE_TRUE(a && ace && b && bce, 0);
+
+ if (!ace->mCritInit[level]) {
+ CmpInitCriterion(a, ace, crit, level);
+ }
+
+ if (!bce->mCritInit[level]) {
+ CmpInitCriterion(b, bce, crit, level);
+ }
+
+ nsXPIDLString &str_a = ace->mCrit[level];
+ nsXPIDLString &str_b = bce->mCrit[level];
+
+ int32_t result;
+ if (str_a && str_b)
+ result = Compare(str_a, str_b, nsCaseInsensitiveStringComparator());
+ else
+ result = !str_a ? (!str_b ? 0 : -1) : 1;
+
+ if (sort_IssuedDateDescending == crit)
+ result *= -1; // reverse compare order
+
+ return result;
+}
+
+int32_t
+nsCertTree::CmpBy(void *cache, nsIX509Cert *a, nsIX509Cert *b,
+ sortCriterion c0, sortCriterion c1, sortCriterion c2)
+{
+ // This will be called when comparing items for display sorting.
+ // Some items might have no cert associated, so either a or b is null.
+ // We want all those orphans show at the top of the list,
+ // so we treat a null cert as "smaller" by returning -1.
+ // We don't try to sort within the group of no-cert entries,
+ // so we treat them as equal wrt sort order.
+
+ if (!a && !b)
+ return 0;
+
+ if (!a)
+ return -1;
+
+ if (!b)
+ return 1;
+
+ NS_ENSURE_TRUE(cache && a && b, 0);
+
+ CompareCacheHashEntry *ace = getCacheEntry(cache, a);
+ CompareCacheHashEntry *bce = getCacheEntry(cache, b);
+
+ int32_t cmp;
+ cmp = CmpByCrit(a, ace, b, bce, c0, 0);
+ if (cmp != 0)
+ return cmp;
+
+ if (c1 != sort_None) {
+ cmp = CmpByCrit(a, ace, b, bce, c1, 1);
+ if (cmp != 0)
+ return cmp;
+
+ if (c2 != sort_None) {
+ return CmpByCrit(a, ace, b, bce, c2, 2);
+ }
+ }
+
+ return cmp;
+}
+
+int32_t
+nsCertTree::CmpCACert(void *cache, nsIX509Cert *a, nsIX509Cert *b)
+{
+ // XXX we assume issuer org is always criterion 1
+ return CmpBy(cache, a, b, sort_IssuerOrg, sort_Org, sort_Token);
+}
+
+int32_t
+nsCertTree::CmpWebSiteCert(void *cache, nsIX509Cert *a, nsIX509Cert *b)
+{
+ // XXX we assume issuer org is always criterion 1
+ return CmpBy(cache, a, b, sort_IssuerOrg, sort_CommonName, sort_None);
+}
+
+int32_t
+nsCertTree::CmpUserCert(void *cache, nsIX509Cert *a, nsIX509Cert *b)
+{
+ // XXX we assume issuer org is always criterion 1
+ return CmpBy(cache, a, b, sort_IssuerOrg, sort_Token, sort_IssuedDateDescending);
+}
+
+int32_t
+nsCertTree::CmpEmailCert(void *cache, nsIX509Cert *a, nsIX509Cert *b)
+{
+ // XXX we assume issuer org is always criterion 1
+ return CmpBy(cache, a, b, sort_IssuerOrg, sort_Email, sort_CommonName);
+}
+