зеркало из https://github.com/mozilla/pjs.git
Add access to the certificate manager to the SMIME panel in the account manager.
This commit is contained in:
Родитель
e8d1963ec9
Коммит
8b93ba5e1b
|
@ -0,0 +1,411 @@
|
|||
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
# The contents of this file are subject to the Netscape Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape
|
||||
# Communications Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998-2001 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributors:
|
||||
# ddrinan@netscape.com
|
||||
# Scott MacGregor <mscott@netscape.com>
|
||||
#
|
||||
|
||||
const nsIX509CertDB = Components.interfaces.nsIX509CertDB;
|
||||
const nsX509CertDBContractID = "@mozilla.org/security/x509certdb;1";
|
||||
const nsIX509Cert = Components.interfaces.nsIX509Cert;
|
||||
|
||||
const email_recipient_cert_usage = 5;
|
||||
const email_signing_cert_usage = 4;
|
||||
|
||||
var gIdentity;
|
||||
var gPref = null;
|
||||
var gEncryptionCertName = null;
|
||||
var gHiddenEncryptionPolicy = null;
|
||||
var gEncryptionChoices = null;
|
||||
var gSignCertName = null;
|
||||
var gSignMessages = null;
|
||||
var gEncryptAlways = null;
|
||||
var gNeverEncrypt = null;
|
||||
var gBundle = null;
|
||||
var gBrandBundle;
|
||||
var gSmimePrefbranch;
|
||||
var gEncryptionChoicesLocked;
|
||||
var gSigningChoicesLocked;
|
||||
const kEncryptionCertPref = "identity.encryption_cert_name";
|
||||
const kSigningCertPref = "identity.signing_cert_name";
|
||||
|
||||
function onInit()
|
||||
{
|
||||
// initialize all of our elements based on the current identity values....
|
||||
gEncryptionCertName = document.getElementById(kEncryptionCertPref);
|
||||
gHiddenEncryptionPolicy = document.getElementById("identity.encryptionpolicy");
|
||||
gEncryptionChoices = document.getElementById("encryptionChoices");
|
||||
gSignCertName = document.getElementById(kSigningCertPref);
|
||||
gSignMessages = document.getElementById("identity.sign_mail");
|
||||
gEncryptAlways = document.getElementById("encrypt_mail_always");
|
||||
gNeverEncrypt = document.getElementById("encrypt_mail_never");
|
||||
gBundle = document.getElementById("bundle_smime");
|
||||
gBrandBundle = document.getElementById("bundle_brand");
|
||||
|
||||
gEncryptionChoicesLocked = false;
|
||||
gSigningChoicesLocked = false;
|
||||
|
||||
gEncryptionCertName.value = gIdentity.getUnicharAttribute("encryption_cert_name");
|
||||
|
||||
var selectedItemId = null;
|
||||
var encryptionPolicy = gIdentity.getIntAttribute("encryptionpolicy");
|
||||
switch (encryptionPolicy)
|
||||
{
|
||||
case 2:
|
||||
selectedItemId = 'encrypt_mail_always';
|
||||
break;
|
||||
default:
|
||||
selectedItemId = 'encrypt_mail_never';
|
||||
break;
|
||||
}
|
||||
|
||||
gEncryptionChoices.selectedItem = document.getElementById(selectedItemId);
|
||||
|
||||
if (!gEncryptionCertName.value)
|
||||
{
|
||||
gEncryptAlways.setAttribute("disabled", true);
|
||||
gNeverEncrypt.setAttribute("disabled", true);
|
||||
}
|
||||
else {
|
||||
enableEncryptionControls(true);
|
||||
}
|
||||
|
||||
gSignCertName.value = gIdentity.getUnicharAttribute("signing_cert_name");
|
||||
gSignMessages.checked = gIdentity.getBoolAttribute("sign_mail");
|
||||
if (!gSignCertName.value)
|
||||
{
|
||||
gSignMessages.setAttribute("disabled", true);
|
||||
}
|
||||
else {
|
||||
enableSigningControls(true);
|
||||
}
|
||||
|
||||
// Always start with enabling signing and encryption cert select buttons.
|
||||
// This will keep the visibility of buttons in a sane state as user
|
||||
// jumps from security panel of one account to another.
|
||||
enableCertSelectButtons();
|
||||
|
||||
// Disable all locked elements on the panel
|
||||
onLockPreference();
|
||||
}
|
||||
|
||||
function onPreInit(account, accountValues)
|
||||
{
|
||||
gIdentity = account.defaultIdentity;
|
||||
}
|
||||
|
||||
function onSave()
|
||||
{
|
||||
// find out which radio for the encryption radio group is selected and set that on our hidden encryptionChoice pref....
|
||||
var newValue = gEncryptionChoices.selectedItem.value;
|
||||
gHiddenEncryptionPolicy.setAttribute('value', newValue);
|
||||
gIdentity.setIntAttribute("encryptionpolicy", newValue);
|
||||
gIdentity.setUnicharAttribute("encryption_cert_name", gEncryptionCertName.value);
|
||||
|
||||
gIdentity.setBoolAttribute("sign_mail", gSignMessages.checked);
|
||||
gIdentity.setUnicharAttribute("signing_cert_name", gSignCertName.value);
|
||||
}
|
||||
|
||||
function onLockPreference()
|
||||
{
|
||||
var initPrefString = "mail.identity";
|
||||
var finalPrefString;
|
||||
|
||||
var prefService = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
|
||||
|
||||
var allPrefElements = [
|
||||
{ prefstring:"signingCertSelectButton", id:"signingCertSelectButton"},
|
||||
{ prefstring:"encryptionCertSelectButton", id:"encryptionCertSelectButton"},
|
||||
{ prefstring:"sign_mail", id:"identity.sign_mail"},
|
||||
{ prefstring:"encryptionpolicy", id:"encryptionChoices"}
|
||||
];
|
||||
|
||||
finalPrefString = initPrefString + "." + gIdentity.key + ".";
|
||||
gSmimePrefbranch = prefService.getBranch(finalPrefString);
|
||||
|
||||
disableIfLocked( allPrefElements );
|
||||
}
|
||||
|
||||
|
||||
// Does the work of disabling an element given the array which contains xul id/prefstring pairs.
|
||||
// Also saves the id/locked state in an array so that other areas of the code can avoid
|
||||
// stomping on the disabled state indiscriminately.
|
||||
function disableIfLocked( prefstrArray )
|
||||
{
|
||||
var i;
|
||||
for (i=0; i<prefstrArray.length; i++) {
|
||||
var id = prefstrArray[i].id;
|
||||
var element = document.getElementById(id);
|
||||
if (gSmimePrefbranch.prefIsLocked(prefstrArray[i].prefstring)) {
|
||||
// If encryption choices radio group is locked, make sure the individual
|
||||
// choices in the group are locked. Set a global (gEncryptionChoicesLocked)
|
||||
// indicating the status so that locking can be maintained further.
|
||||
if (id == "encryptionChoices") {
|
||||
document.getElementById("encrypt_mail_never").setAttribute("disabled", "true");
|
||||
document.getElementById("encrypt_mail_always").setAttribute("disabled", "true");
|
||||
gEncryptionChoicesLocked = true;
|
||||
}
|
||||
// If option to sign mail is locked (with true/false set in config file), disable
|
||||
// the corresponding checkbox and set a global (gSigningChoicesLocked) in order to
|
||||
// honor the locking as user changes other elements on the panel.
|
||||
if (id == "identity.sign_mail") {
|
||||
document.getElementById("identity.sign_mail").setAttribute("disabled", "true");
|
||||
gSigningChoicesLocked = true;
|
||||
}
|
||||
else {
|
||||
element.setAttribute("disabled", "true");
|
||||
if (id == "signingCertSelectButton") {
|
||||
document.getElementById("signingCertClearButton").setAttribute("disabled", "true");
|
||||
}
|
||||
else if (id == "encryptionCertSelectButton") {
|
||||
document.getElementById("encryptionCertClearButton").setAttribute("disabled", "true");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPromptService()
|
||||
{
|
||||
var ifps = Components.interfaces.nsIPromptService;
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
|
||||
if (promptService) {
|
||||
promptService = promptService.QueryInterface(ifps);
|
||||
}
|
||||
return promptService;
|
||||
}
|
||||
|
||||
function alertUser(message)
|
||||
{
|
||||
var ps = getPromptService();
|
||||
if (ps) {
|
||||
ps.alert(
|
||||
window,
|
||||
gBrandBundle.getString("brandShortName"),
|
||||
message);
|
||||
}
|
||||
}
|
||||
|
||||
function askUser(message)
|
||||
{
|
||||
var ps = getPromptService();
|
||||
if (!ps)
|
||||
return false;
|
||||
|
||||
return ps.confirm(
|
||||
window,
|
||||
gBrandBundle.getString("brandShortName"),
|
||||
message);
|
||||
}
|
||||
|
||||
function checkOtherCert(nickname, pref, usage, msgNeedCertWantSame, msgWantSame, msgNeedCertWantToSelect, enabler)
|
||||
{
|
||||
var otherCertInfo = document.getElementById(pref);
|
||||
if (!otherCertInfo)
|
||||
return;
|
||||
|
||||
if (otherCertInfo.value == nickname)
|
||||
// all is fine, same cert is now selected for both purposes
|
||||
return;
|
||||
|
||||
var certdb = Components.classes[nsX509CertDBContractID].getService(nsIX509CertDB);
|
||||
if (!certdb)
|
||||
return;
|
||||
|
||||
if (email_recipient_cert_usage == usage) {
|
||||
matchingOtherCert = certdb.findEmailEncryptionCert(nickname);
|
||||
}
|
||||
else if (email_signing_cert_usage == usage) {
|
||||
matchingOtherCert = certdb.findEmailSigningCert(nickname);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
var userWantsSameCert = false;
|
||||
|
||||
if (!otherCertInfo.value.length) {
|
||||
if (matchingOtherCert) {
|
||||
userWantsSameCert = askUser(gBundle.getString(msgNeedCertWantSame));
|
||||
}
|
||||
else {
|
||||
if (askUser(gBundle.getString(msgNeedCertWantToSelect))) {
|
||||
smimeSelectCert(pref);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (matchingOtherCert) {
|
||||
userWantsSameCert = askUser(gBundle.getString(msgWantSame));
|
||||
}
|
||||
}
|
||||
|
||||
if (userWantsSameCert) {
|
||||
otherCertInfo.value = nickname;
|
||||
enabler(true);
|
||||
}
|
||||
}
|
||||
|
||||
function smimeSelectCert(smime_cert)
|
||||
{
|
||||
var certInfo = document.getElementById(smime_cert);
|
||||
if (!certInfo)
|
||||
return;
|
||||
|
||||
var picker = Components.classes["@mozilla.org/user_cert_picker;1"]
|
||||
.createInstance(Components.interfaces.nsIUserCertPicker);
|
||||
var canceled = new Object;
|
||||
var x509cert = 0;
|
||||
var certUsage;
|
||||
var selectEncryptionCert;
|
||||
|
||||
if (smime_cert == kEncryptionCertPref) {
|
||||
selectEncryptionCert = true;
|
||||
certUsage = email_recipient_cert_usage;
|
||||
} else if (smime_cert == kSigningCertPref) {
|
||||
selectEncryptionCert = false;
|
||||
certUsage = email_signing_cert_usage;
|
||||
}
|
||||
|
||||
try {
|
||||
x509cert = picker.pickByUsage(window,
|
||||
certInfo.value,
|
||||
certUsage, // this is from enum SECCertUsage
|
||||
false, false, canceled);
|
||||
} catch(e) {
|
||||
canceled.value = false;
|
||||
x509cert = null;
|
||||
}
|
||||
|
||||
if (!canceled.value) {
|
||||
if (!x509cert) {
|
||||
var errorString;
|
||||
if (selectEncryptionCert) {
|
||||
errorString = "NoEncryptionCert";
|
||||
}
|
||||
else {
|
||||
errorString = "NoSigningCert";
|
||||
}
|
||||
alertUser(gBundle.getString(errorString));
|
||||
}
|
||||
else {
|
||||
certInfo.removeAttribute("disabled");
|
||||
certInfo.value = x509cert.nickname;
|
||||
|
||||
if (selectEncryptionCert) {
|
||||
enableEncryptionControls(true);
|
||||
|
||||
checkOtherCert(certInfo.value,
|
||||
kSigningCertPref, email_signing_cert_usage,
|
||||
"signing_needCertWantSame",
|
||||
"signing_wantSame",
|
||||
"signing_needCertWantToSelect",
|
||||
enableSigningControls);
|
||||
} else {
|
||||
enableSigningControls(true);
|
||||
|
||||
checkOtherCert(certInfo.value,
|
||||
kEncryptionCertPref, email_recipient_cert_usage,
|
||||
"encryption_needCertWantSame",
|
||||
"encryption_wantSame",
|
||||
"encryption_needCertWantToSelect",
|
||||
enableEncryptionControls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enableCertSelectButtons();
|
||||
}
|
||||
|
||||
function enableEncryptionControls(do_enable)
|
||||
{
|
||||
if (gEncryptionChoicesLocked)
|
||||
return;
|
||||
|
||||
if (do_enable) {
|
||||
gEncryptAlways.removeAttribute("disabled");
|
||||
gNeverEncrypt.removeAttribute("disabled");
|
||||
}
|
||||
else {
|
||||
gEncryptAlways.setAttribute("disabled", "true");
|
||||
gNeverEncrypt.setAttribute("disabled", "true");
|
||||
gEncryptionChoices.selectedItem = document.getElementById('encrypt_mail_never');
|
||||
}
|
||||
}
|
||||
|
||||
function enableSigningControls(do_enable)
|
||||
{
|
||||
if (gSigningChoicesLocked)
|
||||
return;
|
||||
|
||||
if (do_enable) {
|
||||
gSignMessages.removeAttribute("disabled");
|
||||
}
|
||||
else {
|
||||
gSignMessages.setAttribute("disabled", "true");
|
||||
gSignMessages.checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
function enableCertSelectButtons()
|
||||
{
|
||||
document.getElementById("signingCertSelectButton").removeAttribute("disabled");
|
||||
|
||||
if (document.getElementById('identity.signing_cert_name').value.length)
|
||||
document.getElementById("signingCertClearButton").removeAttribute("disabled");
|
||||
else
|
||||
document.getElementById("signingCertClearButton").setAttribute("disabled", "true");
|
||||
|
||||
document.getElementById("encryptionCertSelectButton").removeAttribute("disabled");
|
||||
|
||||
if (document.getElementById('identity.encryption_cert_name').value.length)
|
||||
document.getElementById("encryptionCertClearButton").removeAttribute("disabled");
|
||||
else
|
||||
document.getElementById("encryptionCertClearButton").setAttribute("disabled", "true");
|
||||
}
|
||||
|
||||
function smimeClearCert(smime_cert)
|
||||
{
|
||||
var certInfo = document.getElementById(smime_cert);
|
||||
if (!certInfo)
|
||||
return;
|
||||
|
||||
certInfo.setAttribute("disabled", "true");
|
||||
certInfo.value = "";
|
||||
|
||||
if (smime_cert == kEncryptionCertPref) {
|
||||
enableEncryptionControls(false);
|
||||
} else if (smime_cert == kSigningCertPref) {
|
||||
enableSigningControls(false);
|
||||
}
|
||||
|
||||
enableCertSelectButtons();
|
||||
}
|
||||
|
||||
function openCertManager()
|
||||
{
|
||||
//check for an existing certManager window and focus it; it's not application modal
|
||||
const kWindowMediatorContractID = "@mozilla.org/appshell/window-mediator;1";
|
||||
const kWindowMediatorIID = Components.interfaces.nsIWindowMediator;
|
||||
const kWindowMediator = Components.classes[kWindowMediatorContractID].getService(kWindowMediatorIID);
|
||||
var lastCertManager = kWindowMediator.getMostRecentWindow("mozilla:certmanager");
|
||||
if (lastCertManager)
|
||||
lastCertManager.focus();
|
||||
else
|
||||
window.open('chrome://pippki/content/certManager.xul', "",
|
||||
'chrome,width=500,height=400,resizable=yes,dialog=no');
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
#
|
||||
# The contents of this file are subject to the Netscape Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape
|
||||
# Communications Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998-2001 Netscape Communications Corporation. All Rights Reserved.
|
||||
#
|
||||
#
|
||||
# Contributors:
|
||||
# ddrinan@netscape.com
|
||||
# Scott MacGregor <mscott@netscape.com>
|
||||
#
|
||||
|
||||
<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE page SYSTEM "chrome://messenger/locale/am-smime.dtd">
|
||||
|
||||
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
class="color-dialog"
|
||||
onload="parent.onPanelLoaded('am-smime.xul');"
|
||||
orient="vertical">
|
||||
|
||||
<stringbundle id="bundle_smime" src="chrome://messenger/locale/am-smime.properties"/>
|
||||
<stringbundle id="bundle_brand" src="chrome://global/locale/brand.properties"/>
|
||||
<script type="application/x-javascript" src="chrome://messenger/content/AccountManager.js"/>
|
||||
<script type="application/x-javascript" src="chrome://messenger/content/am-smime.js"/>
|
||||
|
||||
<dialogheader title="&securityTitle.label;"/>
|
||||
|
||||
<label hidden="true" wsm_persist="true" id="identity.encryptionpolicy" />
|
||||
|
||||
<description>&securityHeading.label;</description>
|
||||
|
||||
<groupbox id="signing.titlebox">
|
||||
<caption label="&signingGroupTitle.label;"/>
|
||||
|
||||
<label value="&signingCert.message;"/>
|
||||
|
||||
<hbox align="center">
|
||||
<textbox id="identity.signing_cert_name" wsm_persist="true" flex="1"
|
||||
readonly="true" disabled="true"/>
|
||||
|
||||
<button id="signingCertSelectButton"
|
||||
label="&certificate.button;"
|
||||
oncommand="smimeSelectCert('identity.signing_cert_name')"/>
|
||||
|
||||
<button id="signingCertClearButton"
|
||||
label="&certificate_clear.button;"
|
||||
oncommand="smimeClearCert('identity.signing_cert_name')"/>
|
||||
</hbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<checkbox id="identity.sign_mail" wsm_persist="true" label="&signMessage.label;"/>
|
||||
</groupbox>
|
||||
|
||||
<groupbox id="encryption.titlebox">
|
||||
<caption label="&encryptionGroupTitle.label;"/>
|
||||
|
||||
<label value="&encryptionCert.message;"/>
|
||||
|
||||
<hbox align="center">
|
||||
<textbox id="identity.encryption_cert_name" wsm_persist="true" flex="1"
|
||||
readonly="true" disabled="true"/>
|
||||
|
||||
<button id="encryptionCertSelectButton"
|
||||
label="&certificate.button;"
|
||||
oncommand="smimeSelectCert('identity.encryption_cert_name')"/>
|
||||
|
||||
<button id="encryptionCertClearButton"
|
||||
label="&certificate_clear.button;"
|
||||
oncommand="smimeClearCert('identity.encryption_cert_name')"/>
|
||||
</hbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<label value="&encryptionChoiceLabel.label;"/>
|
||||
|
||||
<radiogroup id="encryptionChoices">
|
||||
<radio id="encrypt_mail_never" wsm_persist="true" value="0"
|
||||
label="&neverEncrypt.label;"/>
|
||||
|
||||
<radio id="encrypt_mail_always" wsm_persist="true" value="2"
|
||||
label="&alwaysEncryptMessage.label;"/>
|
||||
</radiogroup>
|
||||
</groupbox>
|
||||
|
||||
<!-- Certificate manager -->
|
||||
<groupbox>
|
||||
<caption label="&managecerts.label;"/>
|
||||
<description>&managecerts.text;</description>
|
||||
<hbox align="right">
|
||||
<button label="&managecerts.button;"
|
||||
oncommand="openCertManager();"
|
||||
id="openCertManagerButton"
|
||||
accesskey="&managecerts.accesskey;"/>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
||||
</page>
|
|
@ -2,8 +2,11 @@ messenger.jar:
|
|||
*+ content/messenger-smime/msgReadSMIMEOverlay.xul (content/msgReadSMIMEOverlay.xul)
|
||||
*+ content/messenger-smime/msgCompSMIMEOverlay.xul (content/msgCompSMIMEOverlay.xul)
|
||||
*+ content/messenger-smime/msgHdrViewSMIMEOverlay.js (content/msgHdrViewSMIMEOverlay.js)
|
||||
*+ content/messenger/am-smime.xul (content/am-smime.xul)
|
||||
*+ content/messenger/am-smime.js (content/am-smime.js)
|
||||
|
||||
classic.jar:
|
||||
+ skin/classic/messenger/smime/msgCompSMIMEOverlay.css (skin/msgCompSMIMEOverlay.css)
|
||||
|
||||
+ skin/classic/messenger/smime/msgCompSMIMEOverlay.css (skin/msgCompSMIMEOverlay.css)
|
||||
|
||||
en-US.jar:
|
||||
+ locale/en-US/messenger/am-smime.dtd (locale/am-smime.dtd)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<!ENTITY securityTitle.label "Security">
|
||||
<!ENTITY securityHeading.label "To send and receive signed or encrypted messages, you should specify both a digital signing certificate and an encryption certificate.">
|
||||
<!ENTITY encryptionGroupTitle.label "Encryption">
|
||||
<!ENTITY encryptionChoiceLabel.label "Default encryption setting when sending messages:">
|
||||
<!ENTITY neverEncrypt.label "Never (do not use encryption)">
|
||||
<!ENTITY alwaysEncryptMessage.label "Required (can't send message unless all recipients have certificates)">
|
||||
<!ENTITY encryptionCert.message "Use this certificate to encrypt & decrypt messages sent to you:">
|
||||
<!ENTITY encryptionCert.notselected "No certificate set">
|
||||
<!ENTITY certificate.button "Select...">
|
||||
<!ENTITY certificate_clear.button "Clear">
|
||||
<!ENTITY signingGroupTitle.label "Digital Signing">
|
||||
<!ENTITY signMessage.label "Digitally sign messages (by default)">
|
||||
<!ENTITY signingCert.message "Use this certificate to digitally sign messages you send:">
|
||||
<!ENTITY signingCert.notselected "No certificate set">
|
||||
|
||||
<!ENTITY managecerts.label "Manage Certificates">
|
||||
<!ENTITY managecerts.text "Use the Certificate Manager to manage your personal certificates, as well as those of other people and certificate authorities.">
|
||||
<!ENTITY managecerts.button "Manage Certificates...">
|
||||
<!ENTITY managecerts.accesskey "M">
|
Загрузка…
Ссылка в новой задаче