tor-browser

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

certManager.js (24837B)


      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 "use strict";
      6 
      7 const gCertFileTypes = "*.p7b; *.crt; *.cert; *.cer; *.pem; *.der";
      8 
      9 var { NetUtil } = ChromeUtils.importESModule(
     10  "resource://gre/modules/NetUtil.sys.mjs"
     11 );
     12 const { exportToFile, viewCertHelper } = ChromeUtils.importESModule(
     13  "resource://gre/modules/psm/pippki.sys.mjs"
     14 );
     15 
     16 var key;
     17 
     18 var certdialogs = Cc["@mozilla.org/nsCertificateDialogs;1"].getService(
     19  Ci.nsICertificateDialogs
     20 );
     21 
     22 /**
     23 * List of certs currently selected in the active tab.
     24 *
     25 * @type {nsIX509Cert[]}
     26 */
     27 var selected_certs = [];
     28 var selected_tree_items = [];
     29 var selected_index = [];
     30 var certdb;
     31 
     32 /**
     33 * Cert tree for the "Authorities" tab.
     34 *
     35 * @type {nsICertTree}
     36 */
     37 var caTreeView;
     38 /**
     39 * Cert tree for the "Servers" tab.
     40 *
     41 * @type {nsICertTree}
     42 */
     43 var serverTreeView;
     44 
     45 var overrideService;
     46 
     47 function createRichlistItem(item) {
     48  let innerHbox = document.createXULElement("hbox");
     49  innerHbox.setAttribute("align", "center");
     50  innerHbox.setAttribute("flex", "1");
     51 
     52  let row = document.createXULElement("label");
     53  row.setAttribute("flex", "1");
     54  row.setAttribute("crop", "end");
     55  row.setAttribute("style", "margin-inline-start: 15px;");
     56  if ("raw" in item) {
     57    row.setAttribute("value", item.raw);
     58  } else {
     59    document.l10n.setAttributes(row, item.l10nid);
     60  }
     61  row.setAttribute("ordinal", "1");
     62  innerHbox.appendChild(row);
     63 
     64  return innerHbox;
     65 }
     66 
     67 var serverRichList = {
     68  richlist: undefined,
     69 
     70  buildRichList() {
     71    let overrides = overrideService.getOverrides().map(item => {
     72      return {
     73        hostPort: item.hostPort,
     74        asciiHost: item.asciiHost,
     75        port: item.port,
     76        originAttributes: item.originAttributes,
     77        fingerprint: item.fingerprint,
     78      };
     79    });
     80    overrides.sort((a, b) => {
     81      let criteria = ["hostPort", "fingerprint"];
     82      for (let c of criteria) {
     83        let res = a[c].localeCompare(b[c]);
     84        if (res !== 0) {
     85          return res;
     86        }
     87      }
     88      return 0;
     89    });
     90 
     91    this.richlist.textContent = "";
     92    this.richlist.clearSelection();
     93 
     94    let frag = document.createDocumentFragment();
     95    for (let override of overrides) {
     96      let richlistitem = this._richBoxAddItem(override);
     97      frag.appendChild(richlistitem);
     98    }
     99    this.richlist.appendChild(frag);
    100 
    101    this._setButtonState();
    102    this.richlist.addEventListener("select", () => this._setButtonState());
    103  },
    104 
    105  _richBoxAddItem(item) {
    106    let richlistitem = document.createXULElement("richlistitem");
    107 
    108    richlistitem.setAttribute("host", item.asciiHost);
    109    richlistitem.setAttribute("port", item.port);
    110    richlistitem.setAttribute("hostPort", item.hostPort);
    111    richlistitem.setAttribute("fingerprint", item.fingerprint);
    112    richlistitem.setAttribute(
    113      "originAttributes",
    114      JSON.stringify(item.originAttributes)
    115    );
    116 
    117    let hbox = document.createXULElement("hbox");
    118    hbox.setAttribute("flex", "1");
    119    hbox.setAttribute("equalsize", "always");
    120 
    121    hbox.appendChild(createRichlistItem({ raw: item.hostPort }));
    122    hbox.appendChild(createRichlistItem({ raw: item.fingerprint }));
    123 
    124    richlistitem.appendChild(hbox);
    125 
    126    return richlistitem;
    127  },
    128 
    129  deleteSelectedRichListItem() {
    130    let selectedItem = this.richlist.selectedItem;
    131    if (!selectedItem) {
    132      return;
    133    }
    134 
    135    let retVals = {
    136      deleteConfirmed: false,
    137    };
    138    window.browsingContext.topChromeWindow.openDialog(
    139      "chrome://pippki/content/deletecert.xhtml",
    140      "",
    141      "chrome,centerscreen,modal",
    142      "websites_tab",
    143      [
    144        {
    145          hostPort: selectedItem.attributes.hostPort.value,
    146        },
    147      ],
    148      retVals
    149    );
    150 
    151    if (retVals.deleteConfirmed) {
    152      overrideService.clearValidityOverride(
    153        selectedItem.attributes.host.value,
    154        selectedItem.attributes.port.value,
    155        JSON.parse(selectedItem.attributes.originAttributes.value)
    156      );
    157      this.buildRichList();
    158    }
    159  },
    160 
    161  addException() {
    162    let params = {
    163      exceptionAdded: false,
    164    };
    165    let closedCallback = () => {
    166      if (params.exceptionAdded) {
    167        this.buildRichList();
    168      }
    169    };
    170    // Try to use a subdialog, if available.
    171    let cur = window;
    172    let prev = null;
    173    while (cur != prev) {
    174      if (cur.gSubDialog) {
    175        cur.gSubDialog.open(
    176          "chrome://pippki/content/exceptionDialog.xhtml",
    177          { features: "chrome,centerscreen,modal", closedCallback },
    178          params
    179        );
    180        return;
    181      }
    182      prev = cur;
    183      cur = cur.parent;
    184    }
    185    // Otherwise, fall back to a dialog.
    186    window.browsingContext.topChromeWindow.openDialog(
    187      "chrome://pippki/content/exceptionDialog.xhtml",
    188      "",
    189      "chrome,centerscreen,modal",
    190      params
    191    );
    192    closedCallback();
    193  },
    194 
    195  _setButtonState() {
    196    let websiteDeleteButton = document.getElementById("websites_deleteButton");
    197    websiteDeleteButton.disabled = this.richlist.selectedIndex < 0;
    198  },
    199 };
    200 /**
    201 * Cert tree for the "People" tab.
    202 *
    203 * @type {nsICertTree}
    204 */
    205 var emailTreeView;
    206 /**
    207 * Cert tree for the "Your Certificates" tab.
    208 *
    209 * @type {nsICertTree}
    210 */
    211 var userTreeView;
    212 
    213 var clientAuthRememberService;
    214 
    215 var rememberedDecisionsRichList = {
    216  richlist: undefined,
    217 
    218  buildRichList() {
    219    let rememberedDecisions = clientAuthRememberService.getDecisions();
    220 
    221    let oldItems = this.richlist.querySelectorAll("richlistitem");
    222    for (let item of oldItems) {
    223      item.remove();
    224    }
    225 
    226    let frag = document.createDocumentFragment();
    227    for (let decision of rememberedDecisions) {
    228      let richlistitem = this._richBoxAddItem(decision);
    229      frag.appendChild(richlistitem);
    230    }
    231    this.richlist.appendChild(frag);
    232 
    233    this.richlist.addEventListener("select", () => this.setButtonState());
    234  },
    235 
    236  _richBoxAddItem(item) {
    237    let richlistitem = document.createXULElement("richlistitem");
    238 
    239    richlistitem.setAttribute("entryKey", item.entryKey);
    240    richlistitem.setAttribute("dbKey", item.dbKey);
    241 
    242    let hbox = document.createXULElement("hbox");
    243    hbox.setAttribute("flex", "1");
    244    hbox.setAttribute("equalsize", "always");
    245 
    246    hbox.appendChild(createRichlistItem({ raw: item.asciiHost }));
    247    if (item.dbKey == "") {
    248      hbox.appendChild(
    249        createRichlistItem({ l10nid: "send-no-client-certificate" })
    250      );
    251 
    252      hbox.appendChild(createRichlistItem({ raw: "" }));
    253    } else {
    254      let tmpCert = certdb.findCertByDBKey(item.dbKey);
    255      // The certificate corresponding to this item's dbKey may not be
    256      // available (for example, if it was stored on a token that's been
    257      // removed, or if it was deleted).
    258      if (tmpCert) {
    259        hbox.appendChild(createRichlistItem({ raw: tmpCert.commonName }));
    260        hbox.appendChild(createRichlistItem({ raw: tmpCert.serialNumber }));
    261      } else {
    262        hbox.appendChild(
    263          createRichlistItem({ l10nid: "certificate-not-available" })
    264        );
    265        hbox.appendChild(
    266          createRichlistItem({ l10nid: "certificate-not-available" })
    267        );
    268      }
    269    }
    270 
    271    richlistitem.appendChild(hbox);
    272 
    273    return richlistitem;
    274  },
    275 
    276  deleteSelectedRichListItem() {
    277    let selectedItem = this.richlist.selectedItem;
    278    let index = this.richlist.selectedIndex;
    279    if (index < 0) {
    280      return;
    281    }
    282 
    283    clientAuthRememberService.forgetRememberedDecision(
    284      selectedItem.attributes.entryKey.value
    285    );
    286 
    287    this.buildRichList();
    288    this.setButtonState();
    289  },
    290 
    291  viewSelectedRichListItem() {
    292    let selectedItem = this.richlist.selectedItem;
    293    let index = this.richlist.selectedIndex;
    294    if (index < 0) {
    295      return;
    296    }
    297 
    298    if (selectedItem.attributes.dbKey.value != "") {
    299      let cert = certdb.findCertByDBKey(selectedItem.attributes.dbKey.value);
    300      viewCertHelper(window, cert);
    301    }
    302  },
    303 
    304  setButtonState() {
    305    let rememberedDeleteButton = document.getElementById(
    306      "remembered_deleteButton"
    307    );
    308    let rememberedViewButton = document.getElementById("remembered_viewButton");
    309 
    310    rememberedDeleteButton.disabled = this.richlist.selectedIndex < 0;
    311    rememberedViewButton.disabled =
    312      this.richlist.selectedItem == null
    313        ? true
    314        : this.richlist.selectedItem.attributes.dbKey.value == "";
    315  },
    316 };
    317 
    318 function LoadCerts() {
    319  certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
    320    Ci.nsIX509CertDB
    321  );
    322  var certcache = certdb.getCerts();
    323 
    324  caTreeView = Cc["@mozilla.org/security/nsCertTree;1"].createInstance(
    325    Ci.nsICertTree
    326  );
    327  caTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.CA_CERT);
    328  document.getElementById("ca-tree").view = caTreeView;
    329 
    330  emailTreeView = Cc["@mozilla.org/security/nsCertTree;1"].createInstance(
    331    Ci.nsICertTree
    332  );
    333  emailTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.EMAIL_CERT);
    334  document.getElementById("email-tree").view = emailTreeView;
    335 
    336  userTreeView = Cc["@mozilla.org/security/nsCertTree;1"].createInstance(
    337    Ci.nsICertTree
    338  );
    339  userTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.USER_CERT);
    340  document.getElementById("user-tree").view = userTreeView;
    341 
    342  clientAuthRememberService = Cc[
    343    "@mozilla.org/security/clientAuthRememberService;1"
    344  ].getService(Ci.nsIClientAuthRememberService);
    345 
    346  overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(
    347    Ci.nsICertOverrideService
    348  );
    349 
    350  rememberedDecisionsRichList.richlist =
    351    document.getElementById("rememberedList");
    352  serverRichList.richlist = document.getElementById("serverList");
    353 
    354  rememberedDecisionsRichList.buildRichList();
    355  serverRichList.buildRichList();
    356 
    357  rememberedDecisionsRichList.setButtonState();
    358 
    359  enableBackupAllButton();
    360 
    361  document
    362    .getElementById("certmanagertabs")
    363    .addEventListener("command", event => {
    364      switch (event.target.id) {
    365        case "mine_viewButton":
    366          viewCerts();
    367          break;
    368        case "mine_backupButton":
    369          backupCerts();
    370          break;
    371        case "mine_backupAllButton":
    372          backupAllCerts();
    373          break;
    374        case "mine_restoreButton":
    375          restoreCerts();
    376          break;
    377        case "mine_deleteButton":
    378          deleteCerts();
    379          break;
    380        case "remembered_deleteButton":
    381          rememberedDecisionsRichList.deleteSelectedRichListItem();
    382          break;
    383        case "remembered_viewButton":
    384          rememberedDecisionsRichList.viewSelectedRichListItem();
    385          break;
    386        case "email_viewButton":
    387          viewCerts();
    388          break;
    389        case "email_addButton":
    390          addEmailCert();
    391          break;
    392        case "email_exportButton":
    393          exportCerts();
    394          break;
    395        case "email_deleteButton":
    396          deleteCerts();
    397          break;
    398        case "websites_deleteButton":
    399          serverRichList.deleteSelectedRichListItem();
    400          break;
    401        case "websites_exceptionButton":
    402          serverRichList.addException();
    403          break;
    404        case "ca_viewButton":
    405          viewCerts();
    406          break;
    407        case "ca_editButton":
    408          editCerts();
    409          break;
    410        case "ca_addButton":
    411          addCACerts();
    412          break;
    413        case "ca_exportButton":
    414          exportCerts();
    415          break;
    416        case "ca_deleteButton":
    417          deleteCerts();
    418          break;
    419        default:
    420          // Default means that we are not handling a command so we should
    421          // probably let people know.
    422          throw new Error("Unhandled command event");
    423      }
    424    });
    425 
    426  document
    427    .getElementById("user-tree")
    428    .addEventListener("select", mine_enableButtons);
    429  document
    430    .getElementById("user-tree-children")
    431    .addEventListener("dblclick", viewCerts);
    432  document
    433    .getElementById("email-tree")
    434    .addEventListener("select", email_enableButtons);
    435  document
    436    .getElementById("email-tree-children")
    437    .addEventListener("dblclick", viewCerts);
    438  document
    439    .getElementById("serverList")
    440    .addEventListener("dblclick", () =>
    441      serverRichList.viewSelectedRichListItem()
    442    );
    443  document
    444    .getElementById("ca-tree")
    445    .addEventListener("select", ca_enableButtons);
    446  document
    447    .getElementById("ca-tree-children")
    448    .addEventListener("dblclick", viewCerts);
    449 }
    450 
    451 function enableBackupAllButton() {
    452  let backupAllButton = document.getElementById("mine_backupAllButton");
    453  backupAllButton.disabled = userTreeView.rowCount < 1;
    454 }
    455 
    456 function getSelectedCerts() {
    457  var ca_tab = document.getElementById("ca_tab");
    458  var mine_tab = document.getElementById("mine_tab");
    459  var others_tab = document.getElementById("others_tab");
    460  var items = null;
    461  if (ca_tab.selected) {
    462    items = caTreeView.selection;
    463  } else if (mine_tab.selected) {
    464    items = userTreeView.selection;
    465  } else if (others_tab.selected) {
    466    items = emailTreeView.selection;
    467  }
    468  selected_certs = [];
    469  var cert = null;
    470  var nr = 0;
    471  if (items != null) {
    472    nr = items.getRangeCount();
    473  }
    474  if (nr > 0) {
    475    for (let i = 0; i < nr; i++) {
    476      var o1 = {};
    477      var o2 = {};
    478      items.getRangeAt(i, o1, o2);
    479      var min = o1.value;
    480      var max = o2.value;
    481      for (let j = min; j <= max; j++) {
    482        if (ca_tab.selected) {
    483          cert = caTreeView.getCert(j);
    484        } else if (mine_tab.selected) {
    485          cert = userTreeView.getCert(j);
    486        } else if (others_tab.selected) {
    487          cert = emailTreeView.getCert(j);
    488        }
    489        if (cert) {
    490          var sc = selected_certs.length;
    491          selected_certs[sc] = cert;
    492          selected_index[sc] = j;
    493        }
    494      }
    495    }
    496  }
    497 }
    498 
    499 function getSelectedTreeItems() {
    500  var ca_tab = document.getElementById("ca_tab");
    501  var mine_tab = document.getElementById("mine_tab");
    502  var others_tab = document.getElementById("others_tab");
    503  var items = null;
    504  if (ca_tab.selected) {
    505    items = caTreeView.selection;
    506  } else if (mine_tab.selected) {
    507    items = userTreeView.selection;
    508  } else if (others_tab.selected) {
    509    items = emailTreeView.selection;
    510  }
    511  selected_certs = [];
    512  selected_tree_items = [];
    513  selected_index = [];
    514  var tree_item = null;
    515  var nr = 0;
    516  if (items != null) {
    517    nr = items.getRangeCount();
    518  }
    519  if (nr > 0) {
    520    for (let i = 0; i < nr; i++) {
    521      var o1 = {};
    522      var o2 = {};
    523      items.getRangeAt(i, o1, o2);
    524      var min = o1.value;
    525      var max = o2.value;
    526      for (let j = min; j <= max; j++) {
    527        if (ca_tab.selected) {
    528          tree_item = caTreeView.getTreeItem(j);
    529        } else if (mine_tab.selected) {
    530          tree_item = userTreeView.getTreeItem(j);
    531        } else if (others_tab.selected) {
    532          tree_item = emailTreeView.getTreeItem(j);
    533        }
    534        if (tree_item) {
    535          var sc = selected_tree_items.length;
    536          selected_tree_items[sc] = tree_item;
    537          selected_index[sc] = j;
    538        }
    539      }
    540    }
    541  }
    542 }
    543 
    544 /**
    545 * Returns true if nothing in the given cert tree is selected or if the
    546 * selection includes a container. Returns false otherwise.
    547 *
    548 * @param {nsICertTree} certTree
    549 * @returns {boolean}
    550 */
    551 function nothingOrContainerSelected(certTree) {
    552  var certTreeSelection = certTree.selection;
    553  var numSelectionRanges = certTreeSelection.getRangeCount();
    554 
    555  if (numSelectionRanges == 0) {
    556    return true;
    557  }
    558 
    559  for (var i = 0; i < numSelectionRanges; i++) {
    560    var o1 = {};
    561    var o2 = {};
    562    certTreeSelection.getRangeAt(i, o1, o2);
    563    var minIndex = o1.value;
    564    var maxIndex = o2.value;
    565    for (var j = minIndex; j <= maxIndex; j++) {
    566      if (certTree.isContainer(j)) {
    567        return true;
    568      }
    569    }
    570  }
    571 
    572  return false;
    573 }
    574 
    575 async function promptError(aErrorCode) {
    576  if (aErrorCode != Ci.nsIX509CertDB.Success) {
    577    let msgName = "pkcs12-unknown-err";
    578    switch (aErrorCode) {
    579      case Ci.nsIX509CertDB.ERROR_PKCS12_NOSMARTCARD_EXPORT:
    580        msgName = "pkcs12-info-no-smartcard-backup";
    581        break;
    582      case Ci.nsIX509CertDB.ERROR_PKCS12_RESTORE_FAILED:
    583        msgName = "pkcs12-unknown-err-restore";
    584        break;
    585      case Ci.nsIX509CertDB.ERROR_PKCS12_BACKUP_FAILED:
    586        msgName = "pkcs12-unknown-err-backup";
    587        break;
    588      case Ci.nsIX509CertDB.ERROR_PKCS12_CERT_COLLISION:
    589      case Ci.nsIX509CertDB.ERROR_PKCS12_DUPLICATE_DATA:
    590        msgName = "pkcs12-dup-data";
    591        break;
    592      case Ci.nsIX509CertDB.ERROR_BAD_PASSWORD:
    593        msgName = "pk11-bad-password";
    594        break;
    595      case Ci.nsIX509CertDB.ERROR_DECODE_ERROR:
    596        msgName = "pkcs12-decode-err";
    597        break;
    598      default:
    599        break;
    600    }
    601    let [message] = await document.l10n.formatValues([{ id: msgName }]);
    602    let prompter = Services.ww.getNewPrompter(window);
    603    prompter.alert(null, message);
    604  }
    605 }
    606 
    607 /**
    608 * Enables or disables buttons corresponding to a cert tree depending on what
    609 * is selected in the cert tree.
    610 *
    611 * @param {nsICertTree} certTree
    612 * @param {Array} idList A list of string identifiers for button elements to
    613 *    enable or disable.
    614 */
    615 function enableButtonsForCertTree(certTree, idList) {
    616  let disableButtons = nothingOrContainerSelected(certTree);
    617 
    618  for (let id of idList) {
    619    document.getElementById(id).toggleAttribute("disabled", disableButtons);
    620  }
    621 }
    622 
    623 function ca_enableButtons() {
    624  let idList = [
    625    "ca_viewButton",
    626    "ca_editButton",
    627    "ca_exportButton",
    628    "ca_deleteButton",
    629  ];
    630  enableButtonsForCertTree(caTreeView, idList);
    631 }
    632 
    633 function mine_enableButtons() {
    634  let idList = ["mine_viewButton", "mine_backupButton", "mine_deleteButton"];
    635  enableButtonsForCertTree(userTreeView, idList);
    636 }
    637 
    638 function email_enableButtons() {
    639  let idList = ["email_viewButton", "email_exportButton", "email_deleteButton"];
    640  enableButtonsForCertTree(emailTreeView, idList);
    641 }
    642 
    643 async function backupCerts() {
    644  getSelectedCerts();
    645  var numcerts = selected_certs.length;
    646  if (numcerts == 0) {
    647    return;
    648  }
    649 
    650  var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    651  let [backupFileDialog, filePkcs12Spec] = await document.l10n.formatValues([
    652    { id: "choose-p12-backup-file-dialog" },
    653    { id: "file-browse-pkcs12-spec" },
    654  ]);
    655  fp.init(window.browsingContext, backupFileDialog, Ci.nsIFilePicker.modeSave);
    656  fp.appendFilter(filePkcs12Spec, "*.p12");
    657  fp.appendFilters(Ci.nsIFilePicker.filterAll);
    658  fp.defaultExtension = "p12";
    659  fp.open(rv => {
    660    if (
    661      rv == Ci.nsIFilePicker.returnOK ||
    662      rv == Ci.nsIFilePicker.returnReplace
    663    ) {
    664      let password = {};
    665      if (certdialogs.setPKCS12FilePassword(window, password)) {
    666        let errorCode = certdb.exportPKCS12File(
    667          fp.file,
    668          selected_certs,
    669          password.value
    670        );
    671        promptError(errorCode);
    672      }
    673    }
    674  });
    675 }
    676 
    677 function backupAllCerts() {
    678  // Select all rows, then call doBackup()
    679  userTreeView.selection.selectAll();
    680  backupCerts();
    681 }
    682 
    683 function editCerts() {
    684  getSelectedCerts();
    685 
    686  for (let cert of selected_certs) {
    687    window.browsingContext.topChromeWindow.openDialog(
    688      "chrome://pippki/content/editcacert.xhtml",
    689      "",
    690      "chrome,centerscreen,modal",
    691      cert
    692    );
    693  }
    694 }
    695 
    696 async function restoreCerts() {
    697  var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    698  let [restoreFileDialog, filePkcs12Spec, fileCertSpec] =
    699    await document.l10n.formatValues([
    700      { id: "choose-p12-restore-file-dialog" },
    701      { id: "file-browse-pkcs12-spec" },
    702      { id: "file-browse-certificate-spec" },
    703    ]);
    704  fp.init(window.browsingContext, restoreFileDialog, Ci.nsIFilePicker.modeOpen);
    705  fp.appendFilter(filePkcs12Spec, "*.p12; *.pfx");
    706  fp.appendFilter(fileCertSpec, gCertFileTypes);
    707  fp.appendFilters(Ci.nsIFilePicker.filterAll);
    708  fp.open(rv => {
    709    if (rv != Ci.nsIFilePicker.returnOK) {
    710      return;
    711    }
    712 
    713    // If this is an X509 user certificate, import it as one.
    714 
    715    var isX509FileType = false;
    716    var fileTypesList = gCertFileTypes.slice(1).split("; *");
    717    for (var type of fileTypesList) {
    718      if (fp.file.path.endsWith(type)) {
    719        isX509FileType = true;
    720        break;
    721      }
    722    }
    723 
    724    if (isX509FileType) {
    725      let fstream = Cc[
    726        "@mozilla.org/network/file-input-stream;1"
    727      ].createInstance(Ci.nsIFileInputStream);
    728      fstream.init(fp.file, -1, 0, 0);
    729      let dataString = NetUtil.readInputStreamToString(
    730        fstream,
    731        fstream.available()
    732      );
    733      let dataArray = [];
    734      for (let i = 0; i < dataString.length; i++) {
    735        dataArray.push(dataString.charCodeAt(i));
    736      }
    737      fstream.close();
    738      let prompter = Services.ww.getNewPrompter(window);
    739      let interfaceRequestor = {
    740        getInterface() {
    741          return prompter;
    742        },
    743      };
    744      certdb.importUserCertificate(
    745        dataArray,
    746        dataArray.length,
    747        interfaceRequestor
    748      );
    749    } else {
    750      // Otherwise, assume it's a PKCS12 file and import it that way.
    751      let password = {};
    752      let errorCode = Ci.nsIX509CertDB.ERROR_BAD_PASSWORD;
    753      while (
    754        errorCode == Ci.nsIX509CertDB.ERROR_BAD_PASSWORD &&
    755        certdialogs.getPKCS12FilePassword(window, password)
    756      ) {
    757        errorCode = certdb.importPKCS12File(fp.file, password.value);
    758        if (
    759          errorCode == Ci.nsIX509CertDB.ERROR_BAD_PASSWORD &&
    760          !password.value.length
    761        ) {
    762          // It didn't like empty string password, try no password.
    763          errorCode = certdb.importPKCS12File(fp.file, null);
    764        }
    765        promptError(errorCode);
    766      }
    767    }
    768 
    769    var certcache = certdb.getCerts();
    770    userTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.USER_CERT);
    771    userTreeView.selection.clearSelection();
    772    caTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.CA_CERT);
    773    caTreeView.selection.clearSelection();
    774    enableBackupAllButton();
    775  });
    776 }
    777 
    778 async function exportCerts() {
    779  getSelectedCerts();
    780 
    781  for (let cert of selected_certs) {
    782    await exportToFile(window, document, cert);
    783  }
    784 }
    785 
    786 /**
    787 * Deletes the selected certs in the active tab.
    788 */
    789 function deleteCerts() {
    790  getSelectedTreeItems();
    791  let numcerts = selected_tree_items.length;
    792  if (numcerts == 0) {
    793    return;
    794  }
    795 
    796  const treeViewMap = {
    797    mine_tab: userTreeView,
    798    ca_tab: caTreeView,
    799    others_tab: emailTreeView,
    800  };
    801  let selTab = document.getElementById("certMgrTabbox").selectedItem;
    802  let selTabID = selTab.getAttribute("id");
    803 
    804  if (!(selTabID in treeViewMap)) {
    805    return;
    806  }
    807 
    808  let retVals = {
    809    deleteConfirmed: false,
    810  };
    811  window.browsingContext.topChromeWindow.openDialog(
    812    "chrome://pippki/content/deletecert.xhtml",
    813    "",
    814    "chrome,centerscreen,modal",
    815    selTabID,
    816    selected_tree_items,
    817    retVals
    818  );
    819 
    820  if (retVals.deleteConfirmed) {
    821    let treeView = treeViewMap[selTabID];
    822 
    823    for (let t = numcerts - 1; t >= 0; t--) {
    824      treeView.deleteEntryObject(selected_index[t]);
    825    }
    826 
    827    selected_tree_items = [];
    828    selected_index = [];
    829    treeView.selection.clearSelection();
    830    if (selTabID == "mine_tab") {
    831      enableBackupAllButton();
    832    }
    833  }
    834 }
    835 
    836 function viewCerts() {
    837  getSelectedCerts();
    838 
    839  for (let cert of selected_certs) {
    840    viewCertHelper(window, cert);
    841  }
    842 }
    843 
    844 async function addCACerts() {
    845  var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    846  let [importCa, fileCertSpec] = await document.l10n.formatValues([
    847    { id: "import-ca-certs-prompt" },
    848    { id: "file-browse-certificate-spec" },
    849  ]);
    850  fp.init(window.browsingContext, importCa, Ci.nsIFilePicker.modeOpen);
    851  fp.appendFilter(fileCertSpec, gCertFileTypes);
    852  fp.appendFilters(Ci.nsIFilePicker.filterAll);
    853  fp.open(rv => {
    854    if (rv == Ci.nsIFilePicker.returnOK) {
    855      certdb.importCertsFromFile(fp.file, Ci.nsIX509Cert.CA_CERT);
    856      let certcache = certdb.getCerts();
    857      caTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.CA_CERT);
    858      caTreeView.selection.clearSelection();
    859    }
    860  });
    861 }
    862 
    863 async function addEmailCert() {
    864  var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    865  let [importEmail, fileCertSpec] = await document.l10n.formatValues([
    866    { id: "import-email-cert-prompt" },
    867    { id: "file-browse-certificate-spec" },
    868  ]);
    869  fp.init(window.browsingContext, importEmail, Ci.nsIFilePicker.modeOpen);
    870  fp.appendFilter(fileCertSpec, gCertFileTypes);
    871  fp.appendFilters(Ci.nsIFilePicker.filterAll);
    872  fp.open(rv => {
    873    if (rv == Ci.nsIFilePicker.returnOK) {
    874      certdb.importCertsFromFile(fp.file, Ci.nsIX509Cert.EMAIL_CERT);
    875      var certcache = certdb.getCerts();
    876      emailTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.EMAIL_CERT);
    877      emailTreeView.selection.clearSelection();
    878      caTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.CA_CERT);
    879      caTreeView.selection.clearSelection();
    880    }
    881  });
    882 }
    883 
    884 window.addEventListener("load", LoadCerts);