diff options
Diffstat (limited to 'security/manager/ssl/nsCertTree.cpp')
-rw-r--r-- | security/manager/ssl/nsCertTree.cpp | 843 |
1 files changed, 843 insertions, 0 deletions
diff --git a/security/manager/ssl/nsCertTree.cpp b/security/manager/ssl/nsCertTree.cpp new file mode 100644 index 0000000000..b602b3b13b --- /dev/null +++ b/security/manager/ssl/nsCertTree.cpp @@ -0,0 +1,843 @@ +/* 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 "mozilla/Maybe.h" +#include "mozilla/intl/AppDateTimeFormat.h" +#include "nsArray.h" +#include "nsArrayUtils.h" +#include "nsHashKeys.h" +#include "nsISupportsPrimitives.h" +#include "nsIX509CertDB.h" +#include "nsIX509Cert.h" +#include "nsIX509CertValidity.h" +#include "nsNSSCertHelper.h" +#include "nsNSSCertificate.h" +#include "nsNSSCertificateDB.h" +#include "nsNSSHelper.h" +#include "nsReadableUtils.h" +#include "nsTHashtable.h" +#include "nsUnicharUtils.h" +#include "nsXPCOMCID.h" +#include "nsString.h" +#include "nsTreeColumns.h" +#include "mozpkix/pkixtypes.h" + +using namespace mozilla; + +extern LazyLogModule gPIPNSSLog; + +// 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), mCritInit() { + for (int i = 0; i < max_criterions; ++i) { + mCritInit[i] = false; + mCrit[i].SetIsVoid(true); + } +} + +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_ISUPPORTS(nsCertTreeDispInfo, nsICertTreeItem) + +nsCertTreeDispInfo::~nsCertTreeDispInfo() = default; + +NS_IMETHODIMP +nsCertTreeDispInfo::GetCert(nsIX509Cert** aCert) { + NS_ENSURE_ARG(aCert); + nsCOMPtr<nsIX509Cert> cert = mCert; + cert.forget(aCert); + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsCertTree, nsICertTree, nsITreeView) + +nsCertTree::nsCertTree() + : mTreeArray(nullptr), + mNumOrgs(0), + mNumRows(0), + mCompareCache(&gMapOps, sizeof(CompareCacheHashEntryPtr), + kInitialCacheLength) { + 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 = mDispInfo.ElementAt(0)->mCert; + nsCOMPtr<nsIX509Cert> nextCert = nullptr; + int32_t orgCount = 1; + for (i = 1; i < certCount; i++) { + nextCert = mDispInfo.SafeElementAt(i, nullptr)->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 = certdi->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::EMAIL_CERT: + return CmpEmailCert; + case nsIX509Cert::CA_CERT: + default: + return CmpCACert; + } +} + +nsresult nsCertTree::GetCertsByTypeFromCertList( + const nsTArray<RefPtr<nsIX509Cert>>& aCertList, uint32_t aWantedType, + nsCertCompareFunc aCertCmpFn, void* aCertCmpFnArg) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetCertsByTypeFromCertList")); + + nsTHashtable<nsCStringHashKey> allHostPortOverrideKeys; + + if (aWantedType == nsIX509Cert::SERVER_CERT) { + return NS_ERROR_INVALID_ARG; + } + + int count = 0; + for (const auto& cert : aCertList) { + bool wantThisCert = (aWantedType == nsIX509Cert::ANY_CERT); + + if (!wantThisCert) { + uint32_t thisCertType; + nsresult rv = cert->GetCertType(&thisCertType); + if (NS_FAILED(rv)) { + return rv; + } + if (thisCertType == aWantedType) { + wantThisCert = true; + } + } + + if (wantThisCert) { + int InsertPosition = 0; + for (; InsertPosition < count; ++InsertPosition) { + nsCOMPtr<nsIX509Cert> otherCert = nullptr; + RefPtr<nsCertTreeDispInfo> elem( + mDispInfo.SafeElementAt(InsertPosition, nullptr)); + if (elem) { + otherCert = elem->mCert; + } + if ((*aCertCmpFn)(aCertCmpFnArg, cert, otherCert) < 0) { + break; + } + } + nsCertTreeDispInfo* certdi = new nsCertTreeDispInfo(cert); + mDispInfo.InsertElementAt(InsertPosition, certdi); + ++count; + ++InsertPosition; + } + } + + return NS_OK; +} + +// LoadCerts +// +// Load all of the certificates in the DB for this type. Sort them +// by token, organization, then common name. +NS_IMETHODIMP +nsCertTree::LoadCertsFromCache(const nsTArray<RefPtr<nsIX509Cert>>& aCache, + uint32_t aType) { + if (mTreeArray) { + FreeCertArray(); + delete[] mTreeArray; + mTreeArray = nullptr; + mNumRows = 0; + } + ClearCompareHash(); + + nsresult rv = GetCertsByTypeFromCertList( + aCache, 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 = mDispInfo.ElementAt(j)->mCert; + for (int32_t i = 0; i < mNumOrgs; i++) { + nsString& orgNameRef = mTreeArray[i].orgName; + if (!orgCert) { + 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 = + mDispInfo.SafeElementAt(j, nullptr)->mCert; + while (0 == CmpBy(&mCompareCache, orgCert, nextCert, sort_IssuerOrg, + sort_None, sort_None)) { + mTreeArray[i].numChildren++; + if (++j >= count) break; + nextCert = mDispInfo.SafeElementAt(j, nullptr)->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; + + RefPtr<nsCertTreeDispInfo> certdi( + mDispInfo.SafeElementAt(certIndex, nullptr)); + if (certdi) { + nsCOMPtr<nsIX509Cert> cert = certdi->mCert; + RemoveCacheEntry(cert); + certdb->DeleteCertificate(cert); + } + + mDispInfo.RemoveElementAt(certIndex); + + 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, nsTreeColumn* col, + nsAString& aProps) { + return NS_OK; +} + +NS_IMETHODIMP +nsCertTree::GetColumnProperties(nsTreeColumn* 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, nsTreeColumn* col, nsAString& _retval) { + _retval.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +nsCertTree::GetCellValue(int32_t row, nsTreeColumn* col, nsAString& _retval) { + _retval.Truncate(); + return NS_OK; +} + +static void PRTimeToLocalDateString(PRTime time, nsAString& result) { + PRExplodedTime explodedTime; + PR_ExplodeTime(time, PR_LocalTimeParameters, &explodedTime); + intl::DateTimeFormat::StyleBag style; + style.date = Some(intl::DateTimeFormat::Style::Long); + style.time = Nothing(); + Unused << intl::AppDateTimeFormat::Format(style, &explodedTime, result); +} + +NS_IMETHODIMP +nsCertTree::GetCellText(int32_t row, nsTreeColumn* col, nsAString& _retval) { + if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED; + + nsresult rv = NS_OK; + _retval.Truncate(); + + const nsAString& colID = col->GetId(); + + treeArrayEl* el = GetThreadDescAtIndex(row); + if (el) { + if (u"certcol"_ns.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; + + int32_t colIndex = col->Index(); + 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 (u"certcol"_ns.Equals(colID)) { + if (!cert) { + rv = GetPIPNSSBundleString("CertNotStored", _retval); + } else { + rv = cert->GetDisplayName(_retval); + } + } else if (u"tokencol"_ns.Equals(colID) && cert) { + rv = cert->GetTokenName(_retval); + } else if (u"emailcol"_ns.Equals(colID) && cert) { + rv = cert->GetEmailAddress(_retval); + } else if (u"issuedcol"_ns.Equals(colID) && cert) { + nsCOMPtr<nsIX509CertValidity> validity; + + rv = cert->GetValidity(getter_AddRefs(validity)); + if (NS_SUCCEEDED(rv)) { + PRTime notBefore; + rv = validity->GetNotBefore(¬Before); + if (NS_SUCCEEDED(rv)) { + PRTimeToLocalDateString(notBefore, _retval); + } + } + } else if (u"expiredcol"_ns.Equals(colID) && cert) { + nsCOMPtr<nsIX509CertValidity> validity; + + rv = cert->GetValidity(getter_AddRefs(validity)); + if (NS_SUCCEEDED(rv)) { + PRTime notAfter; + rv = validity->GetNotAfter(¬After); + if (NS_SUCCEEDED(rv)) { + PRTimeToLocalDateString(notAfter, _retval); + } + } + } else if (u"serialnumcol"_ns.Equals(colID) && cert) { + rv = cert->GetSerialNumber(_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); + } + return rv; +} + +NS_IMETHODIMP +nsCertTree::SetTree(mozilla::dom::XULTreeElement* 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); + mTree->InvalidateRow(index); + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsCertTree::CycleHeader(nsTreeColumn* col) { return NS_OK; } + +NS_IMETHODIMP +nsCertTree::SelectionChangedXPCOM() { return NS_ERROR_NOT_IMPLEMENTED; } + +NS_IMETHODIMP +nsCertTree::CycleCell(int32_t row, nsTreeColumn* col) { return NS_OK; } + +NS_IMETHODIMP +nsCertTree::IsEditable(int32_t row, nsTreeColumn* col, bool* _retval) { + *_retval = false; + return NS_OK; +} + +NS_IMETHODIMP +nsCertTree::SetCellValue(int32_t row, nsTreeColumn* col, + const nsAString& value) { + return NS_OK; +} + +NS_IMETHODIMP +nsCertTree::SetCellText(int32_t row, nsTreeColumn* col, + const nsAString& value) { + return NS_OK; +} + +// +// CanDrop +// +NS_IMETHODIMP nsCertTree::CanDrop(int32_t index, int32_t orientation, + mozilla::dom::DataTransfer* aDataTransfer, + bool* _retval) { + NS_ENSURE_ARG_POINTER(_retval); + *_retval = false; + + return NS_OK; +} + +// +// Drop +// +NS_IMETHODIMP nsCertTree::Drop(int32_t row, int32_t orient, + mozilla::dom::DataTransfer* 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; + nsString& 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(¬Before); + } + + 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); + } + + nsString& str_a = ace->mCrit[level]; + nsString& str_b = bce->mCrit[level]; + + int32_t result; + if (!str_a.IsVoid() && !str_b.IsVoid()) + result = Compare(str_a, str_b, nsCaseInsensitiveStringComparator); + else + result = str_a.IsVoid() ? (str_b.IsVoid() ? 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::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); +} |