diff --git a/toolkit/components/passwordmgr/src/nsLoginManagerPrompter.js b/toolkit/components/passwordmgr/src/nsLoginManagerPrompter.js index 4bfae9d0f07..2ff9b620e1e 100644 --- a/toolkit/components/passwordmgr/src/nsLoginManagerPrompter.js +++ b/toolkit/components/passwordmgr/src/nsLoginManagerPrompter.js @@ -215,46 +215,49 @@ LoginManagerPrompter.prototype = { */ promptUsernameAndPassword : function (aDialogTitle, aText, aPasswordRealm, aSavePassword, aUsername, aPassword) { + this.log("===== promptUsernameAndPassword() called ====="); + if (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_FOR_SESSION) throw Components.results.NS_ERROR_NOT_IMPLEMENTED; var selectedLogin = null; var checkBox = { value : false }; var checkBoxLabel = null; - var hostname = this._getFormattedHostname(aPasswordRealm); + var [hostname, realm, unused] = this._getRealmInfo(aPasswordRealm); - this.log("===== promptUsernameAndPassword() called ====="); + // If hostname is null, we can't save this login. + if (hostname) { + var canRememberLogin = (aSavePassword == + Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) && + this._pwmgr.getLoginSavingEnabled(hostname); - var canRememberLogin = (aSavePassword == - Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) && - this._pwmgr.getLoginSavingEnabled(hostname); + // if checkBoxLabel is null, the checkbox won't be shown at all. + if (canRememberLogin) + checkBoxLabel = this._getLocalizedString("rememberPassword"); - // if checkBoxLabel is null, the checkbox won't be shown at all. - if (canRememberLogin) - checkBoxLabel = this._getLocalizedString("rememberPassword"); + // Look for existing logins. + var foundLogins = this._pwmgr.findLogins({}, hostname, null, + realm); - // Look for existing logins. - var foundLogins = this._pwmgr.findLogins({}, hostname, null, - aPasswordRealm); + // XXX Like the original code, we can't deal with multiple + // account selection. (bug 227632) + if (foundLogins.length > 0) { + selectedLogin = foundLogins[0]; - // XXX Like the original code, we can't deal with multiple - // account selection. (bug 227632) - if (foundLogins.length > 0) { - selectedLogin = foundLogins[0]; + // If the caller provided a username, try to use it. If they + // provided only a password, this will try to find a password-only + // login (or return null if none exists). + if (aUsername.value) + selectedLogin = this._repickSelectedLogin(foundLogins, + aUsername.value); - // If the caller provided a username, try to use it. If they - // provided only a password, this will try to find a password-only - // login (or return null if none exists). - if (aUsername.value) - selectedLogin = this._repickSelectedLogin(foundLogins, - aUsername.value); - - if (selectedLogin) { - checkBox.value = true; - aUsername.value = selectedLogin.username; - // If the caller provided a password, prefer it. - if (!aPassword.value) - aPassword.value = selectedLogin.password; + if (selectedLogin) { + checkBox.value = true; + aUsername.value = selectedLogin.username; + // If the caller provided a password, prefer it. + if (!aPassword.value) + aPassword.value = selectedLogin.password; + } } } @@ -262,14 +265,12 @@ LoginManagerPrompter.prototype = { aDialogTitle, aText, aUsername, aPassword, checkBoxLabel, checkBox); - if (!ok || !checkBox.value) + if (!ok || !checkBox.value || !hostname) return ok; - var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"]. createInstance(Ci.nsILoginInfo); - newLogin.init(hostname, null, aPasswordRealm, - aUsername.value, aPassword.value, + newLogin.init(hostname, null, realm, aUsername.value, aPassword.value, "", ""); // XXX We can't prompt with multiple logins yet (bug 227632), so @@ -281,11 +282,11 @@ LoginManagerPrompter.prototype = { // changed, save as a new login. if (!selectedLogin) { // add as new - this.log("New login seen for " + aPasswordRealm); + this.log("New login seen for " + realm); this._pwmgr.addLogin(newLogin); } else if (aPassword.value != selectedLogin.password) { // update password - this.log("Updating password for " + aPasswordRealm); + this.log("Updating password for " + realm); this._pwmgr.modifyLogin(selectedLogin, newLogin); } else { this.log("Login unchanged, no further action needed."); @@ -306,54 +307,56 @@ LoginManagerPrompter.prototype = { * allows it, then the password will be saved in the database. */ promptPassword : function (aDialogTitle, aText, aPasswordRealm, - aSavePassword, aPassword) { + aSavePassword, aPassword) { + this.log("===== promptPassword called() ====="); + if (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_FOR_SESSION) throw Components.results.NS_ERROR_NOT_IMPLEMENTED; var checkBox = { value : false }; var checkBoxLabel = null; - var [hostname, username, pathname] = this._decomposeURI(aPasswordRealm); - var newRealm = hostname + pathname; + var [hostname, realm, username] = this._getRealmInfo(aPasswordRealm); - this.log("===== promptPassword called() ====="); - - var canRememberLogin = (aSavePassword == - Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) && - this._pwmgr.getLoginSavingEnabled(hostname); - - // if checkBoxLabel is null, the checkbox won't be shown at all. - if (canRememberLogin) - checkBoxLabel = this._getLocalizedString("rememberPassword"); - - if (!aPassword.value) { - // Look for existing logins. - var foundLogins = this._pwmgr.findLogins({}, hostname, null, - newRealm); - - // XXX Like the original code, we can't deal with multiple - // account selection (bug 227632). We can deal with finding the - // account based on the supplied username - but in this case we'll - // just return the first match. - for (var i = 0; i < foundLogins.length; ++i) { - if (foundLogins[i].username == username) { - aPassword.value = foundLogins[i].password; - // wallet returned straight away, so this mimics that code - return true; - } - } + // If hostname is null, we can't save this login. + if (hostname) { + var canRememberLogin = (aSavePassword == + Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) && + this._pwmgr.getLoginSavingEnabled(hostname); + + // if checkBoxLabel is null, the checkbox won't be shown at all. + if (canRememberLogin) + checkBoxLabel = this._getLocalizedString("rememberPassword"); + + if (!aPassword.value) { + // Look for existing logins. + var foundLogins = this._pwmgr.findLogins({}, hostname, null, + realm); + + // XXX Like the original code, we can't deal with multiple + // account selection (bug 227632). We can deal with finding the + // account based on the supplied username - but in this case we'll + // just return the first match. + for (var i = 0; i < foundLogins.length; ++i) { + if (foundLogins[i].username == username) { + aPassword.value = foundLogins[i].password; + // wallet returned straight away, so this mimics that code + return true; + } + } + } } var ok = this._promptService.promptPassword(this._window, aDialogTitle, aText, aPassword, checkBoxLabel, checkBox); - if (ok && checkBox.value) { + if (ok && checkBox.value && hostname) { var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"]. createInstance(Ci.nsILoginInfo); - newLogin.init(hostname, null, newRealm, username, + newLogin.init(hostname, null, realm, username, aPassword.value, "", ""); - this.log("New login seen for " + newRealm); + this.log("New login seen for " + realm); this._pwmgr.addLogin(newLogin); } @@ -361,8 +364,36 @@ LoginManagerPrompter.prototype = { return ok; }, + /* ---------- nsIAuthPrompt helpers ---------- */ + /** + * Given aRealmString, such as "http://user@example.com/foo", returns an + * array of: + * - the formatted hostname + * - the realm (hostname + path) + * - the username, if present + * + * If aRealmString is in the format produced by NS_GetAuthKey for HTTP[S] + * channels, e.g. "example.com:80 (httprealm)", null is returned for all + * arguments to let callers know the login can't be saved because we don't + * know whether it's http or https. + */ + _getRealmInfo : function (aRealmString) { + var httpRealm = /^.+ \(.+\)$/; + if (httpRealm.test(aRealmString)) + return [null, null, null]; + + var uri = this._ioService.newURI(aRealmString, null, null); + var pathname = ""; + + if (uri.path != "/") + pathname = uri.path; + + var formattedHostname = this._getFormattedHostname(uri); + + return [formattedHostname, formattedHostname + pathname, uri.username]; + }, /* ---------- nsIAuthPrompt2 prompts ---------- */ @@ -977,20 +1008,6 @@ LoginManagerPrompter.prototype = { return hostname; }, - /** - * Extracts a hostname, username and a pathname from a string based URI - * via a standard URI implementation. - */ - _decomposeURI: function (aURIString) { - var uri = this._ioService.newURI(aURIString, null, null); - var pathname = ""; - - if (uri.path != "/") - pathname = uri.path; - - return [this._getFormattedHostname(uri), uri.username, pathname]; - }, - /* * _getAuthTarget * diff --git a/toolkit/components/passwordmgr/test/test_prompt.html b/toolkit/components/passwordmgr/test/test_prompt.html index a45ebaac93b..0184b5d376c 100644 --- a/toolkit/components/passwordmgr/test/test_prompt.html +++ b/toolkit/components/passwordmgr/test/test_prompt.html @@ -145,6 +145,12 @@ function handleDialog(doc, testNum) { passfield.setAttribute("value", "secret"); break; + case 30: + case 31: + is(password, "", "Checking provided password"); + passfield.setAttribute("value", "fill2pass"); + break; + case 100: is(username, "inuser", "Checking provided username"); is(password, "inpass", "Checking provided password"); @@ -188,6 +194,14 @@ function handleDialog(doc, testNum) { passfield.setAttribute("value", "user2pass"); break; + case 120: + case 121: + is(username, "", "Checking filled username"); + is(password, "", "Checking filled password"); + userfield.setAttribute("value", "fill2user"); + passfield.setAttribute("value", "fill2pass"); + break; + case 500: is(username, "inuser", "Checking unfilled username"); is(password, "inpass", "Checking unfilled password"); @@ -493,6 +507,27 @@ is(pword.value, "user2pass", "Checking returned password"); // XXX test saving a password with Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY +// ===== test 30 ===== +// We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt +testNum = 30; +pword.value = null; +startCallbackTimer(); +isOk = prompter1.promptPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)", + Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword); +ok(isOk, "Checking dialog return value (accept)"); +is(pword.value, "fill2pass", "Checking returned password"); + +// ===== test 31 ===== +// We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt +testNum++; +pword.value = null; +startCallbackTimer(); +isOk = prompter1.promptPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)", + Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, pword); +ok(isOk, "Checking dialog return value (accept)"); +is(pword.value, "fill2pass", "Checking returned password"); + + // ===== test 100 ===== testNum = 100; uname.value = "inuser"; @@ -585,6 +620,29 @@ ok(isOk, "Checking dialog return value (accept)"); is(uname.value, "user2name", "Checking returned username"); is(pword.value, "user2pass", "Checking returned password"); +// ===== test 120 ===== +// We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt +testNum = 120; +uname.value = null; +pword.value = null; +startCallbackTimer(); +isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)", + Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword); +ok(isOk, "Checking dialog return value (accept)"); +is(uname.value, "fill2user", "Checking returned username"); +is(pword.value, "fill2pass", "Checking returned password"); + +// ===== test 121 ===== +// We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt +testNum++; +uname.value = null; +pword.value = null; +startCallbackTimer(); +isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)", + Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword); +ok(isOk, "Checking dialog return value (accept)"); +is(uname.value, "fill2user", "Checking returned username"); +is(pword.value, "fill2pass", "Checking returned password"); var channel1 = Cc["@mozilla.org/network/io-service;1"].