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);