--- a/chat/chat-prefs.js
+++ b/chat/chat-prefs.js
@@ -1,16 +1,20 @@
// What to do when starting up
// 0 = do not connect / show the account manager
// 1 = connect automatically
// Other values will be added later, for example to start minimized
pref("messenger.startup.action", 1);
pref("messenger.accounts", "");
+// Should the accounts service stored in the password manager the
+// passwords that are currently stored in the preferences?
+pref("messenger.accounts.convertOldPasswords", false);
+
// The intervals in seconds between automatic reconnection attempts
// The last value will be reused forever.
// A value of 0 means that there will be no more reconnection attempts.
pref("messenger.accounts.reconnectTimer", "1,5,30,60,90,300,600,1200,3600");
// List of tags ids whose contacts should be shown in the special
// "Other contacts" group.
pref("messenger.buddies.hiddenTags", "");
--- a/chat/components/public/imIAccount.idl
+++ b/chat/components/public/imIAccount.idl
@@ -236,17 +236,17 @@ interface imIAccount: prplIAccount {
/* Set when the account is trying to connect for the first time
with the current parameters (removed after a successsful connection) */
const short FIRST_CONNECTION_PENDING = 2;
/* Set at startup when the previous state was pending */
const short FIRST_CONNECTION_CRASHED = 4;
attribute short firstConnectionState;
- // FIXME password should be in password manager
+ // Passwords are stored in the toolkit Password Manager.
attribute AUTF8String password;
attribute AUTF8String alias;
/* While an account is connecting, this attribute contains a message
indicating the current step of the connection */
readonly attribute AUTF8String connectionStateMsg;
--- a/chat/components/src/imAccounts.js
+++ b/chat/components/src/imAccounts.js
@@ -42,21 +42,34 @@ Cu.import("resource:///modules/imService
const kPrefAutologinPending = "messenger.accounts.autoLoginPending";
const kPrefMessengerAccounts = "messenger.accounts";
const kPrefAccountPrefix = "messenger.account.";
const kAccountKeyPrefix = "account";
const kAccountOptionPrefPrefix = "options.";
const kPrefAccountName = "name";
const kPrefAccountPrpl = "prpl";
-const kPrefAccountPassword = "password";
const kPrefAccountAutoLogin = "autoLogin";
const kPrefAccountAlias = "alias";
const kPrefAccountFirstConnectionState = "firstConnectionState";
+const kPrefConvertOldPasswords = "messenger.accounts.convertOldPasswords";
+const kPrefAccountPassword = "password";
+
+XPCOMUtils.defineLazyGetter(this, "LoginManager", function()
+ Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager)
+);
+
+XPCOMUtils.defineLazyGetter(this, "_", function()
+ l10nHelper("chrome://chat/locale/accounts.properties")
+);
+
+var gUserCanceledMasterPasswordPrompt = false;
+var gConvertingOldPasswords = false;
+
var SavePrefTimer = {
saveNow: function() {
if (this._timer) {
clearTimeout(this._timer);
this._timer = null;
}
Services.prefs.savePrefFile(null);
},
@@ -162,16 +175,27 @@ function imAccount(aKey, aName, aPrplId)
// Send status change notifications to the account.
this.observedStatusInfo = null; // (To execute the setter).
// If we have never finished the first connection attempt for this account,
// mark the account as having caused a crash.
if (this.firstConnectionState == Ci.imIAccount.FIRST_CONNECTION_PENDING)
this.firstConnectionState = Ci.imIAccount.FIRST_CONNECTION_CRASHED;
+ // Try to convert old passwords stored in the preferences.
+ // Don't try too hard if the user has canceled a master password prompt:
+ // we don't want to display several of theses prompts at startup.
+ if (gConvertingOldPasswords && !this.protocol.noPassword) {
+ try {
+ let password = this.prefBranch.getCharPref(kPrefAccountPassword);
+ if (password && !this.password)
+ this.password = password;
+ } catch (e) { /* No password saved in the prefs for this account. */ }
+ }
+
// Check for errors that should prevent connection attempts.
if (this._passwordRequired && !this.password)
this._connectionErrorReason = Ci.imIAccount.ERROR_MISSING_PASSWORD;
else if (this.firstConnectionState == Ci.imIAccount.FIRST_CONNECTION_CRASHED)
this._connectionErrorReason = Ci.imIAccount.ERROR_CRASHED;
}
imAccount.prototype = {
@@ -182,17 +206,19 @@ imAccount.prototype = {
numericId: 0,
protocol: null,
prplAccount: null,
connectionState: Ci.imIAccount.STATE_DISCONNECTED,
connectionStateMsg: "",
connectionErrorMessage: "",
_connectionErrorReason: Ci.prplIAccount.NO_ERROR,
get connectionErrorReason() {
- if (this._connectionErrorReason != Ci.prplIAccount.NO_ERROR)
+ if (this._connectionErrorReason != Ci.prplIAccount.NO_ERROR &&
+ (this._connectionErrorReason != Ci.imIAccount.ERROR_MISSING_PASSWORD ||
+ !this._password))
return this._connectionErrorReason;
else
return this.prplAccount.connectionErrorReason;
},
observe: function(aSubject, aTopic, aData) {
if (aTopic == "account-connect-progress")
this.connectionStateMsg = aData;
@@ -372,35 +398,102 @@ imAccount.prototype = {
get alias() {
try {
return this.prefBranch.getCharPref(kPrefAccountAlias);
} catch (e) {
return "";
}
},
+ _password: "",
get password() {
+ if (this._password)
+ return this._password;
+
+ // Avoid prompting the user for the master password more than once at startup.
+ if (gUserCanceledMasterPasswordPrompt)
+ return "";
+
+ let passwordURI = "im://" + this.protocol.id;
+ let logins;
try {
- return this.prefBranch.getCharPref(kPrefAccountPassword);
+ logins = LoginManager.findLogins({}, passwordURI, null, passwordURI);
} catch (e) {
+ this._handleMasterPasswordException(e);
return "";
}
+ let normalizedName = this.normalizedName;
+ for each (let login in logins) {
+ if (login.username == normalizedName) {
+ this._password = login.password;
+ if (this._connectionErrorReason == Ci.imIAccount.ERROR_MISSING_PASSWORD) {
+ // We have found a password for an account marked as missing password,
+ // re-check all others accounts missing a password. But first,
+ // remove the error on our own account to avoid re-checking it.
+ delete this._connectionErrorReason;
+ gAccountsService._checkIfPasswordStillMissing();
+ }
+ return this._password;
+ }
+ }
+ return "";
+ },
+ _checkIfPasswordStillMissing: function() {
+ if (this._connectionErrorReason != Ci.imIAccount.ERROR_MISSING_PASSWORD ||
+ !this.password)
+ return;
+
+ delete this._connectionErrorReason;
+ this._sendUpdateNotification();
},
get _passwordRequired()
!this.protocol.noPassword && !this.protocol.passwordOptional,
set password(aPassword) {
- this.prefBranch.setCharPref(kPrefAccountPassword, aPassword);
+ this._password = aPassword;
+ if (gUserCanceledMasterPasswordPrompt)
+ return;
+ let newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"]
+ .createInstance(Ci.nsILoginInfo);
+ let passwordURI = "im://" + this.protocol.id;
+ newLogin.init(passwordURI, null, passwordURI, this.normalizedName,
+ aPassword, "", "");
+ try {
+ let logins = LoginManager.findLogins({}, passwordURI, null, passwordURI);
+ let saved = false;
+ for each (let login in logins) {
+ if (newLogin.matches(login, true)) {
+ if (aPassword)
+ LoginManager.modifyLogin(login, newLogin);
+ else
+ LoginManager.removeLogin(login);
+ saved = true;
+ break;
+ }
+ }
+ if (!saved && aPassword)
+ LoginManager.addLogin(newLogin);
+ } catch (e) {
+ this._handleMasterPasswordException(e);
+ }
+
this._connectionInfoChanged();
if (aPassword &&
this._connectionErrorReason == Ci.imIAccount.ERROR_MISSING_PASSWORD)
this._connectionErrorReason = Ci.imIAccount.NO_ERROR;
else if (!aPassword && this._passwordRequired)
this._connectionErrorReason = Ci.imIAccount.ERROR_MISSING_PASSWORD;
this._sendUpdateNotification();
},
+ _handleMasterPasswordException: function(aException) {
+ if (aException.result != Components.results.NS_ERROR_ABORT)
+ throw aException;
+
+ gUserCanceledMasterPasswordPrompt = true;
+ executeSoon(function () { gUserCanceledMasterPasswordPrompt = false; });
+ },
get autoLogin() {
let autoLogin = true;
try {
autoLogin = this.prefBranch.getBoolPref(kPrefAccountAutoLogin);
} catch (e) { }
return autoLogin;
},
@@ -430,16 +523,27 @@ imAccount.prototype = {
_finishedAutoLogin: function() {
if (!this.hasOwnProperty("_autoLoginPending"))
return;
delete this._autoLoginPending;
AutoLoginCounter.finishedAutoLogin();
},
remove: function() {
+ let login = Cc["@mozilla.org/login-manager/loginInfo;1"]
+ .createInstance(Ci.nsILoginInfo);
+ let passwordURI = "im://" + this.protocol.id;
+ login.init(passwordURI, null, passwordURI, this.normalizedName, "", "", "");
+ let logins = LoginManager.findLogins({}, passwordURI, null, passwordURI);
+ for each (let l in logins) {
+ if (login.matches(l, true)) {
+ LoginManager.removeLogin(l);
+ break;
+ }
+ }
this.unInit();
Services.contacts.forgetAccount(this.numericId);
this.prefBranch.deleteBranch("");
},
unInit: function() {
// remove any pending reconnection timer.
this.cancelReconnection();
@@ -460,17 +564,43 @@ imAccount.prototype = {
delete this.prplAccount;
},
get _ensurePrplAccount() {
if (this.prplAccount)
return this.prplAccount;
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
- connect: function() { this._ensurePrplAccount.connect(); },
+ connect: function() {
+ if (this._passwordRequired) {
+ // If the previous connection attempt failed because we have a wrong password,
+ // clear the passwor cache so that if there's no password in the password
+ // manager the user gets prompted again.
+ if (this.connectionErrorReason == Ci.prplIAccount.ERROR_AUTHENTICATION_FAILED)
+ delete this._password;
+
+ let password = this.password;
+ if (!password) {
+ let prompts = Services.prompt;
+ let shouldSave = {value: false};
+ password = {value: ""};
+ if (!prompts.promptPassword(null, _("passwordPromptTitle", this.name),
+ _("passwordPromptText", this.name),
+ password, _("passwordPromptSaveCheckbox"),
+ shouldSave))
+ return;
+
+ if (shouldSave.value)
+ this.password = password.value;
+ else
+ this._password = password.value;
+ }
+ }
+ this._ensurePrplAccount.connect();
+ },
disconnect: function() {
if (this._statusObserver) {
this.statusInfo.removeObserver(this._statusObserver);
delete this._statusObserver;
}
this._ensurePrplAccount.disconnect();
},
@@ -540,36 +670,46 @@ imAccount.prototype = {
get proxyInfo() this._ensurePrplAccount.proxyInfo,
set proxyInfo(val) {
this._ensurePrplAccount.proxyInfo = val;
this._connectionInfoChanged();
}
};
+var gAccountsService = null;
+
function AccountsService() { }
AccountsService.prototype = {
initAccounts: function() {
this._initAutoLoginStatus();
this._accounts = [];
this._accountsById = {};
+ gAccountsService = this;
+ gConvertingOldPasswords =
+ Services.prefs.getBoolPref(kPrefConvertOldPasswords);
let accountList = this._accountList;
for each (let account in (accountList ? accountList.split(",") : [])) {
try {
account.trim();
if (!account)
throw Cr.NS_ERROR_INVALID_ARG;
let newAccount = new imAccount(account);
this._accounts.push(newAccount);
this._accountsById[newAccount.numericId] = newAccount;
} catch (e) {
Cu.reportError(e);
dump(e + " " + e.toSource() + "\n");
}
}
+ // If the user has canceled a master password prompt, we haven't
+ // been able to save any password, so the old password conversion
+ // still needs to happen.
+ if (gConvertingOldPasswords && !gUserCanceledMasterPasswordPrompt)
+ Services.prefs.setBoolPref(kPrefConvertOldPasswords, false);
this._prefObserver = this.observe.bind(this);
Services.prefs.addObserver(kPrefMessengerAccounts, this._prefObserver, false);
},
_observingAccountListChange: true,
_prefObserver: null,
observe: function(aSubject, aTopic, aData) {
@@ -592,16 +732,17 @@ AccountsService.prototype = {
this._observingAccountListChange = false;
Services.prefs.setCharPref(kPrefMessengerAccounts, aNewList);
delete this._observingAccountListChange;
},
unInitAccounts: function() {
for each (let account in this._accounts)
account.unInit();
+ gAccountsService = null;
delete this._accounts;
delete this._accountsById;
Services.prefs.removeObserver(kPrefMessengerAccounts, this._prefObserver);
delete this._prefObserver;
},
autoLoginStatus: Ci.imIAccountsService.AUTOLOGIN_ENABLED,
_initAutoLoginStatus: function() {
@@ -710,16 +851,28 @@ AccountsService.prototype = {
// should be processed now.
this.autoLoginStatus = Ci.imIAccountsService.AUTOLOGIN_ENABLED;
// Notify observers so that any message stating that autologin is
// disabled can be removed
Services.obs.notifyObservers(this, "autologin-processed", null);
},
+ _checkingIfPasswordStillMissing: false,
+ _checkIfPasswordStillMissing: function() {
+ // Avoid recursion.
+ if (this._checkingIfPasswordStillMissing)
+ return;
+
+ this._checkingIfPasswordStillMissing = true;
+ for each (let account in this._accounts)
+ account._checkIfPasswordStillMissing();
+ delete this._checkingIfPasswordStillMissing;
+ },
+
getAccountById: function(aAccountId) {
if (aAccountId.indexOf(kAccountKeyPrefix) != 0)
throw Cr.NS_ERROR_INVALID_ARG;
let id = parseInt(aAccountId.substr(kAccountKeyPrefix.length));
return this.getAccountByNumericId(id);
},
new file mode 100644
--- /dev/null
+++ b/chat/locales/en-US/accounts.properties
@@ -0,0 +1,3 @@
+passwordPromptTitle=Password for %S
+passwordPromptText=Please enter your password for your account %S in order to connect it.
+passwordPromptSaveCheckbox=Use Password Manager to remember this password.
--- a/chat/locales/jar.mn
+++ b/chat/locales/jar.mn
@@ -1,10 +1,11 @@
#filter substitution
@AB_CD@.jar:
% locale chat @AB_CD@ %locale/@AB_CD@/chat/
+ locale/@AB_CD@/chat/accounts.properties (%accounts.properties)
locale/@AB_CD@/chat/commands.properties (%commands.properties)
locale/@AB_CD@/chat/conversations.properties (%conversations.properties)
locale/@AB_CD@/chat/facebook.properties (%facebook.properties)
locale/@AB_CD@/chat/status.properties (%status.properties)
locale/@AB_CD@/chat/twitter.properties (%twitter.properties)
locale/@AB_CD@/chat/xmpp.properties (%xmpp.properties)
--- a/chat/modules/jsProtoHelper.jsm
+++ b/chat/modules/jsProtoHelper.jsm
@@ -723,17 +723,17 @@ const GenericProtocolPrototype = {
get usernameEmptyText() "",
accountExists: function() false, //FIXME
get uniqueChatName() false,
get chatHasTopic() false,
get noPassword() false,
get newMailNotification() false,
get imagesInIM() false,
- get passwordOptional() true,
+ get passwordOptional() false,
get usePointSize() true,
get registerNoScreenName() false,
get slashCommandsNative() false,
get usePurpleProxy() false,
get classDescription() this.name + " Protocol",
get contractID() "@instantbird.org/purple/" + this.normalizedName + ";1"
};
--- a/im/app/profile/all-instantbird.js
+++ b/im/app/profile/all-instantbird.js
@@ -21,16 +21,17 @@ pref("general.autoScroll", true);
// this will automatically enable inline spellchecking (if it is available) for
// editable elements in HTML
// 0 = spellcheck nothing
// 1 = check multi-line controls [default]
// 2 = check multi/single line controls
pref("layout.spellcheckDefault", 1);
+pref("messenger.accounts.convertOldPasswords", true);
pref("messenger.accounts.promptOnDelete", true);
pref("messenger.buddies.showOffline", false);
pref("messenger.buddies.hideTagPrompt", true);
pref("messenger.conversations.openInTabs", true);
pref("messenger.conversations.useSeparateWindowsForMUCs", false);
pref("messenger.conversations.doubleClickToReply", true);
--- a/im/content/account.xml
+++ b/im/content/account.xml
@@ -136,22 +136,24 @@
const key = "account.connection.error";
var account = this._account;
var text;
let errorReason = account.connectionErrorReason;
if (errorReason == Ci.imIAccount.ERROR_UNKNOWN_PRPL)
text = bundle.getFormattedString(key + "UnknownPrpl",
[account.protocol.id]);
else if (errorReason == Ci.imIAccount.ERROR_MISSING_PASSWORD)
- text = bundle.getString(key + "MissingPassword");
+ text = bundle.getString(key + "EnteringPasswordRequired");
else if (errorReason == Ci.imIAccount.ERROR_CRASHED)
text = bundle.getString(key + "CrashedAccount");
else
text = account.connectionErrorMessage;
- text = bundle.getFormattedString(key, [text]);
+
+ if (errorReason != Ci.imIAccount.ERROR_MISSING_PASSWORD)
+ text = bundle.getFormattedString(key, [text]);
this.setAttribute("error", "true");
var error = document.getAnonymousElementByAttribute(this, "anonid",
"error");
error.textContent = text;
var updateReconnect = (function() {
var date = Math.round((account.timeOfNextReconnect - Date.now()) / 1000);
--- a/im/content/account.xul
+++ b/im/content/account.xul
@@ -69,17 +69,16 @@
<tab id="advancedTab" label="&account.advanced;"/>
</tabs>
<tabpanels id="panels" flex="1">
<tabpanel orient="vertical" flex="1" style="overflow:auto;">
<hbox id="passwordBox" equalsize="always" align="baseline">
<label value="&account.password;" control="password" flex="1"/>
<textbox id="password" flex="1" type="password"/>
</hbox>
- <checkbox id="rememberPassword" label="&account.rememberPassword;" hidden="true"/>
<separator class="groove"/>
<hbox id="aliasBox" equalsize="always" align="baseline">
<label value="&account.alias;" control="alias" flex="1"/>
<textbox id="alias" flex="1"/>
</hbox>
--- a/im/content/accountWizard.js
+++ b/im/content/accountWizard.js
@@ -376,21 +376,23 @@ var accountWizard = {
var label = document.getElementById("protoLabel").value;
rows.appendChild(this.createSummaryRow(label, this.proto.name));
this.username = this.getUsername();
label = bundle.getString("accountUsername");
rows.appendChild(this.createSummaryRow(label, this.username));
if (!this.proto.noPassword) {
this.password = this.getValue("password");
- label = document.getElementById("passwordLabel").value;
- var pass = "";
- for (let i = 0; i < this.password.length; ++i)
- pass += "*";
- rows.appendChild(this.createSummaryRow(label, pass));
+ if (this.password) {
+ label = document.getElementById("passwordLabel").value;
+ var pass = "";
+ for (let i = 0; i < this.password.length; ++i)
+ pass += "*";
+ rows.appendChild(this.createSummaryRow(label, pass));
+ }
}
this.alias = this.getValue("alias");
if (this.alias) {
label = document.getElementById("aliasLabel").value;
rows.appendChild(this.createSummaryRow(label, this.alias));
}
/* FIXME
@@ -434,17 +436,17 @@ var accountWizard = {
let opt = this.prefs[i];
let label = bundle.getFormattedString("accountColon", [opt.opt.label]);
rows.appendChild(this.createSummaryRow(label, opt.value));
}
},
createAccount: function aw_createAccount() {
var acc = Services.accounts.createAccount(this.username, this.proto.id);
- if (!this.proto.noPassword)
+ if (!this.proto.noPassword && this.password)
acc.password = this.password;
if (this.alias)
acc.alias = this.alias;
//FIXME: newMailNotification
for (let i = 0; i < this.prefs.length; ++i) {
let option = this.prefs[i];
let opt = option.opt;
--- a/im/content/accountWizard.xul
+++ b/im/content/accountWizard.xul
@@ -93,16 +93,18 @@
<wizardpage id="accountpassword" pageid="accountpassword" next="accountadvanced"
label="&accountPasswordTitle.label;">
<description>&accountPasswordInfo.label;</description>
<separator/>
<hbox id="passwordBox" align="baseline">
<label value="&accountPasswordField.label;" control="password" id="passwordLabel"/>
<textbox id="password" type="password"/>
</hbox>
+ <separator/>
+ <description id="passwordManagerDescription">&accountPasswordManager.label;</description>
</wizardpage>
<wizardpage id="accountadvanced" pageid="accountadvanced" next="accountsummary"
label="&accountAdvancedTitle.label;"
onpageshow="accountWizard.showAdvanced();">
<description>&accountAdvancedInfo.label;</description>
<separator class="thin"/>
<groupbox id="aliasGroupbox" class="collapsable"
--- a/im/content/accounts.js
+++ b/im/content/accounts.js
@@ -330,18 +330,17 @@ var gAccountManager = {
let account = selectedItem.account;
let activeCommandName =
(this.isOffline || account.disconnected) ? "connect" : "disconnect";
let activeCommandElt = document.getElementById("cmd_" + activeCommandName);
let isCommandDisabled =
(this.isOffline ||
(account.disconnected &&
- (account.connectionErrorReason == Ci.imIAccount.ERROR_UNKNOWN_PRPL ||
- account.connectionErrorReason == Ci.imIAccount.ERROR_MISSING_PASSWORD)));
+ account.connectionErrorReason == Ci.imIAccount.ERROR_UNKNOWN_PRPL));
[[activeCommandElt, isCommandDisabled],
[document.getElementById("cmd_moveup"), accountList.selectedIndex == 0],
[document.getElementById("cmd_movedown"),
accountList.selectedIndex == accountList.itemCount - 1]
].forEach(function (aEltArray) {
let [elt, state] = aEltArray;
if (state)
--- a/im/content/debug/fake/fake.js
+++ b/im/content/debug/fake/fake.js
@@ -333,17 +333,16 @@ function Account(aName, aProto)
dump("account " + aName + " created\n");
}
Account.prototype = {
__proto__: ClassInfo("imIAccount", "generic account object"),
protocol: null,
password: "",
autoLogin: true,
- rememberPassword: true,
alias: "",
proxyInfo: null,
connectionStageMsg: "",
connectionErrorReason: -1,
timeOfNextReconnect: 0,
timeOfLastConnect: new Date(),
connectionErrorMessage: "",
disconnecting: false,
--- a/im/content/preferences/privacy.js
+++ b/im/content/preferences/privacy.js
@@ -34,16 +34,17 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var gPrivacyPane = {
init: function ()
{
this.updateDisabledState();
+ this._initMasterPasswordUI();
},
updateDisabledState: function ()
{
let broadcaster = document.getElementById("idleReportingEnabled");
if (document.getElementById("messenger.status.reportIdle").value) {
broadcaster.removeAttribute("disabled");
this.updateMessageDisabledState();
@@ -85,10 +86,117 @@ var gPrivacyPane = {
// If launch also fails (probably because it's not implemented), let the
// OS handler try to open the parent
let uri = Services.io.newFileURI(parent);
let protocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
getService(Ci.nsIExternalProtocolService);
protocolSvc.loadUrl(uri);
}
}
+ },
+
+ /**
+ * Initializes master password UI: the "use master password" checkbox, selects
+ * the master password button to show, and enables/disables it as necessary.
+ * The master password is controlled by various bits of NSS functionality,
+ * so the UI for it can't be controlled by the normal preference bindings.
+ */
+ _initMasterPasswordUI: function ()
+ {
+ var noMP = !this._masterPasswordSet();
+
+ document.getElementById("changeMasterPassword").disabled = noMP;
+
+ document.getElementById("useMasterPassword").checked = !noMP;
+ },
+
+
+ /**
+ * Returns true if the user has a master password set and false otherwise.
+ */
+ _masterPasswordSet: function ()
+ {
+ const Cc = Components.classes, Ci = Components.interfaces;
+ var secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].
+ getService(Ci.nsIPKCS11ModuleDB);
+ var slot = secmodDB.findSlotByName("");
+ if (slot) {
+ var status = slot.status;
+ var hasMP = status != Ci.nsIPKCS11Slot.SLOT_UNINITIALIZED &&
+ status != Ci.nsIPKCS11Slot.SLOT_READY;
+ return hasMP;
+ } else {
+ // XXX I have no bloody idea what this means
+ return false;
+ }
+ },
+
+
+ /**
+ * Enables/disables the master password button depending on the state of the
+ * "use master password" checkbox, and prompts for master password removal
+ * if one is set.
+ */
+ updateMasterPasswordButton: function ()
+ {
+ var checkbox = document.getElementById("useMasterPassword");
+ var button = document.getElementById("changeMasterPassword");
+ button.disabled = !checkbox.checked;
+
+ // unchecking the checkbox should try to immediately remove the master
+ // password, because it's impossible to non-destructively remove the master
+ // password used to encrypt all the passwords without providing it (by
+ // design), and it would be extremely odd to pop up that dialog when the
+ // user closes the prefwindow and saves his settings
+ if (!checkbox.checked)
+ this._removeMasterPassword();
+ else
+ this.changeMasterPassword();
+
+ this._initMasterPasswordUI();
+ },
+
+ /**
+ * Displays the "remove master password" dialog to allow the user to remove
+ * the current master password. When the dialog is dismissed, master password
+ * UI is automatically updated.
+ */
+ _removeMasterPassword: function ()
+ {
+ const Cc = Components.classes, Ci = Components.interfaces;
+ var secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].
+ getService(Ci.nsIPKCS11ModuleDB);
+ if (secmodDB.isFIPSEnabled) {
+ var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
+ getService(Ci.nsIPromptService);
+ var bundle = document.getElementById("bundlePreferences");
+ promptService.alert(window,
+ bundle.getString("pw_change_failed_title"),
+ bundle.getString("pw_change2empty_in_fips_mode"));
+ }
+ else {
+ document.documentElement.openSubDialog("chrome://mozapps/content/preferences/removemp.xul",
+ "", null);
+ }
+ this._initMasterPasswordUI();
+ },
+
+ /**
+ * Displays a dialog in which the master password may be changed.
+ */
+ changeMasterPassword: function ()
+ {
+ document.documentElement.openSubDialog("chrome://mozapps/content/preferences/changemp.xul",
+ "", null);
+ this._initMasterPasswordUI();
+ },
+
+ /**
+ * Shows the sites where the user has saved passwords and the associated
+ * login information.
+ */
+ showPasswords: function ()
+ {
+ document.documentElement.openWindow("Toolkit:PasswordManager",
+ "chrome://passwordmgr/content/passwordManager.xul",
+ "", null);
}
};
--- a/im/content/preferences/privacy.xul
+++ b/im/content/preferences/privacy.xul
@@ -58,16 +58,17 @@
onchange="gPrivacyPane.updateDisabledState();"/>
<preference id="messenger.status.timeBeforeIdle" name="messenger.status.timeBeforeIdle" type="int"/>
<preference id="messenger.status.awayWhenIdle" name="messenger.status.awayWhenIdle" type="bool"
onchange="gPrivacyPane.updateMessageDisabledState();"/>
<preference id="messenger.status.defaultIdleAwayMessage" name="messenger.status.defaultIdleAwayMessage" type="wstring"/>
<preference id="purple.logging.log_chats" name="purple.logging.log_chats" type="bool"/>
<preference id="purple.logging.log_ims" name="purple.logging.log_ims" type="bool"/>
<preference id="purple.logging.log_system" name="purple.logging.log_system" type="bool"/>
+ <preference id="pref.privacy.disable_button.view_passwords" name="pref.privacy.disable_button.view_passwords" type="bool"/>
</preferences>
<broadcaster id="idleReportingEnabled"/>
<!-- Status -->
<groupbox id="statusGroup">
<caption label="&status.label;"/>
<hbox align="center">
@@ -112,11 +113,37 @@
<description control="openLogFolder"
flex="1">&logShowFolder.description;</description>
<button id="openLogFolder" label="&logShowFolderButton.label;"
accesskey="&logShowFolderButton.accesskey;"
oncommand="gPrivacyPane.openLogFolder();"/>
</hbox>
</groupbox>
+ <!-- Passwords -->
+ <groupbox id="passwordsGroup" orient="vertical">
+ <caption label="&passwords.label;"/>
+
+ <description>&savedPasswords.intro;</description>
+ <hbox>
+ <spacer flex="1"/>
+ <button label="&savedPasswords.label;" accesskey="&savedPasswords.accesskey;"
+ oncommand="gPrivacyPane.showPasswords();"
+ preference="pref.privacy.disable_button.view_passwords"/>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <description>&masterPassword.intro;</description>
+ <hbox>
+ <checkbox id="useMasterPassword" flex="1"
+ label="&useMasterPassword.label;" accesskey="&useMasterPassword.accesskey;"
+ oncommand="gPrivacyPane.updateMasterPasswordButton();"/>
+
+ <button id="changeMasterPassword"
+ label="&changeMasterPassword.label;" accesskey="&changeMasterPassword.accesskey;"
+ oncommand="gPrivacyPane.changeMasterPassword();"/>
+ </hbox>
+ </groupbox>
+
</prefpane>
</overlay>
--- a/im/locales/en-US/chrome/instantbird/account.dtd
+++ b/im/locales/en-US/chrome/instantbird/account.dtd
@@ -1,13 +1,12 @@
<!ENTITY accountWindow.title "Account properties">
<!ENTITY accountWindow.width "300">
<!ENTITY account.general "General">
<!ENTITY account.advanced "Advanced Options">
<!ENTITY account.name "Username:">
<!ENTITY account.password "Password:">
-<!ENTITY account.rememberPassword "remember password">
<!ENTITY account.alias "Alias:">
<!ENTITY account.newMailNotification "Notify on new Mail">
<!ENTITY account.autojoin "Auto-Joined Channels:">
<!ENTITY account.proxySettings.caption "Proxy Settings:">
<!ENTITY account.proxySettings.change.label "Change…">
<!ENTITY account.proxySettings.change.accessKey "C">
--- a/im/locales/en-US/chrome/instantbird/accountWizard.dtd
+++ b/im/locales/en-US/chrome/instantbird/accountWizard.dtd
@@ -9,16 +9,17 @@
<!ENTITY accountProtocolGetMore.label "Get more…">
<!ENTITY accountUsernameTitle.label "Username">
<!ENTITY accountUsernameDuplicate.label "This account is already configured!">
<!ENTITY accountPasswordTitle.label "Password">
<!ENTITY accountPasswordInfo.label "Please enter your password in the box below.">
<!ENTITY accountPasswordField.label "Password:">
+<!ENTITY accountPasswordManager.label "The password entered here will be stored in the Password Manager. Leave this box empty if you want to be prompted for your password each time this account is connected.">
<!ENTITY accountAdvancedTitle.label "Advanced Options">
<!ENTITY accountAdvancedInfo.label "Feel free to skip this step if you want to.">
<!ENTITY accountAdvanced.newMailNotification.label "Notify on new Mail">
<!ENTITY accountAliasGroupbox.caption "Local Alias">
<!ENTITY accountAliasField.label "Alias:">
<!ENTITY accountAliasInfo.label "This will only be displayed in your conversations when you talk, remote buddies won't see it.">
<!ENTITY accountProxySettings.caption "Proxy Settings">
--- a/im/locales/en-US/chrome/instantbird/accounts.properties
+++ b/im/locales/en-US/chrome/instantbird/accounts.properties
@@ -2,17 +2,17 @@
protoOptions=%S Options
accountUsername=Username:
accountColon=%S:
accountUsernameInfo=Please enter the username for your %S account.
accountUsernameInfoWithDescription=Please enter the username (%S) for your %S account.
account.connection.error=Error: %S
account.connection.errorUnknownPrpl= No '%S' protocol plugin.
-account.connection.errorMissingPassword=A password is required to connect this account.
+account.connection.errorEnteringPasswordRequired=Entering a password is required to connect this account.
account.connection.errorCrashedAccount=A crash occurred while connecting this account.
account.connection.progress=Connecting: %S…
account.connecting=Connecting…
account.connectedForSeconds=Connected for a few seconds.
account.connectedForDouble=Connected for %S %S and %S %S.
account.connectedForSingle=Connected for about %S %S.
account.reconnectInDouble=Reconnection in %S %S and %S %S.
account.reconnectInSingle=Reconnection in %S %S.
--- a/im/locales/en-US/chrome/instantbird/preferences/privacy.dtd
+++ b/im/locales/en-US/chrome/instantbird/preferences/privacy.dtd
@@ -14,8 +14,19 @@
<!ENTITY logConversations.label "Keep records of my conversations">
<!ENTITY logConversations.accesskey "r">
<!ENTITY logSystem.label "Record connections, disconnections and status changes">
<!ENTITY logSystem.accesskey "c">
<!ENTITY logShowFolder.description "Open the folder containing the record files">
<!ENTITY logShowFolderButton.label "Show Log Folder…">
<!ENTITY logShowFolderButton.accesskey "L">
+
+<!-- Passwords, see mail/chrome/messenger/preferences/security.dtd -->
+<!ENTITY passwords.label "Passwords">
+<!ENTITY savedPasswords.intro "&brandShortName; can remember passwords for all of your accounts.">
+<!ENTITY useMasterPassword.label "Use a master password">
+<!ENTITY useMasterPassword.accesskey "U">
+<!ENTITY masterPassword.intro "A Master Password protects all your passwords, but you must enter it once per session.">
+<!ENTITY changeMasterPassword.label "Change Master Password…">
+<!ENTITY changeMasterPassword.accesskey "C">
+<!ENTITY savedPasswords.label "Saved Passwords…">
+<!ENTITY savedPasswords.accesskey "S">
deleted file mode 100644
--- a/im/locales/en-US/chrome/instantbird/preferences/security.dtd
+++ /dev/null
@@ -1,42 +0,0 @@
-<!ENTITY warnAddonInstall.label "Warn me when sites try to install add-ons">
-<!ENTITY warnAddonInstall.accesskey "W">
-
-<!-- LOCALIZATION NOTE (blockWebForgeries.label, blockAttackSites.label):
- The methods by which forged (phished) and attack sites will be detected by
- phishing providers will vary from human review to machine-based heuristics to a
- combination of both, so it's important that these strings and
- useDownloadedList.label convey the meaning "reported" (and not something like
- "known").
--->
-<!ENTITY blockAttackSites.label "Block reported attack sites">
-<!ENTITY blockAttackSites.accesskey "k">
-
-<!ENTITY blockWebForgeries.label "Block reported web forgeries">
-<!ENTITY blockWebForgeries.accesskey "B">
-
-<!ENTITY addonExceptions.label "Exceptions…">
-<!ENTITY addonExceptions.accesskey "E">
-
-
-<!ENTITY passwords.label "Passwords">
-
-<!ENTITY rememberPasswords.label "Remember passwords for sites">
-<!ENTITY rememberPasswords.accesskey "R">
-<!ENTITY passwordExceptions.label "Exceptions…">
-<!ENTITY passwordExceptions.accesskey "x">
-
-<!ENTITY useMasterPassword.label "Use a master password">
-<!ENTITY useMasterPassword.accesskey "U">
-<!ENTITY changeMasterPassword.label "Change Master Password…">
-<!ENTITY changeMasterPassword.accesskey "M">
-
-<!ENTITY savedPasswords.label "Saved Passwords…">
-<!ENTITY savedPasswords.accesskey "P">
-
-
-<!ENTITY warnings.label "Warning Messages">
-
-<!ENTITY chooseWarnings.label "Choose which warning messages you want to see while browsing the web">
-
-<!ENTITY warningSettings.label "Settings…">
-<!ENTITY warningSettings.accesskey "S">
--- a/im/locales/jar.mn
+++ b/im/locales/jar.mn
@@ -32,12 +32,11 @@
locale/@AB_CD@/instantbird/preferences/applications.dtd (%chrome/instantbird/preferences/applications.dtd)
locale/@AB_CD@/instantbird/preferences/colors.dtd (%chrome/instantbird/preferences/colors.dtd)
locale/@AB_CD@/instantbird/preferences/connection.dtd (%chrome/instantbird/preferences/connection.dtd)
locale/@AB_CD@/instantbird/preferences/content.dtd (%chrome/instantbird/preferences/content.dtd)
locale/@AB_CD@/instantbird/preferences/main.dtd (%chrome/instantbird/preferences/main.dtd)
locale/@AB_CD@/instantbird/preferences/preferences.dtd (%chrome/instantbird/preferences/preferences.dtd)
locale/@AB_CD@/instantbird/preferences/preferences.properties (%chrome/instantbird/preferences/preferences.properties)
locale/@AB_CD@/instantbird/preferences/privacy.dtd (%chrome/instantbird/preferences/privacy.dtd)
- locale/@AB_CD@/instantbird/preferences/security.dtd (%chrome/instantbird/preferences/security.dtd)
locale/@AB_CD@/instantbird/preferences/tabs.dtd (%chrome/instantbird/preferences/tabs.dtd)
locale/@AB_CD@/instantbird/preferences/themes.dtd (%chrome/instantbird/preferences/themes.dtd)
locale/@AB_CD@/instantbird/preferences/themes.properties (%chrome/instantbird/preferences/themes.properties)