tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsCertTree.cpp (23759B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "nsCertTree.h"
      6 
      7 #include "ScopedNSSTypes.h"
      8 #include "mozilla/Logging.h"
      9 #include "mozilla/Maybe.h"
     10 #include "mozilla/intl/AppDateTimeFormat.h"
     11 #include "nsArray.h"
     12 #include "nsArrayUtils.h"
     13 #include "nsHashKeys.h"
     14 #include "nsISupportsPrimitives.h"
     15 #include "nsIX509CertDB.h"
     16 #include "nsIX509Cert.h"
     17 #include "nsIX509CertValidity.h"
     18 #include "nsNSSCertHelper.h"
     19 #include "nsNSSCertificate.h"
     20 #include "nsComponentManagerUtils.h"
     21 #include "nsNSSCertificateDB.h"
     22 #include "nsNSSHelper.h"
     23 #include "nsReadableUtils.h"
     24 #include "nsTHashtable.h"
     25 #include "nsUnicharUtils.h"
     26 #include "nsXPCOMCID.h"
     27 #include "nsString.h"
     28 #include "nsTreeColumns.h"
     29 #include "mozpkix/pkixtypes.h"
     30 
     31 using namespace mozilla;
     32 
     33 extern LazyLogModule gPIPNSSLog;
     34 
     35 // treeArrayElStr
     36 //
     37 // structure used to hold map of tree.  Each thread (an organization
     38 // field from a cert) has an element in the array.  The numChildren field
     39 // stores the number of certs corresponding to that thread.
     40 struct treeArrayElStr {
     41  nsString orgName;    /* heading for thread                   */
     42  bool open;           /* toggle open state for thread         */
     43  int32_t certIndex;   /* index into cert array for 1st cert   */
     44  int32_t numChildren; /* number of chidren (certs) for thread */
     45 };
     46 
     47 CompareCacheHashEntryPtr::CompareCacheHashEntryPtr() {
     48  entry = new CompareCacheHashEntry;
     49 }
     50 
     51 CompareCacheHashEntryPtr::~CompareCacheHashEntryPtr() { delete entry; }
     52 
     53 CompareCacheHashEntry::CompareCacheHashEntry() : key(nullptr), mCritInit() {
     54  for (int i = 0; i < max_criterions; ++i) {
     55    mCritInit[i] = false;
     56    mCrit[i].SetIsVoid(true);
     57  }
     58 }
     59 
     60 static bool CompareCacheMatchEntry(const PLDHashEntryHdr* hdr,
     61                                   const void* key) {
     62  const CompareCacheHashEntryPtr* entryPtr =
     63      static_cast<const CompareCacheHashEntryPtr*>(hdr);
     64  return entryPtr->entry->key == key;
     65 }
     66 
     67 static void CompareCacheInitEntry(PLDHashEntryHdr* hdr, const void* key) {
     68  new (hdr) CompareCacheHashEntryPtr();
     69  CompareCacheHashEntryPtr* entryPtr =
     70      static_cast<CompareCacheHashEntryPtr*>(hdr);
     71  entryPtr->entry->key = (void*)key;
     72 }
     73 
     74 static void CompareCacheClearEntry(PLDHashTable* table, PLDHashEntryHdr* hdr) {
     75  CompareCacheHashEntryPtr* entryPtr =
     76      static_cast<CompareCacheHashEntryPtr*>(hdr);
     77  entryPtr->~CompareCacheHashEntryPtr();
     78 }
     79 
     80 static const PLDHashTableOps gMapOps = {
     81    PLDHashTable::HashVoidPtrKeyStub, CompareCacheMatchEntry,
     82    PLDHashTable::MoveEntryStub, CompareCacheClearEntry, CompareCacheInitEntry};
     83 
     84 NS_IMPL_ISUPPORTS(nsCertTreeDispInfo, nsICertTreeItem)
     85 
     86 nsCertTreeDispInfo::~nsCertTreeDispInfo() = default;
     87 
     88 NS_IMETHODIMP
     89 nsCertTreeDispInfo::GetCert(nsIX509Cert** aCert) {
     90  NS_ENSURE_ARG(aCert);
     91  nsCOMPtr<nsIX509Cert> cert = mCert;
     92  cert.forget(aCert);
     93  return NS_OK;
     94 }
     95 
     96 NS_IMPL_ISUPPORTS(nsCertTree, nsICertTree, nsITreeView)
     97 
     98 nsCertTree::nsCertTree()
     99    : mTreeArray(nullptr),
    100      mNumOrgs(0),
    101      mNumRows(0),
    102      mCompareCache(&gMapOps, sizeof(CompareCacheHashEntryPtr),
    103                    kInitialCacheLength) {
    104  mCellText = nullptr;
    105 }
    106 
    107 void nsCertTree::ClearCompareHash() {
    108  mCompareCache.ClearAndPrepareForLength(kInitialCacheLength);
    109 }
    110 
    111 nsCertTree::~nsCertTree() { delete[] mTreeArray; }
    112 
    113 void nsCertTree::FreeCertArray() { mDispInfo.Clear(); }
    114 
    115 CompareCacheHashEntry* nsCertTree::getCacheEntry(void* cache, void* aCert) {
    116  PLDHashTable& aCompareCache = *static_cast<PLDHashTable*>(cache);
    117  auto entryPtr = static_cast<CompareCacheHashEntryPtr*>(
    118      aCompareCache.Add(aCert, fallible));
    119  return entryPtr ? entryPtr->entry : nullptr;
    120 }
    121 
    122 void nsCertTree::RemoveCacheEntry(void* key) { mCompareCache.Remove(key); }
    123 
    124 // CountOrganizations
    125 //
    126 // Count the number of different organizations encountered in the cert
    127 // list.
    128 int32_t nsCertTree::CountOrganizations() {
    129  uint32_t i, certCount;
    130  certCount = mDispInfo.Length();
    131  if (certCount == 0) return 0;
    132  nsCOMPtr<nsIX509Cert> orgCert = mDispInfo.ElementAt(0)->mCert;
    133  nsCOMPtr<nsIX509Cert> nextCert = nullptr;
    134  int32_t orgCount = 1;
    135  for (i = 1; i < certCount; i++) {
    136    nextCert = mDispInfo.SafeElementAt(i, nullptr)->mCert;
    137    // XXX we assume issuer org is always criterion 1
    138    if (CmpBy(&mCompareCache, orgCert, nextCert, sort_IssuerOrg, sort_None,
    139              sort_None) != 0) {
    140      orgCert = nextCert;
    141      orgCount++;
    142    }
    143  }
    144  return orgCount;
    145 }
    146 
    147 // GetThreadDescAtIndex
    148 //
    149 // If the row at index is an organization thread, return the collection
    150 // associated with that thread.  Otherwise, return null.
    151 treeArrayEl* nsCertTree::GetThreadDescAtIndex(int32_t index) {
    152  int i, idx = 0;
    153  if (index < 0) return nullptr;
    154  for (i = 0; i < mNumOrgs; i++) {
    155    if (index == idx) {
    156      return &mTreeArray[i];
    157    }
    158    if (mTreeArray[i].open) {
    159      idx += mTreeArray[i].numChildren;
    160    }
    161    idx++;
    162    if (idx > index) break;
    163  }
    164  return nullptr;
    165 }
    166 
    167 //  GetCertAtIndex
    168 //
    169 //  If the row at index is a cert, return that cert.  Otherwise, return null.
    170 already_AddRefed<nsIX509Cert> nsCertTree::GetCertAtIndex(
    171    int32_t index, int32_t* outAbsoluteCertOffset) {
    172  RefPtr<nsCertTreeDispInfo> certdi(
    173      GetDispInfoAtIndex(index, outAbsoluteCertOffset));
    174  if (!certdi) return nullptr;
    175 
    176  nsCOMPtr<nsIX509Cert> ret = certdi->mCert;
    177  return ret.forget();
    178 }
    179 
    180 //  If the row at index is a cert, return that cert.  Otherwise, return null.
    181 already_AddRefed<nsCertTreeDispInfo> nsCertTree::GetDispInfoAtIndex(
    182    int32_t index, int32_t* outAbsoluteCertOffset) {
    183  int i, idx = 0, cIndex = 0, nc;
    184  if (index < 0) return nullptr;
    185  // Loop over the threads
    186  for (i = 0; i < mNumOrgs; i++) {
    187    if (index == idx) return nullptr;  // index is for thread
    188    idx++;                             // get past the thread
    189    nc = (mTreeArray[i].open) ? mTreeArray[i].numChildren : 0;
    190    if (index < idx + nc) {  // cert is within range of this thread
    191      int32_t certIndex = cIndex + index - idx;
    192      if (outAbsoluteCertOffset) *outAbsoluteCertOffset = certIndex;
    193      RefPtr<nsCertTreeDispInfo> certdi(
    194          mDispInfo.SafeElementAt(certIndex, nullptr));
    195      if (certdi) {
    196        return certdi.forget();
    197      }
    198      break;
    199    }
    200    if (mTreeArray[i].open) idx += mTreeArray[i].numChildren;
    201    cIndex += mTreeArray[i].numChildren;
    202    if (idx > index) break;
    203  }
    204  return nullptr;
    205 }
    206 
    207 nsCertTree::nsCertCompareFunc nsCertTree::GetCompareFuncFromCertType(
    208    uint32_t aType) {
    209  switch (aType) {
    210    case nsIX509Cert::ANY_CERT:
    211    case nsIX509Cert::USER_CERT:
    212      return CmpUserCert;
    213    case nsIX509Cert::EMAIL_CERT:
    214      return CmpEmailCert;
    215    case nsIX509Cert::CA_CERT:
    216    default:
    217      return CmpCACert;
    218  }
    219 }
    220 
    221 nsresult nsCertTree::GetCertsByTypeFromCertList(
    222    const nsTArray<RefPtr<nsIX509Cert>>& aCertList, uint32_t aWantedType,
    223    nsCertCompareFunc aCertCmpFn, void* aCertCmpFnArg) {
    224  MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetCertsByTypeFromCertList"));
    225 
    226  nsTHashtable<nsCStringHashKey> allHostPortOverrideKeys;
    227 
    228  if (aWantedType == nsIX509Cert::SERVER_CERT) {
    229    return NS_ERROR_INVALID_ARG;
    230  }
    231 
    232  int count = 0;
    233  for (const auto& cert : aCertList) {
    234    bool wantThisCert = (aWantedType == nsIX509Cert::ANY_CERT);
    235 
    236    if (!wantThisCert) {
    237      uint32_t thisCertType;
    238      nsresult rv = cert->GetCertType(&thisCertType);
    239      if (NS_FAILED(rv)) {
    240        return rv;
    241      }
    242      if (thisCertType == aWantedType) {
    243        wantThisCert = true;
    244      }
    245    }
    246 
    247    if (wantThisCert) {
    248      int InsertPosition = 0;
    249      for (; InsertPosition < count; ++InsertPosition) {
    250        nsCOMPtr<nsIX509Cert> otherCert = nullptr;
    251        RefPtr<nsCertTreeDispInfo> elem(
    252            mDispInfo.SafeElementAt(InsertPosition, nullptr));
    253        if (elem) {
    254          otherCert = elem->mCert;
    255        }
    256        if ((*aCertCmpFn)(aCertCmpFnArg, cert, otherCert) < 0) {
    257          break;
    258        }
    259      }
    260      nsCertTreeDispInfo* certdi = new nsCertTreeDispInfo(cert);
    261      mDispInfo.InsertElementAt(InsertPosition, certdi);
    262      ++count;
    263      ++InsertPosition;
    264    }
    265  }
    266 
    267  return NS_OK;
    268 }
    269 
    270 // LoadCerts
    271 //
    272 // Load all of the certificates in the DB for this type.  Sort them
    273 // by token, organization, then common name.
    274 NS_IMETHODIMP
    275 nsCertTree::LoadCertsFromCache(const nsTArray<RefPtr<nsIX509Cert>>& aCache,
    276                               uint32_t aType) {
    277  if (mTreeArray) {
    278    FreeCertArray();
    279    delete[] mTreeArray;
    280    mTreeArray = nullptr;
    281    mNumRows = 0;
    282  }
    283  ClearCompareHash();
    284 
    285  nsresult rv = GetCertsByTypeFromCertList(
    286      aCache, aType, GetCompareFuncFromCertType(aType), &mCompareCache);
    287  if (NS_FAILED(rv)) {
    288    return rv;
    289  }
    290  return UpdateUIContents();
    291 }
    292 
    293 nsresult nsCertTree::UpdateUIContents() {
    294  uint32_t count = mDispInfo.Length();
    295  mNumOrgs = CountOrganizations();
    296  mTreeArray = new treeArrayEl[mNumOrgs];
    297 
    298  mCellText = nsArrayBase::Create();
    299 
    300  if (count) {
    301    uint32_t j = 0;
    302    nsCOMPtr<nsIX509Cert> orgCert = mDispInfo.ElementAt(j)->mCert;
    303    for (int32_t i = 0; i < mNumOrgs; i++) {
    304      nsString& orgNameRef = mTreeArray[i].orgName;
    305      if (!orgCert) {
    306        GetPIPNSSBundleString("CertOrgUnknown", orgNameRef);
    307      } else {
    308        orgCert->GetIssuerOrganization(orgNameRef);
    309        if (orgNameRef.IsEmpty()) orgCert->GetCommonName(orgNameRef);
    310      }
    311      mTreeArray[i].open = true;
    312      mTreeArray[i].certIndex = j;
    313      mTreeArray[i].numChildren = 1;
    314      if (++j >= count) break;
    315      nsCOMPtr<nsIX509Cert> nextCert =
    316          mDispInfo.SafeElementAt(j, nullptr)->mCert;
    317      while (0 == CmpBy(&mCompareCache, orgCert, nextCert, sort_IssuerOrg,
    318                        sort_None, sort_None)) {
    319        mTreeArray[i].numChildren++;
    320        if (++j >= count) break;
    321        nextCert = mDispInfo.SafeElementAt(j, nullptr)->mCert;
    322      }
    323      orgCert = nextCert;
    324    }
    325  }
    326  if (mTree) {
    327    mTree->BeginUpdateBatch();
    328    mTree->RowCountChanged(0, -mNumRows);
    329  }
    330  mNumRows = count + mNumOrgs;
    331  if (mTree) mTree->EndUpdateBatch();
    332  return NS_OK;
    333 }
    334 
    335 NS_IMETHODIMP
    336 nsCertTree::DeleteEntryObject(uint32_t index) {
    337  if (!mTreeArray) {
    338    return NS_ERROR_FAILURE;
    339  }
    340 
    341  nsCOMPtr<nsIX509CertDB> certdb =
    342      do_GetService("@mozilla.org/security/x509certdb;1");
    343  if (!certdb) {
    344    return NS_ERROR_FAILURE;
    345  }
    346 
    347  int i;
    348  uint32_t idx = 0, cIndex = 0, nc;
    349  // Loop over the threads
    350  for (i = 0; i < mNumOrgs; i++) {
    351    if (index == idx) return NS_OK;  // index is for thread
    352    idx++;                           // get past the thread
    353    nc = (mTreeArray[i].open) ? mTreeArray[i].numChildren : 0;
    354    if (index < idx + nc) {  // cert is within range of this thread
    355      int32_t certIndex = cIndex + index - idx;
    356 
    357      RefPtr<nsCertTreeDispInfo> certdi(
    358          mDispInfo.SafeElementAt(certIndex, nullptr));
    359      if (certdi) {
    360        nsCOMPtr<nsIX509Cert> cert = certdi->mCert;
    361        RemoveCacheEntry(cert);
    362        certdb->DeleteCertificate(cert);
    363      }
    364 
    365      mDispInfo.RemoveElementAt(certIndex);
    366 
    367      delete[] mTreeArray;
    368      mTreeArray = nullptr;
    369      return UpdateUIContents();
    370    }
    371    if (mTreeArray[i].open) idx += mTreeArray[i].numChildren;
    372    cIndex += mTreeArray[i].numChildren;
    373    if (idx > index) break;
    374  }
    375  return NS_ERROR_FAILURE;
    376 }
    377 
    378 //////////////////////////////////////////////////////////////////////////////
    379 //
    380 //  Begin nsITreeView methods
    381 //
    382 /////////////////////////////////////////////////////////////////////////////
    383 
    384 NS_IMETHODIMP
    385 nsCertTree::GetCert(uint32_t aIndex, nsIX509Cert** _cert) {
    386  NS_ENSURE_ARG(_cert);
    387  *_cert = GetCertAtIndex(aIndex).take();
    388  return NS_OK;
    389 }
    390 
    391 NS_IMETHODIMP
    392 nsCertTree::GetTreeItem(uint32_t aIndex, nsICertTreeItem** _treeitem) {
    393  NS_ENSURE_ARG(_treeitem);
    394 
    395  RefPtr<nsCertTreeDispInfo> certdi(GetDispInfoAtIndex(aIndex));
    396  if (!certdi) return NS_ERROR_FAILURE;
    397 
    398  *_treeitem = certdi;
    399  NS_IF_ADDREF(*_treeitem);
    400  return NS_OK;
    401 }
    402 
    403 NS_IMETHODIMP
    404 nsCertTree::GetRowCount(int32_t* aRowCount) {
    405  if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED;
    406  uint32_t count = 0;
    407  for (int32_t i = 0; i < mNumOrgs; i++) {
    408    if (mTreeArray[i].open) {
    409      count += mTreeArray[i].numChildren;
    410    }
    411    count++;
    412  }
    413  *aRowCount = count;
    414  return NS_OK;
    415 }
    416 
    417 NS_IMETHODIMP
    418 nsCertTree::GetSelection(nsITreeSelection** aSelection) {
    419  *aSelection = mSelection;
    420  NS_IF_ADDREF(*aSelection);
    421  return NS_OK;
    422 }
    423 
    424 NS_IMETHODIMP
    425 nsCertTree::SetSelection(nsITreeSelection* aSelection) {
    426  mSelection = aSelection;
    427  return NS_OK;
    428 }
    429 
    430 NS_IMETHODIMP
    431 nsCertTree::GetRowProperties(int32_t index, nsAString& aProps) { return NS_OK; }
    432 
    433 NS_IMETHODIMP
    434 nsCertTree::GetCellProperties(int32_t row, nsTreeColumn* col,
    435                              nsAString& aProps) {
    436  return NS_OK;
    437 }
    438 
    439 NS_IMETHODIMP
    440 nsCertTree::GetColumnProperties(nsTreeColumn* col, nsAString& aProps) {
    441  return NS_OK;
    442 }
    443 NS_IMETHODIMP
    444 nsCertTree::IsContainer(int32_t index, bool* _retval) {
    445  if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED;
    446  treeArrayEl* el = GetThreadDescAtIndex(index);
    447  if (el) {
    448    *_retval = true;
    449  } else {
    450    *_retval = false;
    451  }
    452  return NS_OK;
    453 }
    454 
    455 NS_IMETHODIMP
    456 nsCertTree::IsContainerOpen(int32_t index, bool* _retval) {
    457  if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED;
    458  treeArrayEl* el = GetThreadDescAtIndex(index);
    459  if (el && el->open) {
    460    *_retval = true;
    461  } else {
    462    *_retval = false;
    463  }
    464  return NS_OK;
    465 }
    466 
    467 NS_IMETHODIMP
    468 nsCertTree::IsContainerEmpty(int32_t index, bool* _retval) {
    469  *_retval = !mTreeArray;
    470  return NS_OK;
    471 }
    472 
    473 NS_IMETHODIMP
    474 nsCertTree::IsSeparator(int32_t index, bool* _retval) {
    475  *_retval = false;
    476  return NS_OK;
    477 }
    478 
    479 NS_IMETHODIMP
    480 nsCertTree::GetParentIndex(int32_t rowIndex, int32_t* _retval) {
    481  if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED;
    482  int i, idx = 0;
    483  for (i = 0; i < mNumOrgs && idx < rowIndex; i++, idx++) {
    484    if (mTreeArray[i].open) {
    485      if (rowIndex <= idx + mTreeArray[i].numChildren) {
    486        *_retval = idx;
    487        return NS_OK;
    488      }
    489      idx += mTreeArray[i].numChildren;
    490    }
    491  }
    492  *_retval = -1;
    493  return NS_OK;
    494 }
    495 
    496 NS_IMETHODIMP
    497 nsCertTree::HasNextSibling(int32_t rowIndex, int32_t afterIndex,
    498                           bool* _retval) {
    499  if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED;
    500 
    501  int i, idx = 0;
    502  for (i = 0; i < mNumOrgs && idx <= rowIndex; i++, idx++) {
    503    if (mTreeArray[i].open) {
    504      idx += mTreeArray[i].numChildren;
    505      if (afterIndex <= idx) {
    506        *_retval = afterIndex < idx;
    507        return NS_OK;
    508      }
    509    }
    510  }
    511  *_retval = false;
    512  return NS_OK;
    513 }
    514 
    515 NS_IMETHODIMP
    516 nsCertTree::GetLevel(int32_t index, int32_t* _retval) {
    517  if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED;
    518  treeArrayEl* el = GetThreadDescAtIndex(index);
    519  if (el) {
    520    *_retval = 0;
    521  } else {
    522    *_retval = 1;
    523  }
    524  return NS_OK;
    525 }
    526 
    527 NS_IMETHODIMP
    528 nsCertTree::GetImageSrc(int32_t row, nsTreeColumn* col, nsAString& _retval) {
    529  _retval.Truncate();
    530  return NS_OK;
    531 }
    532 
    533 NS_IMETHODIMP
    534 nsCertTree::GetCellValue(int32_t row, nsTreeColumn* col, nsAString& _retval) {
    535  _retval.Truncate();
    536  return NS_OK;
    537 }
    538 
    539 static void PRTimeToLocalDateString(PRTime time, nsAString& result) {
    540  PRExplodedTime explodedTime;
    541  PR_ExplodeTime(time, PR_LocalTimeParameters, &explodedTime);
    542  intl::DateTimeFormat::StyleBag style;
    543  style.date = Some(intl::DateTimeFormat::Style::Long);
    544  style.time = Nothing();
    545  (void)intl::AppDateTimeFormat::Format(style, &explodedTime, result);
    546 }
    547 
    548 NS_IMETHODIMP
    549 nsCertTree::GetCellText(int32_t row, nsTreeColumn* col, nsAString& _retval) {
    550  if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED;
    551 
    552  nsresult rv = NS_OK;
    553  _retval.Truncate();
    554 
    555  const nsAString& colID = col->GetId();
    556 
    557  treeArrayEl* el = GetThreadDescAtIndex(row);
    558  if (el) {
    559    if (u"certcol"_ns.Equals(colID))
    560      _retval.Assign(el->orgName);
    561    else
    562      _retval.Truncate();
    563    return NS_OK;
    564  }
    565 
    566  int32_t absoluteCertOffset;
    567  RefPtr<nsCertTreeDispInfo> certdi(
    568      GetDispInfoAtIndex(row, &absoluteCertOffset));
    569  if (!certdi) return NS_ERROR_FAILURE;
    570 
    571  nsCOMPtr<nsIX509Cert> cert = certdi->mCert;
    572 
    573  int32_t colIndex = col->Index();
    574  uint32_t arrayIndex = absoluteCertOffset + colIndex * (mNumRows - mNumOrgs);
    575  uint32_t arrayLength = 0;
    576  if (mCellText) {
    577    mCellText->GetLength(&arrayLength);
    578  }
    579  if (arrayIndex < arrayLength) {
    580    nsCOMPtr<nsISupportsString> myString(
    581        do_QueryElementAt(mCellText, arrayIndex));
    582    if (myString) {
    583      myString->GetData(_retval);
    584      return NS_OK;
    585    }
    586  }
    587 
    588  if (u"certcol"_ns.Equals(colID)) {
    589    if (!cert) {
    590      rv = GetPIPNSSBundleString("CertNotStored", _retval);
    591    } else {
    592      rv = cert->GetDisplayName(_retval);
    593    }
    594  } else if (u"tokencol"_ns.Equals(colID) && cert) {
    595    rv = cert->GetTokenName(_retval);
    596  } else if (u"emailcol"_ns.Equals(colID) && cert) {
    597    rv = cert->GetEmailAddress(_retval);
    598  } else if (u"issuedcol"_ns.Equals(colID) && cert) {
    599    nsCOMPtr<nsIX509CertValidity> validity;
    600 
    601    rv = cert->GetValidity(getter_AddRefs(validity));
    602    if (NS_SUCCEEDED(rv)) {
    603      PRTime notBefore;
    604      rv = validity->GetNotBefore(&notBefore);
    605      if (NS_SUCCEEDED(rv)) {
    606        PRTimeToLocalDateString(notBefore, _retval);
    607      }
    608    }
    609  } else if (u"expiredcol"_ns.Equals(colID) && cert) {
    610    nsCOMPtr<nsIX509CertValidity> validity;
    611 
    612    rv = cert->GetValidity(getter_AddRefs(validity));
    613    if (NS_SUCCEEDED(rv)) {
    614      PRTime notAfter;
    615      rv = validity->GetNotAfter(&notAfter);
    616      if (NS_SUCCEEDED(rv)) {
    617        PRTimeToLocalDateString(notAfter, _retval);
    618      }
    619    }
    620  } else if (u"serialnumcol"_ns.Equals(colID) && cert) {
    621    rv = cert->GetSerialNumber(_retval);
    622  } else {
    623    return NS_ERROR_FAILURE;
    624  }
    625  if (mCellText) {
    626    nsCOMPtr<nsISupportsString> text(
    627        do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
    628    NS_ENSURE_SUCCESS(rv, rv);
    629    text->SetData(_retval);
    630    mCellText->ReplaceElementAt(text, arrayIndex);
    631  }
    632  return rv;
    633 }
    634 
    635 NS_IMETHODIMP
    636 nsCertTree::SetTree(mozilla::dom::XULTreeElement* tree) {
    637  mTree = tree;
    638  return NS_OK;
    639 }
    640 
    641 NS_IMETHODIMP
    642 nsCertTree::ToggleOpenState(int32_t index) {
    643  if (!mTreeArray) return NS_ERROR_NOT_INITIALIZED;
    644  treeArrayEl* el = GetThreadDescAtIndex(index);
    645  if (el) {
    646    el->open = !el->open;
    647    int32_t newChildren = (el->open) ? el->numChildren : -el->numChildren;
    648    if (mTree) {
    649      mTree->RowCountChanged(index + 1, newChildren);
    650      mTree->InvalidateRow(index);
    651    }
    652  }
    653  return NS_OK;
    654 }
    655 
    656 NS_IMETHODIMP
    657 nsCertTree::CycleHeader(nsTreeColumn* col) { return NS_OK; }
    658 
    659 NS_IMETHODIMP
    660 nsCertTree::SelectionChangedXPCOM() { return NS_ERROR_NOT_IMPLEMENTED; }
    661 
    662 NS_IMETHODIMP
    663 nsCertTree::CycleCell(int32_t row, nsTreeColumn* col) { return NS_OK; }
    664 
    665 NS_IMETHODIMP
    666 nsCertTree::IsEditable(int32_t row, nsTreeColumn* col, bool* _retval) {
    667  *_retval = false;
    668  return NS_OK;
    669 }
    670 
    671 NS_IMETHODIMP
    672 nsCertTree::SetCellValue(int32_t row, nsTreeColumn* col,
    673                         const nsAString& value) {
    674  return NS_OK;
    675 }
    676 
    677 NS_IMETHODIMP
    678 nsCertTree::SetCellText(int32_t row, nsTreeColumn* col,
    679                        const nsAString& value) {
    680  return NS_OK;
    681 }
    682 
    683 //
    684 // CanDrop
    685 //
    686 NS_IMETHODIMP nsCertTree::CanDrop(int32_t index, int32_t orientation,
    687                                  mozilla::dom::DataTransfer* aDataTransfer,
    688                                  bool* _retval) {
    689  NS_ENSURE_ARG_POINTER(_retval);
    690  *_retval = false;
    691 
    692  return NS_OK;
    693 }
    694 
    695 //
    696 // Drop
    697 //
    698 NS_IMETHODIMP nsCertTree::Drop(int32_t row, int32_t orient,
    699                               mozilla::dom::DataTransfer* aDataTransfer) {
    700  return NS_OK;
    701 }
    702 
    703 //
    704 // IsSorted
    705 //
    706 // ...
    707 //
    708 NS_IMETHODIMP nsCertTree::IsSorted(bool* _retval) {
    709  *_retval = false;
    710  return NS_OK;
    711 }
    712 
    713 #define RETURN_NOTHING
    714 
    715 void nsCertTree::CmpInitCriterion(nsIX509Cert* cert,
    716                                  CompareCacheHashEntry* entry,
    717                                  sortCriterion crit, int32_t level) {
    718  NS_ENSURE_TRUE(cert && entry, RETURN_NOTHING);
    719 
    720  entry->mCritInit[level] = true;
    721  nsString& str = entry->mCrit[level];
    722 
    723  switch (crit) {
    724    case sort_IssuerOrg:
    725      cert->GetIssuerOrganization(str);
    726      if (str.IsEmpty()) cert->GetCommonName(str);
    727      break;
    728    case sort_Org:
    729      cert->GetOrganization(str);
    730      break;
    731    case sort_Token:
    732      cert->GetTokenName(str);
    733      break;
    734    case sort_CommonName:
    735      cert->GetCommonName(str);
    736      break;
    737    case sort_IssuedDateDescending: {
    738      nsresult rv;
    739      nsCOMPtr<nsIX509CertValidity> validity;
    740      PRTime notBefore;
    741 
    742      rv = cert->GetValidity(getter_AddRefs(validity));
    743      if (NS_SUCCEEDED(rv)) {
    744        rv = validity->GetNotBefore(&notBefore);
    745      }
    746 
    747      if (NS_SUCCEEDED(rv)) {
    748        PRExplodedTime explodedTime;
    749        PR_ExplodeTime(notBefore, PR_GMTParameters, &explodedTime);
    750        char datebuf[20];  // 4 + 2 + 2 + 2 + 2 + 2 + 1 = 15
    751        if (0 != PR_FormatTime(datebuf, sizeof(datebuf), "%Y%m%d%H%M%S",
    752                               &explodedTime)) {
    753          str = NS_ConvertASCIItoUTF16(nsDependentCString(datebuf));
    754        }
    755      }
    756    } break;
    757    case sort_Email:
    758      cert->GetEmailAddress(str);
    759      break;
    760    case sort_None:
    761    default:
    762      break;
    763  }
    764 }
    765 
    766 int32_t nsCertTree::CmpByCrit(nsIX509Cert* a, CompareCacheHashEntry* ace,
    767                              nsIX509Cert* b, CompareCacheHashEntry* bce,
    768                              sortCriterion crit, int32_t level) {
    769  NS_ENSURE_TRUE(a && ace && b && bce, 0);
    770 
    771  if (!ace->mCritInit[level]) {
    772    CmpInitCriterion(a, ace, crit, level);
    773  }
    774 
    775  if (!bce->mCritInit[level]) {
    776    CmpInitCriterion(b, bce, crit, level);
    777  }
    778 
    779  nsString& str_a = ace->mCrit[level];
    780  nsString& str_b = bce->mCrit[level];
    781 
    782  int32_t result;
    783  if (!str_a.IsVoid() && !str_b.IsVoid())
    784    result = Compare(str_a, str_b, nsCaseInsensitiveStringComparator);
    785  else
    786    result = str_a.IsVoid() ? (str_b.IsVoid() ? 0 : -1) : 1;
    787 
    788  if (sort_IssuedDateDescending == crit) result *= -1;  // reverse compare order
    789 
    790  return result;
    791 }
    792 
    793 int32_t nsCertTree::CmpBy(void* cache, nsIX509Cert* a, nsIX509Cert* b,
    794                          sortCriterion c0, sortCriterion c1,
    795                          sortCriterion c2) {
    796  // This will be called when comparing items for display sorting.
    797  // Some items might have no cert associated, so either a or b is null.
    798  // We want all those orphans show at the top of the list,
    799  // so we treat a null cert as "smaller" by returning -1.
    800  // We don't try to sort within the group of no-cert entries,
    801  // so we treat them as equal wrt sort order.
    802 
    803  if (!a && !b) return 0;
    804 
    805  if (!a) return -1;
    806 
    807  if (!b) return 1;
    808 
    809  NS_ENSURE_TRUE(cache && a && b, 0);
    810 
    811  CompareCacheHashEntry* ace = getCacheEntry(cache, a);
    812  CompareCacheHashEntry* bce = getCacheEntry(cache, b);
    813 
    814  int32_t cmp;
    815  cmp = CmpByCrit(a, ace, b, bce, c0, 0);
    816  if (cmp != 0) return cmp;
    817 
    818  if (c1 != sort_None) {
    819    cmp = CmpByCrit(a, ace, b, bce, c1, 1);
    820    if (cmp != 0) return cmp;
    821 
    822    if (c2 != sort_None) {
    823      return CmpByCrit(a, ace, b, bce, c2, 2);
    824    }
    825  }
    826 
    827  return cmp;
    828 }
    829 
    830 int32_t nsCertTree::CmpCACert(void* cache, nsIX509Cert* a, nsIX509Cert* b) {
    831  // XXX we assume issuer org is always criterion 1
    832  return CmpBy(cache, a, b, sort_IssuerOrg, sort_Org, sort_Token);
    833 }
    834 
    835 int32_t nsCertTree::CmpUserCert(void* cache, nsIX509Cert* a, nsIX509Cert* b) {
    836  // XXX we assume issuer org is always criterion 1
    837  return CmpBy(cache, a, b, sort_IssuerOrg, sort_Token,
    838               sort_IssuedDateDescending);
    839 }
    840 
    841 int32_t nsCertTree::CmpEmailCert(void* cache, nsIX509Cert* a, nsIX509Cert* b) {
    842  // XXX we assume issuer org is always criterion 1
    843  return CmpBy(cache, a, b, sort_IssuerOrg, sort_Email, sort_CommonName);
    844 }