2007-05-16 03:02:45 -07:00
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla 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/MPL/
|
|
|
|
*
|
|
|
|
* 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 Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Mozilla Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2007
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Justin Dolske <dolske@mozilla.com> (original author)
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
|
2007-06-30 18:41:33 -07:00
|
|
|
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
|
2007-05-16 03:02:45 -07:00
|
|
|
/*
|
|
|
|
* LoginManagerPromptFactory
|
|
|
|
*
|
|
|
|
* Implements nsIPromptFactory
|
|
|
|
*
|
|
|
|
* Invoked by NS_NewAuthPrompter2()
|
|
|
|
* [embedding/components/windowwatcher/src/nsPrompt.cpp]
|
|
|
|
*/
|
|
|
|
function LoginManagerPromptFactory() {}
|
|
|
|
|
|
|
|
LoginManagerPromptFactory.prototype = {
|
|
|
|
|
2007-06-30 18:41:33 -07:00
|
|
|
classDescription : "LoginManagerPromptFactory",
|
|
|
|
contractID : "@mozilla.org/passwordmanager/authpromptfactory;1",
|
2007-08-31 15:29:55 -07:00
|
|
|
classID : Components.ID("{749e62f4-60ae-4569-a8a2-de78b649660e}"),
|
2007-06-30 18:41:33 -07:00
|
|
|
QueryInterface : XPCOMUtils.generateQI([Ci.nsIPromptFactory]),
|
2007-05-16 03:02:45 -07:00
|
|
|
|
|
|
|
getPrompt : function (aWindow, aIID) {
|
2007-08-31 15:29:55 -07:00
|
|
|
var prompt = new LoginManagerPrompter().QueryInterface(aIID);
|
|
|
|
prompt.init(aWindow);
|
|
|
|
return prompt;
|
2007-05-16 03:02:45 -07:00
|
|
|
}
|
|
|
|
}; // end of LoginManagerPromptFactory implementation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ==================== LoginManagerPrompter ==================== */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LoginManagerPrompter
|
|
|
|
*
|
2007-08-31 15:29:55 -07:00
|
|
|
* Implements nsIAuthPrompt2 and nsILoginManagerPrompter.
|
|
|
|
* nsIAuthPrompt2 usage is invoked by a channel for protocol-based
|
|
|
|
* authentication (eg HTTP Authenticate, FTP login). nsILoginManagerPrompter
|
|
|
|
* is invoked by Login Manager for saving/changing a login.
|
2007-05-16 03:02:45 -07:00
|
|
|
*/
|
2007-08-31 15:29:55 -07:00
|
|
|
function LoginManagerPrompter() {}
|
2007-08-09 11:59:06 -07:00
|
|
|
|
2007-05-16 03:02:45 -07:00
|
|
|
LoginManagerPrompter.prototype = {
|
|
|
|
|
2007-08-31 15:29:55 -07:00
|
|
|
classDescription : "LoginManagerPrompter",
|
|
|
|
contractID : "@mozilla.org/login-manager/prompter;1",
|
|
|
|
classID : Components.ID("{8aa66d77-1bbb-45a6-991e-b8f47751c291}"),
|
|
|
|
QueryInterface : XPCOMUtils.generateQI(
|
|
|
|
[Ci.nsIAuthPrompt2, Ci.nsILoginManagerPrompter]),
|
|
|
|
|
|
|
|
_window : null,
|
2007-11-19 20:51:40 -08:00
|
|
|
_debug : false, // mirrors signon.debug
|
2007-08-31 15:29:55 -07:00
|
|
|
|
|
|
|
__pwmgr : null, // Password Manager service
|
|
|
|
get _pwmgr() {
|
|
|
|
if (!this.__pwmgr)
|
|
|
|
this.__pwmgr = Cc["@mozilla.org/login-manager;1"].
|
|
|
|
getService(Ci.nsILoginManager);
|
|
|
|
return this.__pwmgr;
|
|
|
|
},
|
2007-05-16 03:02:45 -07:00
|
|
|
|
|
|
|
__logService : null, // Console logging service, used for debugging.
|
|
|
|
get _logService() {
|
|
|
|
if (!this.__logService)
|
2007-08-31 15:29:55 -07:00
|
|
|
this.__logService = Cc["@mozilla.org/consoleservice;1"].
|
|
|
|
getService(Ci.nsIConsoleService);
|
2007-05-16 03:02:45 -07:00
|
|
|
return this.__logService;
|
|
|
|
},
|
|
|
|
|
2007-08-31 15:29:55 -07:00
|
|
|
__promptService : null, // Prompt service for user interaction
|
|
|
|
get _promptService() {
|
|
|
|
if (!this.__promptService)
|
|
|
|
this.__promptService =
|
|
|
|
Cc["@mozilla.org/embedcomp/prompt-service;1"].
|
|
|
|
getService(Ci.nsIPromptService2);
|
|
|
|
return this.__promptService;
|
|
|
|
},
|
2007-05-16 03:02:45 -07:00
|
|
|
|
2007-08-31 15:29:55 -07:00
|
|
|
|
|
|
|
__strBundle : null, // String bundle for L10N
|
|
|
|
get _strBundle() {
|
|
|
|
if (!this.__strBundle) {
|
|
|
|
var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
|
|
|
|
getService(Ci.nsIStringBundleService);
|
|
|
|
this.__strBundle = bunService.createBundle(
|
|
|
|
"chrome://passwordmgr/locale/passwordmgr.properties");
|
|
|
|
if (!this.__strBundle)
|
|
|
|
throw "String bundle for Login Manager not present!";
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.__strBundle;
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
__brandBundle : null, // String bundle for L10N
|
|
|
|
get _brandBundle() {
|
|
|
|
if (!this.__brandBundle) {
|
|
|
|
var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
|
|
|
|
getService(Ci.nsIStringBundleService);
|
|
|
|
this.__brandBundle = bunService.createBundle(
|
|
|
|
"chrome://branding/locale/brand.properties");
|
|
|
|
if (!this.__brandBundle)
|
|
|
|
throw "Branding string bundle not present!";
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.__brandBundle;
|
|
|
|
},
|
2007-05-16 03:02:45 -07:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* log
|
|
|
|
*
|
|
|
|
* Internal function for logging debug messages to the Error Console window.
|
|
|
|
*/
|
|
|
|
log : function (message) {
|
|
|
|
if (!this._debug)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dump("Pwmgr Prompter: " + message + "\n");
|
|
|
|
this._logService.logStringMessage("Pwmgr Prompter: " + message);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2007-08-31 15:29:55 -07:00
|
|
|
|
|
|
|
|
|
|
|
/* ---------- nsIAuthPrompt2 prompts ---------- */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-05-16 03:02:45 -07:00
|
|
|
/*
|
|
|
|
* promptAuth
|
|
|
|
*
|
|
|
|
* Implementation of nsIAuthPrompt2.
|
|
|
|
*
|
|
|
|
* nsIChannel aChannel
|
|
|
|
* int aLevel
|
|
|
|
* nsIAuthInformation aAuthInfo
|
|
|
|
*/
|
2007-08-31 15:29:55 -07:00
|
|
|
promptAuth : function (aChannel, aLevel, aAuthInfo) {
|
2007-05-16 03:02:45 -07:00
|
|
|
var selectedLogin = null;
|
2007-08-31 15:29:55 -07:00
|
|
|
var checkbox = { value : false };
|
2007-05-16 03:02:45 -07:00
|
|
|
var checkboxLabel = null;
|
2007-10-24 14:23:50 -07:00
|
|
|
var epicfail = false;
|
2007-05-16 03:02:45 -07:00
|
|
|
|
2007-10-24 14:23:50 -07:00
|
|
|
try {
|
2007-05-16 03:02:45 -07:00
|
|
|
|
2007-10-24 14:23:50 -07:00
|
|
|
this.log("===== promptAuth called =====");
|
|
|
|
|
|
|
|
// If the user submits a login but it fails, we need to remove the
|
|
|
|
// notification bar that was displayed. Conveniently, the user will
|
|
|
|
// be prompted for authentication again, which brings us here.
|
|
|
|
var notifyBox = this._getNotifyBox();
|
|
|
|
if (notifyBox)
|
|
|
|
this._removeSaveLoginNotification(notifyBox);
|
2007-08-31 15:29:55 -07:00
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
var [hostname, httpRealm] = this._getAuthTarget(aChannel, aAuthInfo);
|
2007-05-16 03:02:45 -07:00
|
|
|
|
|
|
|
|
2007-10-24 14:23:50 -07:00
|
|
|
// Looks for existing logins to prefill the prompt with.
|
|
|
|
var foundLogins = this._pwmgr.findLogins({},
|
2007-08-31 15:29:55 -07:00
|
|
|
hostname, null, httpRealm);
|
2007-05-16 03:02:45 -07:00
|
|
|
|
2007-11-19 20:51:40 -08:00
|
|
|
// XXX Can't select from multiple accounts yet. (bug 227632)
|
2007-10-24 14:23:50 -07:00
|
|
|
if (foundLogins.length > 0) {
|
|
|
|
selectedLogin = foundLogins[0];
|
|
|
|
this._SetAuthInfo(aAuthInfo, selectedLogin.username,
|
|
|
|
selectedLogin.password);
|
|
|
|
checkbox.value = true;
|
|
|
|
}
|
2007-05-16 03:02:45 -07:00
|
|
|
|
2007-10-24 14:23:50 -07:00
|
|
|
var canRememberLogin = this._pwmgr.getLoginSavingEnabled(hostname);
|
2007-08-31 15:29:55 -07:00
|
|
|
|
2007-10-24 14:23:50 -07:00
|
|
|
// if checkboxLabel is null, the checkbox won't be shown at all.
|
|
|
|
if (canRememberLogin && !notifyBox)
|
|
|
|
checkboxLabel = this._getLocalizedString("rememberPassword");
|
|
|
|
} catch (e) {
|
|
|
|
// Ignore any errors and display the prompt anyway.
|
|
|
|
epicfail = true;
|
|
|
|
Components.utils.reportError("LoginManagerPrompter: " +
|
|
|
|
"Epic fail in promptAuth: " + e + "\n");
|
|
|
|
}
|
2007-05-16 03:02:45 -07:00
|
|
|
|
|
|
|
var ok = this._promptService.promptAuth(this._window, aChannel,
|
|
|
|
aLevel, aAuthInfo, checkboxLabel, checkbox);
|
2007-10-24 14:23:50 -07:00
|
|
|
if (epicfail)
|
|
|
|
return ok;
|
|
|
|
|
|
|
|
try {
|
2008-02-03 14:11:26 -08:00
|
|
|
var [username, password] = this._GetAuthInfo(aAuthInfo);
|
|
|
|
|
2007-10-24 14:23:50 -07:00
|
|
|
// If there's a notification box, use it to allow the user to
|
|
|
|
// determine if the login should be saved. If there isn't a
|
|
|
|
// notification box, only save the login if the user set the
|
|
|
|
// checkbox to do so.
|
|
|
|
var rememberLogin = notifyBox ? canRememberLogin : checkbox.value;
|
|
|
|
|
|
|
|
if (ok && rememberLogin) {
|
|
|
|
var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
|
|
|
|
createInstance(Ci.nsILoginInfo);
|
|
|
|
newLogin.init(hostname, null, httpRealm,
|
2008-02-03 14:11:26 -08:00
|
|
|
username, password, "", "");
|
2007-10-24 14:23:50 -07:00
|
|
|
|
|
|
|
// If we didn't find an existing login, or if the username
|
|
|
|
// changed, save as a new login.
|
2008-02-03 14:11:26 -08:00
|
|
|
if (!selectedLogin || username != selectedLogin.username) {
|
2007-10-24 14:23:50 -07:00
|
|
|
|
|
|
|
// add as new
|
2008-02-03 14:11:26 -08:00
|
|
|
this.log("New login seen for " + username +
|
2007-10-24 14:23:50 -07:00
|
|
|
" @ " + hostname + " (" + httpRealm + ")");
|
|
|
|
if (notifyBox)
|
|
|
|
this._showSaveLoginNotification(notifyBox, newLogin);
|
|
|
|
else
|
|
|
|
this._pwmgr.addLogin(newLogin);
|
|
|
|
|
|
|
|
} else if (selectedLogin &&
|
2008-02-03 14:11:26 -08:00
|
|
|
password != selectedLogin.password) {
|
2007-10-24 14:23:50 -07:00
|
|
|
|
2008-02-03 14:11:26 -08:00
|
|
|
this.log("Updating password for " + username +
|
2007-10-24 14:23:50 -07:00
|
|
|
" @ " + hostname + " (" + httpRealm + ")");
|
|
|
|
// update password
|
|
|
|
this._pwmgr.modifyLogin(foundLogins[0], newLogin);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
this.log("Login unchanged, no further action needed.");
|
|
|
|
return ok;
|
|
|
|
}
|
2007-05-16 03:02:45 -07:00
|
|
|
}
|
2007-10-24 14:23:50 -07:00
|
|
|
} catch (e) {
|
|
|
|
Components.utils.reportError("LoginManagerPrompter: " +
|
|
|
|
"Fail2 in promptAuth: " + e + "\n");
|
2007-05-16 03:02:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
},
|
|
|
|
|
|
|
|
asyncPromptAuth : function () {
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
},
|
|
|
|
|
2007-08-31 15:29:55 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- nsILoginManagerPrompter prompts ---------- */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* init
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
init : function (aWindow) {
|
|
|
|
this._window = aWindow;
|
2007-11-19 20:51:40 -08:00
|
|
|
|
|
|
|
var prefBranch = Cc["@mozilla.org/preferences-service;1"].
|
|
|
|
getService(Ci.nsIPrefService).getBranch("signon.");
|
|
|
|
this._debug = prefBranch.getBoolPref("debug");
|
2007-08-31 15:29:55 -07:00
|
|
|
this.log("===== initialized =====");
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* promptToSavePassword
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
promptToSavePassword : function (aLogin) {
|
|
|
|
var notifyBox = this._getNotifyBox();
|
|
|
|
|
|
|
|
if (notifyBox)
|
|
|
|
this._showSaveLoginNotification(notifyBox, aLogin);
|
|
|
|
else
|
|
|
|
this._showSaveLoginDialog(aLogin);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* _showSaveLoginNotification
|
|
|
|
*
|
|
|
|
* Displays a notification bar (rather than a popup), to allow the user to
|
|
|
|
* save the specified login. This allows the user to see the results of
|
|
|
|
* their login, and only save a login which they know worked.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
_showSaveLoginNotification : function (aNotifyBox, aLogin) {
|
|
|
|
|
|
|
|
// Ugh. We can't use the strings from the popup window, because they
|
|
|
|
// have the access key marked in the string (eg "Mo&zilla"), along
|
|
|
|
// with some weird rules for handling access keys that do not occur
|
|
|
|
// in the string, for L10N. See commonDialog.js's setLabelForNode().
|
|
|
|
var neverButtonText =
|
|
|
|
this._getLocalizedString("notifyBarNeverForSiteButtonText");
|
|
|
|
var neverButtonAccessKey =
|
|
|
|
this._getLocalizedString("notifyBarNeverForSiteButtonAccessKey");
|
|
|
|
var rememberButtonText =
|
|
|
|
this._getLocalizedString("notifyBarRememberButtonText");
|
|
|
|
var rememberButtonAccessKey =
|
|
|
|
this._getLocalizedString("notifyBarRememberButtonAccessKey");
|
|
|
|
|
|
|
|
var brandShortName =
|
|
|
|
this._brandBundle.GetStringFromName("brandShortName");
|
|
|
|
var notificationText = this._getLocalizedString(
|
|
|
|
"savePasswordText", [brandShortName]);
|
|
|
|
|
|
|
|
// The callbacks in |buttons| have a closure to access the variables
|
|
|
|
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
|
|
|
|
// without a getService() call.
|
|
|
|
var pwmgr = this._pwmgr;
|
|
|
|
|
|
|
|
|
|
|
|
var buttons = [
|
|
|
|
// "Remember" button
|
|
|
|
{
|
|
|
|
label: rememberButtonText,
|
|
|
|
accessKey: rememberButtonAccessKey,
|
|
|
|
popup: null,
|
|
|
|
callback: function(aNotificationBar, aButton) {
|
|
|
|
pwmgr.addLogin(aLogin);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// "Never for this site" button
|
|
|
|
{
|
|
|
|
label: neverButtonText,
|
|
|
|
accessKey: neverButtonAccessKey,
|
|
|
|
popup: null,
|
|
|
|
callback: function(aNotificationBar, aButton) {
|
|
|
|
pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// "Not now" button not needed, as notification bar isn't modal.
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
var oldBar = aNotifyBox.getNotificationWithValue("password-save");
|
|
|
|
const priority = aNotifyBox.PRIORITY_INFO_MEDIUM;
|
|
|
|
|
|
|
|
this.log("Adding new save-password notification bar");
|
|
|
|
var newBar = aNotifyBox.appendNotification(
|
|
|
|
notificationText, "password-save",
|
2008-01-31 12:03:36 -08:00
|
|
|
"chrome://mozapps/skin/passwordmgr/key.png",
|
|
|
|
priority, buttons);
|
2007-08-31 15:29:55 -07:00
|
|
|
|
|
|
|
// The page we're going to hasn't loaded yet, so we want to persist
|
|
|
|
// across the first location change.
|
2008-01-29 04:24:59 -08:00
|
|
|
newBar.persistence++;
|
2007-08-31 15:29:55 -07:00
|
|
|
|
|
|
|
// Sites like Gmail perform a funky redirect dance before you end up
|
|
|
|
// at the post-authentication page. I don't see a good way to
|
|
|
|
// heuristically determine when to ignore such location changes, so
|
|
|
|
// we'll try ignoring location changes based on a time interval.
|
2008-01-29 20:34:19 -08:00
|
|
|
newBar.timeout = Date.now() + 20000; // 20 seconds
|
2007-08-31 15:29:55 -07:00
|
|
|
|
|
|
|
if (oldBar) {
|
|
|
|
this.log("(...and removing old save-password notification bar)");
|
|
|
|
aNotifyBox.removeNotification(oldBar);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* _removeSaveLoginNotification
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
_removeSaveLoginNotification : function (aNotifyBox) {
|
|
|
|
|
|
|
|
var oldBar = aNotifyBox.getNotificationWithValue("password-save");
|
|
|
|
|
|
|
|
if (oldBar) {
|
|
|
|
this.log("Removing save-password notification bar.");
|
|
|
|
aNotifyBox.removeNotification(oldBar);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* _showSaveLoginDialog
|
|
|
|
*
|
|
|
|
* Called when we detect a new login in a form submission,
|
|
|
|
* asks the user what to do.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
_showSaveLoginDialog : function (aLogin) {
|
|
|
|
const buttonFlags = Ci.nsIPrompt.BUTTON_POS_1_DEFAULT +
|
|
|
|
(Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
|
|
|
|
(Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1) +
|
|
|
|
(Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_2);
|
|
|
|
|
|
|
|
var brandShortName =
|
|
|
|
this._brandBundle.GetStringFromName("brandShortName");
|
|
|
|
|
|
|
|
var dialogText = this._getLocalizedString(
|
|
|
|
"savePasswordText", [brandShortName]);
|
|
|
|
var dialogTitle = this._getLocalizedString(
|
|
|
|
"savePasswordTitle");
|
|
|
|
var neverButtonText = this._getLocalizedString(
|
|
|
|
"neverForSiteButtonText");
|
|
|
|
var rememberButtonText = this._getLocalizedString(
|
|
|
|
"rememberButtonText");
|
|
|
|
var notNowButtonText = this._getLocalizedString(
|
|
|
|
"notNowButtonText");
|
|
|
|
|
|
|
|
this.log("Prompting user to save/ignore login");
|
|
|
|
var userChoice = this._promptService.confirmEx(this._window,
|
|
|
|
dialogTitle, dialogText,
|
|
|
|
buttonFlags, rememberButtonText,
|
|
|
|
notNowButtonText, neverButtonText,
|
|
|
|
null, {});
|
|
|
|
// Returns:
|
|
|
|
// 0 - Save the login
|
|
|
|
// 1 - Ignore the login this time
|
|
|
|
// 2 - Never save logins for this site
|
|
|
|
if (userChoice == 2) {
|
|
|
|
this.log("Disabling " + aLogin.hostname + " logins by request.");
|
|
|
|
this._pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
|
|
|
|
} else if (userChoice == 0) {
|
|
|
|
this.log("Saving login for " + aLogin.hostname);
|
2007-10-25 08:57:38 -07:00
|
|
|
this._pwmgr.addLogin(aLogin);
|
2007-08-31 15:29:55 -07:00
|
|
|
} else {
|
|
|
|
// userChoice == 1 --> just ignore the login.
|
|
|
|
this.log("Ignoring login.");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* promptToChangePassword
|
|
|
|
*
|
|
|
|
* Called when we think we detect a password change for an existing
|
|
|
|
* login, when the form being submitted contains multiple password
|
|
|
|
* fields.
|
|
|
|
*
|
|
|
|
*/
|
2007-09-04 19:07:43 -07:00
|
|
|
promptToChangePassword : function (aOldLogin, aNewLogin) {
|
2007-08-31 15:29:55 -07:00
|
|
|
const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
|
|
|
|
|
2007-09-20 18:52:44 -07:00
|
|
|
var dialogText;
|
|
|
|
if (aOldLogin.username)
|
|
|
|
dialogText = this._getLocalizedString(
|
2007-09-04 19:07:43 -07:00
|
|
|
"passwordChangeText",
|
|
|
|
[aOldLogin.username]);
|
2007-09-20 18:52:44 -07:00
|
|
|
else
|
|
|
|
dialogText = this._getLocalizedString(
|
|
|
|
"passwordChangeTextNoUser");
|
|
|
|
|
2007-08-31 15:29:55 -07:00
|
|
|
var dialogTitle = this._getLocalizedString(
|
|
|
|
"passwordChangeTitle");
|
|
|
|
|
|
|
|
// returns 0 for yes, 1 for no.
|
2007-09-04 19:07:43 -07:00
|
|
|
var ok = !this._promptService.confirmEx(this._window,
|
2007-08-31 15:29:55 -07:00
|
|
|
dialogTitle, dialogText, buttonFlags,
|
|
|
|
null, null, null,
|
|
|
|
null, {});
|
2007-09-04 19:07:43 -07:00
|
|
|
if (ok) {
|
|
|
|
this.log("Updating password for user " + aOldLogin.username);
|
|
|
|
this._pwmgr.modifyLogin(aOldLogin, aNewLogin);
|
|
|
|
}
|
2007-08-31 15:29:55 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* promptToChangePasswordWithUsernames
|
|
|
|
*
|
|
|
|
* Called when we detect a password change in a form submission, but we
|
|
|
|
* don't know which existing login (username) it's for. Asks the user
|
|
|
|
* to select a username and confirm the password change.
|
|
|
|
*
|
2007-09-04 19:07:43 -07:00
|
|
|
* Note: The caller doesn't know the username for aNewLogin, so this
|
|
|
|
* function fills in .username and .usernameField with the values
|
|
|
|
* from the login selected by the user.
|
|
|
|
*
|
|
|
|
* Note; XPCOM stupidity: |count| is just |logins.length|.
|
2007-08-31 15:29:55 -07:00
|
|
|
*/
|
2007-09-04 19:07:43 -07:00
|
|
|
promptToChangePasswordWithUsernames : function (logins, count, aNewLogin) {
|
2007-08-31 15:29:55 -07:00
|
|
|
const buttonFlags = Ci.nsIPrompt.STD_YES_NO_BUTTONS;
|
|
|
|
|
2007-09-04 19:07:43 -07:00
|
|
|
var usernames = logins.map(function (l) l.username);
|
2007-08-31 15:29:55 -07:00
|
|
|
var dialogText = this._getLocalizedString("userSelectText");
|
|
|
|
var dialogTitle = this._getLocalizedString("passwordChangeTitle");
|
2007-09-04 19:07:43 -07:00
|
|
|
var selectedIndex = { value: null };
|
2007-08-31 15:29:55 -07:00
|
|
|
|
|
|
|
// If user selects ok, outparam.value is set to the index
|
|
|
|
// of the selected username.
|
|
|
|
var ok = this._promptService.select(this._window,
|
|
|
|
dialogTitle, dialogText,
|
|
|
|
usernames.length, usernames,
|
|
|
|
selectedIndex);
|
2007-09-04 19:07:43 -07:00
|
|
|
if (ok) {
|
|
|
|
// Now that we know which login to change the password for,
|
|
|
|
// update the missing username info in the aNewLogin.
|
|
|
|
|
|
|
|
var selectedLogin = logins[selectedIndex.value];
|
|
|
|
|
|
|
|
this.log("Updating password for user " + selectedLogin.username);
|
2007-08-31 15:29:55 -07:00
|
|
|
|
2007-09-04 19:07:43 -07:00
|
|
|
aNewLogin.username = selectedLogin.username;
|
|
|
|
aNewLogin.usernameField = selectedLogin.usernameField;
|
|
|
|
|
|
|
|
this._pwmgr.modifyLogin(selectedLogin, aNewLogin);
|
|
|
|
}
|
2007-08-31 15:29:55 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------- Internal Methods ---------- */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* _getNotifyBox
|
|
|
|
*
|
|
|
|
* Returns the notification box to this prompter, or null if there isn't
|
|
|
|
* a notification box available.
|
|
|
|
*/
|
|
|
|
_getNotifyBox : function () {
|
|
|
|
try {
|
|
|
|
// Get topmost window, in case we're in a frame.
|
|
|
|
var notifyWindow = this._window.top
|
|
|
|
|
|
|
|
// Some sites pop up a temporary login window, when disappears
|
|
|
|
// upon submission of credentials. We want to put the notification
|
|
|
|
// bar in the opener window if this seems to be happening.
|
|
|
|
if (notifyWindow.opener) {
|
2008-02-07 16:32:36 -08:00
|
|
|
var chromeWin = notifyWindow
|
2007-08-31 15:29:55 -07:00
|
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
2008-02-07 16:32:36 -08:00
|
|
|
.getInterface(Ci.nsIWebNavigation)
|
2007-08-31 15:29:55 -07:00
|
|
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
|
|
|
.rootTreeItem
|
|
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindow);
|
|
|
|
var chromeDoc = chromeWin.document.documentElement;
|
|
|
|
|
2008-02-07 16:32:36 -08:00
|
|
|
// Check to see if the current window was opened with
|
|
|
|
// chrome disabled, and if so use the opener window.
|
|
|
|
if (chromeDoc.getAttribute("chromehidden")) {
|
2007-08-31 15:29:55 -07:00
|
|
|
this.log("Using opener window for notification bar.");
|
|
|
|
notifyWindow = notifyWindow.opener;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Find the <browser> which contains notifyWindow, by looking
|
|
|
|
// through all the open windows and all the <browsers> in each.
|
|
|
|
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
|
|
|
|
getService(Ci.nsIWindowMediator);
|
|
|
|
var enumerator = wm.getEnumerator("navigator:browser");
|
|
|
|
var tabbrowser = null;
|
|
|
|
var foundBrowser = null;
|
|
|
|
|
|
|
|
while (!foundBrowser && enumerator.hasMoreElements()) {
|
|
|
|
var win = enumerator.getNext();
|
|
|
|
tabbrowser = win.getBrowser();
|
|
|
|
foundBrowser = tabbrowser.getBrowserForDocument(
|
|
|
|
notifyWindow.document);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the notificationBox associated with the browser.
|
|
|
|
if (foundBrowser)
|
|
|
|
return tabbrowser.getNotificationBox(foundBrowser)
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
// If any errors happen, just assume no notification box.
|
|
|
|
this.log("No notification box available: " + e)
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2007-12-14 13:14:56 -08:00
|
|
|
* _getLocalizedString
|
2007-08-31 15:29:55 -07:00
|
|
|
*
|
|
|
|
* Can be called as:
|
2007-12-14 13:14:56 -08:00
|
|
|
* _getLocalizedString("key1");
|
2007-08-31 15:29:55 -07:00
|
|
|
* _getLocalizedString("key2", ["arg1"]);
|
|
|
|
* _getLocalizedString("key3", ["arg1", "arg2"]);
|
|
|
|
* (etc)
|
|
|
|
*
|
|
|
|
* Returns the localized string for the specified key,
|
|
|
|
* formatted if required.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
_getLocalizedString : function (key, formatArgs) {
|
|
|
|
if (formatArgs)
|
|
|
|
return this._strBundle.formatStringFromName(
|
|
|
|
key, formatArgs, formatArgs.length);
|
|
|
|
else
|
|
|
|
return this._strBundle.GetStringFromName(key);
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
/*
|
|
|
|
* _getFormattedHostname
|
|
|
|
*
|
|
|
|
* Returns the hostname to use in a nsILoginInfo object (for example,
|
|
|
|
* "http://example.com").
|
2007-05-16 03:02:45 -07:00
|
|
|
*/
|
2007-12-05 13:16:33 -08:00
|
|
|
_getFormattedHostname : function (aURI) {
|
2007-05-16 03:02:45 -07:00
|
|
|
var scheme = aURI.scheme;
|
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
var hostname = scheme + "://" + aURI.host;
|
|
|
|
|
|
|
|
// If the URI explicitly specified a port, only include it when
|
|
|
|
// it's not the default. (We never want "http://foo.com:80")
|
|
|
|
port = aURI.port;
|
|
|
|
if (port != -1) {
|
|
|
|
var ioService = Cc["@mozilla.org/network/io-service;1"].
|
|
|
|
getService(Ci.nsIIOService);
|
|
|
|
var handler = ioService.getProtocolHandler(scheme);
|
|
|
|
if (port != handler.defaultPort)
|
|
|
|
hostname += ":" + port;
|
|
|
|
}
|
2007-05-16 03:02:45 -07:00
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
return hostname;
|
2007-05-16 03:02:45 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
/*
|
|
|
|
* _getAuthTarget
|
|
|
|
*
|
|
|
|
* Returns the hostname and realm for which authentication is being
|
|
|
|
* requested, in the format expected to be used with nsILoginInfo.
|
|
|
|
*/
|
|
|
|
_getAuthTarget : function (aChannel, aAuthInfo) {
|
|
|
|
var hostname, realm;
|
|
|
|
|
|
|
|
// If our proxy is demanding authentication, don't use the
|
|
|
|
// channel's actual destination.
|
|
|
|
if (aAuthInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) {
|
|
|
|
this.log("getAuthTarget is for proxy auth");
|
|
|
|
if (!(aChannel instanceof Ci.nsIProxiedChannel))
|
2007-05-16 03:02:45 -07:00
|
|
|
throw "proxy auth needs nsIProxiedChannel";
|
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
var info = aChannel.proxyInfo;
|
2007-05-16 03:02:45 -07:00
|
|
|
if (!info)
|
|
|
|
throw "proxy auth needs nsIProxyInfo";
|
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
// Proxies don't have a scheme, but we'll use "moz-proxy://"
|
|
|
|
// so that it's more obvious what the login is for.
|
2007-08-31 15:29:55 -07:00
|
|
|
var idnService = Cc["@mozilla.org/network/idn-service;1"].
|
|
|
|
getService(Ci.nsIIDNService);
|
2007-12-05 13:16:33 -08:00
|
|
|
hostname = "moz-proxy://" +
|
|
|
|
idnService.convertUTF8toACE(info.host) +
|
|
|
|
":" + info.port;
|
|
|
|
realm = aAuthInfo.realm;
|
|
|
|
if (!realm)
|
|
|
|
realm = hostname;
|
|
|
|
|
|
|
|
return [hostname, realm];
|
2007-05-16 03:02:45 -07:00
|
|
|
}
|
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
hostname = this._getFormattedHostname(aChannel.URI);
|
2007-05-16 03:02:45 -07:00
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
// If a HTTP WWW-Authenticate header specified a realm, that value
|
|
|
|
// will be available here. If it wasn't set or wasn't HTTP, we'll use
|
|
|
|
// the formatted hostname instead.
|
|
|
|
realm = aAuthInfo.realm;
|
|
|
|
if (!realm)
|
|
|
|
realm = hostname;
|
2007-05-16 03:02:45 -07:00
|
|
|
|
2007-12-05 13:16:33 -08:00
|
|
|
return [hostname, realm];
|
2007-05-16 03:02:45 -07:00
|
|
|
},
|
|
|
|
|
2008-02-03 14:11:26 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns [username, password] as extracted from aAuthInfo (which
|
|
|
|
* holds this info after having prompted the user).
|
|
|
|
*
|
|
|
|
* If the authentication was for a Windows domain, we'll prepend the
|
|
|
|
* return username with the domain. (eg, "domain\user")
|
|
|
|
*/
|
|
|
|
_GetAuthInfo : function (aAuthInfo) {
|
|
|
|
var username, password;
|
|
|
|
|
|
|
|
var flags = aAuthInfo.flags;
|
|
|
|
if (flags & Ci.nsIAuthInformation.NEED_DOMAIN)
|
|
|
|
username = aAuthInfo.domain + "\\" + aAuthInfo.username;
|
|
|
|
else
|
|
|
|
username = aAuthInfo.username;
|
|
|
|
|
|
|
|
password = aAuthInfo.password;
|
|
|
|
|
|
|
|
return [username, password];
|
|
|
|
},
|
|
|
|
|
|
|
|
|
2007-05-16 03:02:45 -07:00
|
|
|
/**
|
|
|
|
* Given a username (possibly in DOMAIN\user form) and password, parses the
|
|
|
|
* domain out of the username if necessary and sets domain, username and
|
|
|
|
* password on the auth information object.
|
|
|
|
*/
|
|
|
|
_SetAuthInfo : function (aAuthInfo, username, password) {
|
|
|
|
var flags = aAuthInfo.flags;
|
|
|
|
if (flags & Ci.nsIAuthInformation.NEED_DOMAIN) {
|
|
|
|
// Domain is separated from username by a backslash
|
|
|
|
var idx = username.indexOf("\\");
|
|
|
|
if (idx == -1) {
|
|
|
|
aAuthInfo.username = username;
|
|
|
|
} else {
|
|
|
|
aAuthInfo.domain = username.substring(0, idx);
|
|
|
|
aAuthInfo.username = username.substring(idx+1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
aAuthInfo.username = username;
|
|
|
|
}
|
|
|
|
aAuthInfo.password = password;
|
|
|
|
}
|
2007-08-31 15:29:55 -07:00
|
|
|
|
2007-05-16 03:02:45 -07:00
|
|
|
}; // end of LoginManagerPrompter implementation
|
|
|
|
|
2007-08-31 15:29:55 -07:00
|
|
|
var component = [LoginManagerPromptFactory, LoginManagerPrompter];
|
2007-05-16 03:02:45 -07:00
|
|
|
function NSGetModule(compMgr, fileSpec) {
|
2007-06-30 18:41:33 -07:00
|
|
|
return XPCOMUtils.generateModule(component);
|
2007-05-16 03:02:45 -07:00
|
|
|
}
|