Software: Apache. PHP/5.5.15 uname -a: Windows NT SVR-DMZ 6.1 build 7600 (Windows Server 2008 R2 Enterprise Edition) i586 SYSTEM Safe-mode: OFF (not secure) C:\Extranet\C\Archivos de programa\Mozilla Firefox\components\ drwxrwxrwx |
Viewing file: Select action/file-type: /* ***** 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; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); function LoginManager() { this.init(); } LoginManager.prototype = { classDescription: "LoginManager", contractID: "@mozilla.org/login-manager;1", classID: Components.ID("{cb9e0de8-3598-4ed7-857b-827f011ad5d8}"), QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManager, Ci.nsISupportsWeakReference]), /* ---------- private memebers ---------- */ __logService : null, // Console logging service, used for debugging. get _logService() { if (!this.__logService) this.__logService = Cc["@mozilla.org/consoleservice;1"]. getService(Ci.nsIConsoleService); return this.__logService; }, __ioService: null, // IO service for string -> nsIURI conversion get _ioService() { if (!this.__ioService) this.__ioService = Cc["@mozilla.org/network/io-service;1"]. getService(Ci.nsIIOService); return this.__ioService; }, __formFillService : null, // FormFillController, for username autocompleting get _formFillService() { if (!this.__formFillService) this.__formFillService = Cc["@mozilla.org/satchel/form-fill-controller;1"]. getService(Ci.nsIFormFillController); return this.__formFillService; }, __storage : null, // Storage component which contains the saved logins get _storage() { if (!this.__storage) { var contractID = "@mozilla.org/login-manager/storage/legacy;1"; try { var catMan = Cc["@mozilla.org/categorymanager;1"]. getService(Ci.nsICategoryManager); contractID = catMan.getCategoryEntry("login-manager-storage", "nsILoginManagerStorage"); this.log("Found alternate nsILoginManagerStorage with " + "contract ID: " + contractID); } catch (e) { this.log("No alternate nsILoginManagerStorage registered"); } this.__storage = Cc[contractID]. createInstance(Ci.nsILoginManagerStorage); try { this.__storage.init(); } catch (e) { this.log("Initialization of storage component failed: " + e); this.__storage = null; } } return this.__storage; }, _prefBranch : null, // Preferences service _nsLoginInfo : null, // Constructor for nsILoginInfo implementation _remember : true, // mirrors signon.rememberSignons preference _debug : false, // mirrors signon.debug /* * init * * Initialize the Login Manager. Automatically called when service * is created. * * Note: Service created in /browser/base/content/browser.js, * delayedStartup() */ init : function () { // Cache references to current |this| in utility objects this._webProgressListener._domEventListener = this._domEventListener; this._webProgressListener._pwmgr = this; this._domEventListener._pwmgr = this; this._observer._pwmgr = this; // Preferences. Add observer so we get notified of changes. this._prefBranch = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefService).getBranch("signon."); this._prefBranch.QueryInterface(Ci.nsIPrefBranch2); this._prefBranch.addObserver("", this._observer, false); // Get current preference values. this._debug = this._prefBranch.getBoolPref("debug"); this._remember = this._prefBranch.getBoolPref("rememberSignons"); // Get constructor for nsILoginInfo this._nsLoginInfo = new Components.Constructor( "@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo); // Form submit observer checks forms for new logins and pw changes. var observerService = Cc["@mozilla.org/observer-service;1"]. getService(Ci.nsIObserverService); observerService.addObserver(this._observer, "earlyformsubmit", false); observerService.addObserver(this._observer, "xpcom-shutdown", false); // WebProgressListener for getting notification of new doc loads. var progress = Cc["@mozilla.org/docloaderservice;1"]. getService(Ci.nsIWebProgress); progress.addProgressListener(this._webProgressListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); }, /* * log * * Internal function for logging debug messages to the Error Console window */ log : function (message) { if (!this._debug) return; dump("Login Manager: " + message + "\n"); this._logService.logStringMessage("Login Manager: " + message); }, /* ---------- Utility objects ---------- */ /* * _observer object * * Internal utility object, implements the nsIObserver interface. * Used to receive notification for: form submission, preference changes. */ _observer : { _pwmgr : null, QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsIFormSubmitObserver, Ci.nsISupportsWeakReference]), // nsFormSubmitObserver notify : function (formElement, aWindow, actionURI) { this._pwmgr.log("observer notified for form submission."); // We're invoked before the content's |onsubmit| handlers, so we // can grab form data before it might be modified (see bug 257781). try { this._pwmgr._onFormSubmit(formElement); } catch (e) { this._pwmgr.log("Caught error in onFormSubmit: " + e); } return true; // Always return true, or form submit will be canceled. }, // nsObserver observe : function (subject, topic, data) { if (topic == "nsPref:changed") { var prefName = data; this._pwmgr.log("got change to " + prefName + " preference"); if (prefName == "debug") { this._pwmgr._debug = this._pwmgr._prefBranch.getBoolPref("debug"); } else if (prefName == "rememberSignons") { this._pwmgr._remember = this._pwmgr._prefBranch.getBoolPref("rememberSignons"); } else { this._pwmgr.log("Oops! Pref not handled, change ignored."); } } else if (topic == "xpcom-shutdown") { for (let i in this._pwmgr) { try { this._pwmgr[i] = null; } catch(ex) {} } this._pwmgr = null; } else { this._pwmgr.log("Oops! Unexpected notification: " + topic); } } }, /* * _webProgressListener object * * Internal utility object, implements nsIWebProgressListener interface. * This is attached to the document loader service, so we get * notifications about all page loads. */ _webProgressListener : { _pwmgr : null, _domEventListener : null, QueryInterface : XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), onStateChange : function (aWebProgress, aRequest, aStateFlags, aStatus) { // STATE_START is too early, doc is still the old page. if (!(aStateFlags & Ci.nsIWebProgressListener.STATE_TRANSFERRING)) return; if (!this._pwmgr._remember) return; var domWin = aWebProgress.DOMWindow; var domDoc = domWin.document; // Only process things which might have HTML forms. if (!(domDoc instanceof Ci.nsIDOMHTMLDocument)) return; this._pwmgr.log("onStateChange accepted: req = " + (aRequest ? aRequest.name : "(null)") + ", flags = 0x" + aStateFlags.toString(16)); // Fastback doesn't fire DOMContentLoaded, so process forms now. if (aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING) { this._pwmgr.log("onStateChange: restoring document"); return this._pwmgr._fillDocument(domDoc); } // Add event listener to process page when DOM is complete. domDoc.addEventListener("DOMContentLoaded", this._domEventListener, false); return; }, // stubs for the nsIWebProgressListener interfaces which we don't use. onProgressChange : function() { throw "Unexpected onProgressChange"; }, onLocationChange : function() { throw "Unexpected onLocationChange"; }, onStatusChange : function() { throw "Unexpected onStatusChange"; }, onSecurityChange : function() { throw "Unexpected onSecurityChange"; } }, /* * _domEventListener object * * Internal utility object, implements nsIDOMEventListener * Used to catch certain DOM events needed to properly implement form fill. */ _domEventListener : { _pwmgr : null, QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMEventListener, Ci.nsISupportsWeakReference]), handleEvent : function (event) { this._pwmgr.log("domEventListener: got event " + event.type); var doc, inputElement; switch (event.type) { case "DOMContentLoaded": doc = event.target; this._pwmgr._fillDocument(doc); return; case "DOMAutoComplete": case "blur": inputElement = event.target; this._pwmgr._fillPassword(inputElement); return; default: this._pwmgr.log("Oops! This event unexpected."); return; } } }, /* ---------- Primary Public interfaces ---------- */ /* * addLogin * * Add a new login to login storage. */ addLogin : function (login) { // Sanity check the login if (login.hostname == null || login.hostname.length == 0) throw "Can't add a login with a null or empty hostname."; // For logins w/o a username, set to "", not null. if (login.username == null) throw "Can't add a login with a null username."; if (login.password == null || login.password.length == 0) throw "Can't add a login with a null or empty password."; if (login.formSubmitURL || login.formSubmitURL == "") { // We have a form submit URL. Can't have a HTTP realm. if (login.httpRealm != null) throw "Can't add a login with both a httpRealm and formSubmitURL."; } else if (login.httpRealm) { // We have a HTTP realm. Can't have a form submit URL. if (login.formSubmitURL != null) throw "Can't add a login with both a httpRealm and formSubmitURL."; } else { // Need one or the other! throw "Can't add a login without a httpRealm or formSubmitURL."; } // Look for an existing entry. var logins = this.findLogins({}, login.hostname, login.formSubmitURL, login.httpRealm); if (logins.some(function(l) login.matches(l, true))) throw "This login already exists."; this.log("Adding login: " + login); return this._storage.addLogin(login); }, /* * removeLogin * * Remove the specified login from the stored logins. */ removeLogin : function (login) { this.log("Removing login: " + login); return this._storage.removeLogin(login); }, /* * modifyLogin * * Change the specified login to match the new login. */ modifyLogin : function (oldLogin, newLogin) { this.log("Modifying oldLogin: " + oldLogin + " newLogin: " + newLogin); return this._storage.modifyLogin(oldLogin, newLogin); }, /* * getAllLogins * * Get a dump of all stored logins. Used by the login manager UI. * * |count| is only needed for XPCOM. * * Returns an array of logins. If there are no logins, the array is empty. */ getAllLogins : function (count) { this.log("Getting a list of all logins"); return this._storage.getAllLogins(count); }, /* * removeAllLogins * * Remove all stored logins. */ removeAllLogins : function () { this.log("Removing all logins"); this._storage.removeAllLogins(); }, /* * getAllDisabledHosts * * Get a list of all hosts for which logins are disabled. * * |count| is only needed for XPCOM. * * Returns an array of disabled logins. If there are no disabled logins, * the array is empty. */ getAllDisabledHosts : function (count) { this.log("Getting a list of all disabled hosts"); return this._storage.getAllDisabledHosts(count); }, /* * findLogins * * Search for the known logins for entries matching the specified criteria. */ findLogins : function (count, hostname, formSubmitURL, httpRealm) { this.log("Searching for logins matching host: " + hostname + ", formSubmitURL: " + formSubmitURL + ", httpRealm: " + httpRealm); return this._storage.findLogins(count, hostname, formSubmitURL, httpRealm); }, /* * countLogins * * Search for the known logins for entries matching the specified criteria, * returns only the count. */ countLogins : function (hostname, formSubmitURL, httpRealm) { this.log("Counting logins matching host: " + hostname + ", formSubmitURL: " + formSubmitURL + ", httpRealm: " + httpRealm); return this._storage.countLogins(hostname, formSubmitURL, httpRealm); }, /* * getLoginSavingEnabled * * Check to see if user has disabled saving logins for the host. */ getLoginSavingEnabled : function (host) { this.log("Checking if logins to " + host + " can be saved."); if (!this._remember) return false; return this._storage.getLoginSavingEnabled(host); }, /* * setLoginSavingEnabled * * Enable or disable storing logins for the specified host. */ setLoginSavingEnabled : function (hostname, enabled) { // Nulls won't round-trip with getAllDisabledHosts(). if (hostname.indexOf("\0") != -1) throw "Invalid hostname"; this.log("Saving logins for " + hostname + " enabled? " + enabled); return this._storage.setLoginSavingEnabled(hostname, enabled); }, /* * autoCompleteSearch * * Yuck. This is called directly by satchel: * nsFormFillController::StartSearch() * [toolkit/components/satchel/src/nsFormFillController.cpp] * * We really ought to have a simple way for code to register an * auto-complete provider, and not have satchel calling pwmgr directly. */ autoCompleteSearch : function (aSearchString, aPreviousResult, aElement) { // aPreviousResult & aResult are nsIAutoCompleteResult, // aElement is nsIDOMHTMLInputElement if (!this._remember) return false; this.log("AutoCompleteSearch invoked. Search is: " + aSearchString); var result = null; if (aPreviousResult) { this.log("Using previous autocomplete result"); result = aPreviousResult; // We have a list of results for a shorter search string, so just // filter them further based on the new search string. // Count backwards, because result.matchCount is decremented // when we remove an entry. for (var i = result.matchCount - 1; i >= 0; i--) { var match = result.getValueAt(i); // Remove results that are too short, or have different prefix. if (aSearchString.length > match.length || aSearchString.toLowerCase() != match.substr(0, aSearchString.length).toLowerCase()) { this.log("Removing autocomplete entry '" + match + "'"); result.removeValueAt(i, false); } } } else { this.log("Creating new autocomplete search result."); var doc = aElement.ownerDocument; var origin = this._getPasswordOrigin(doc.documentURI); var actionOrigin = this._getActionOrigin(aElement.form); var logins = this.findLogins({}, origin, actionOrigin, null); var matchingLogins = []; for (i = 0; i < logins.length; i++) { var username = logins[i].username.toLowerCase(); if (aSearchString.length <= username.length && aSearchString.toLowerCase() == username.substr(0, aSearchString.length)) { matchingLogins.push(logins[i]); } } this.log(matchingLogins.length + " autocomplete logins avail."); result = new UserAutoCompleteResult(aSearchString, matchingLogins); } return result; }, /* ------- Internal methods / callbacks for document integration ------- */ /* * _getPasswordFields * * Returns an array of password field elements for the specified form. * If no pw fields are found, or if more than 3 are found, then null * is returned. * * skipEmptyFields can be set to ignore password fields with no value. */ _getPasswordFields : function (form, skipEmptyFields) { // Locate the password fields in the form. var pwFields = []; for (var i = 0; i < form.elements.length; i++) { if (form.elements[i].type != "password") continue; if (skipEmptyFields && !form.elements[i].value) continue; pwFields[pwFields.length] = { index : i, element : form.elements[i] }; } // If too few or too many fields, bail out. if (pwFields.length == 0) { this.log("(form ignored -- no password fields.)"); return null; } else if (pwFields.length > 3) { this.log("(form ignored -- too many password fields. [got " + pwFields.length + "])"); return null; } return pwFields; }, /* * _getFormFields * * Returns the username and password fields found in the form. * Can handle complex forms by trying to figure out what the * relevant fields are. * * Returns: [usernameField, newPasswordField, oldPasswordField] * * usernameField may be null. * newPasswordField will always be non-null. * oldPasswordField may be null. If null, newPasswordField is just * "theLoginField". If not null, the form is apparently a * change-password field, with oldPasswordField containing the password * that is being changed. */ _getFormFields : function (form, isSubmission) { var usernameField = null; // Locate the password field(s) in the form. Up to 3 supported. // If there's no password field, there's nothing for us to do. var pwFields = this._getPasswordFields(form, isSubmission); if (!pwFields) return [null, null, null]; // Locate the username field in the form by searching backwards // from the first passwordfield, assume the first text field is the // username. We might not find a username field if the user is // already logged in to the site. for (var i = pwFields[0].index - 1; i >= 0; i--) { if (form.elements[i].type == "text") { usernameField = form.elements[i]; break; } } if (!usernameField) this.log("(form -- no username field found)"); // If we're not submitting a form (it's a page load), there are no // password field values for us to use for identifying fields. So, // just assume the first password field is the one to be filled in. if (!isSubmission || pwFields.length == 1) return [usernameField, pwFields[0].element, null]; // Try to figure out WTF is in the form based on the password values. var oldPasswordField, newPasswordField; var pw1 = pwFields[0].element.value; var pw2 = pwFields[1].element.value; var pw3 = (pwFields[2] ? pwFields[2].element.value : null); if (pwFields.length == 3) { // Look for two identical passwords, that's the new password if (pw1 == pw2 && pw2 == pw3) { // All 3 passwords the same? Weird! Treat as if 1 pw field. newPasswordField = pwFields[0].element; oldPasswordField = null; } else if (pw1 == pw2) { newPasswordField = pwFields[0].element; oldPasswordField = pwFields[2].element; } else if (pw2 == pw3) { oldPasswordField = pwFields[0].element; newPasswordField = pwFields[2].element; } else if (pw1 == pw3) { // A bit odd, but could make sense with the right page layout. newPasswordField = pwFields[0].element; oldPasswordField = pwFields[1].element; } else { // We can't tell which of the 3 passwords should be saved. this.log("(form ignored -- all 3 pw fields differ)"); return [null, null, null]; } } else { // pwFields.length == 2 if (pw1 == pw2) { // Treat as if 1 pw field newPasswordField = pwFields[0].element; oldPasswordField = null; } else { // Just assume that the 2nd password is the new password oldPasswordField = pwFields[0].element; newPasswordField = pwFields[1].element; } } return [usernameField, newPasswordField, oldPasswordField]; }, /* * _isAutoCompleteDisabled * * Returns true if the page requests autocomplete be disabled for the * specified form input. */ _isAutocompleteDisabled : function (element) { if (element && element.hasAttribute("autocomplete") && element.getAttribute("autocomplete").toLowerCase() == "off") return true; return false; }, /* * _onFormSubmit * * Called by the our observer when notified of a form submission. * [Note that this happens before any DOM onsubmit handlers are invoked.] * Looks for a password change in the submitted form, so we can update * our stored password. */ _onFormSubmit : function (form) { // local helper function function getPrompter(aWindow) { var prompterSvc = Cc["@mozilla.org/login-manager/prompter;1"]. createInstance(Ci.nsILoginManagerPrompter); prompterSvc.init(aWindow); return prompterSvc; } var doc = form.ownerDocument; var win = doc.defaultView; // If password saving is disabled (globally or for host), bail out now. if (!this._remember) return; var hostname = this._getPasswordOrigin(doc.documentURI); var formSubmitURL = this._getActionOrigin(form) if (!this.getLoginSavingEnabled(hostname)) { this.log("(form submission ignored -- saving is " + "disabled for: " + hostname + ")"); return; } // Get the appropriate fields from the form. var [usernameField, newPasswordField, oldPasswordField] = this._getFormFields(form, true); // Need at least 1 valid password field to do anything. if (newPasswordField == null) return; // Check for autocomplete=off attribute. We don't use it to prevent // autofilling (for existing logins), but won't save logins when it's // present. if (this._isAutocompleteDisabled(form) || this._isAutocompleteDisabled(usernameField) || this._isAutocompleteDisabled(newPasswordField) || this._isAutocompleteDisabled(oldPasswordField)) { this.log("(form submission ignored -- autocomplete=off found)"); return; } var formLogin = new this._nsLoginInfo(); formLogin.init(hostname, formSubmitURL, null, (usernameField ? usernameField.value : ""), newPasswordField.value, (usernameField ? usernameField.name : ""), newPasswordField.name); // If we didn't find a username field, but seem to be changing a // password, allow the user to select from a list of applicable // logins to update the password for. if (!usernameField && oldPasswordField) { var logins = this.findLogins({}, hostname, formSubmitURL, null); if (logins.length == 0) { // Could prompt to save this as a new password-only login. // This seems uncommon, and might be wrong, so ignore. this.log("(no logins for this host -- pwchange ignored)"); return; } var prompter = getPrompter(win); if (logins.length == 1) { var oldLogin = logins[0]; formLogin.username = oldLogin.username; formLogin.usernameField = oldLogin.usernameField; prompter.promptToChangePassword(oldLogin, formLogin); } else { prompter.promptToChangePasswordWithUsernames( logins, logins.length, formLogin); } return; } // Look for an existing login that matches the form login. var existingLogin = null; var logins = this.findLogins({}, hostname, formSubmitURL, null); for (var i = 0; i < logins.length; i++) { var same, login = logins[i]; // If one login has a username but the other doesn't, ignore // the username when comparing and only match if they have the // same password. Otherwise, compare the logins and match even // if the passwords differ. if (!login.username && formLogin.username) { var restoreMe = formLogin.username; formLogin.username = ""; same = formLogin.matches(login, false); formLogin.username = restoreMe; } else if (!formLogin.username && login.username) { formLogin.username = login.username; same = formLogin.matches(login, false); formLogin.username = ""; // we know it's always blank. } else { same = formLogin.matches(login, true); } if (same) { existingLogin = login; break; } } if (existingLogin) { this.log("Found an existing login matching this form submission"); /* * Change password if needed. * * If the login has a username, change the password w/o prompting * (because we can be fairly sure there's only one password * associated with the username). But for logins without a * username, ask the user... Some sites use a password-only "login" * in different contexts (enter your PIN, answer a security * question, etc), and without a username we can't be sure if * modifying an existing login is the right thing to do. */ if (existingLogin.password != formLogin.password) { if (formLogin.username) { this.log("...Updating password for existing login."); this.modifyLogin(existingLogin, formLogin); } else { this.log("...passwords differ, prompting to change."); prompter = getPrompter(win); prompter.promptToChangePassword(existingLogin, formLogin); } } return; } // Prompt user to save login (via dialog or notification bar) prompter = getPrompter(win); prompter.promptToSavePassword(formLogin); }, /* * _getPasswordOrigin * * Get the parts of the URL we want for identification. */ _getPasswordOrigin : function (uriString, allowJS) { var realm = ""; try { var uri = this._ioService.newURI(uriString, null, null); if (allowJS && uri.scheme == "javascript") return "javascript:" realm = uri.scheme + "://" + uri.host; // If the URI explicitly specified a port, only include it when // it's not the default. (We never want "http://foo.com:80") var port = uri.port; if (port != -1) { var handler = this._ioService.getProtocolHandler(uri.scheme); if (port != handler.defaultPort) realm += ":" + port; } } catch (e) { // bug 159484 - disallow url types that don't support a hostPort. // (although we handle "javascript:..." as a special case above.) this.log("Couldn't parse origin for " + uriString); realm = null; } return realm; }, _getActionOrigin : function (form) { var uriString = form.action; // A blank or mission action submits to where it came from. if (uriString == "") uriString = form.baseURI; // ala bug 297761 return this._getPasswordOrigin(uriString, true); }, /* * _fillDocument * * Called when a page has loaded. For each form in the document, * we check to see if it can be filled with a stored login. */ _fillDocument : function (doc) { var forms = doc.forms; if (!forms || forms.length == 0) return; var formOrigin = this._getPasswordOrigin(doc.documentURI); // If there are no logins for this site, bail out now. if (!this.countLogins(formOrigin, "", null)) return; this.log("fillDocument processing " + forms.length + " forms on " + doc.documentURI); var autofillForm = this._prefBranch.getBoolPref("autofillForms"); var previousActionOrigin = null; for (var i = 0; i < forms.length; i++) { var form = forms[i]; // Heuristically determine what the user/pass fields are // We do this before checking to see if logins are stored, // so that the user isn't prompted for a master password // without need. var [usernameField, passwordField, ignored] = this._getFormFields(form, false); // Need a valid password field to do anything. if (passwordField == null) continue; // Only the actionOrigin might be changing, so if it's the same // as the last form on the page we can reuse the same logins. var actionOrigin = this._getActionOrigin(form); if (actionOrigin != previousActionOrigin) { var foundLogins = this.findLogins({}, formOrigin, actionOrigin, null); this.log("form[" + i + "]: found " + foundLogins.length + " matching logins."); previousActionOrigin = actionOrigin; } else { this.log("form[" + i + "]: reusing logins from last form."); } // Discard logins which have username/password values that don't // fit into the fields (as specified by the maxlength attribute). // The user couldn't enter these values anyway, and it helps // with sites that have an extra PIN to be entered (bug 391514) var maxUsernameLen = Number.MAX_VALUE; var maxPasswordLen = Number.MAX_VALUE; // If attribute wasn't set, default is -1. if (usernameField && usernameField.maxLength >= 0) maxUsernameLen = usernameField.maxLength; if (passwordField.maxLength >= 0) maxPasswordLen = passwordField.maxLength; logins = foundLogins.filter(function (l) { var fit = (l.username.length <= maxUsernameLen && l.password.length <= maxPasswordLen); if (!fit) this.log("Ignored " + l.username + " login: won't fit"); return fit; }, this); // Nothing to do if we have no matching logins available. if (logins.length == 0) continue; // Attach autocomplete stuff to the username field, if we have // one. This is normally used to select from multiple accounts, // but even with one account we should refill if the user edits. if (usernameField) this._attachToInput(usernameField); // If the form has an autocomplete=off attribute in play, don't // fill in the login automatically. We check this after attaching // the autocomplete stuff to the username field, so the user can // still manually select a login to be filled in. var isFormDisabled = false; if (this._isAutocompleteDisabled(form) || this._isAutocompleteDisabled(usernameField) || this._isAutocompleteDisabled(passwordField)) { isFormDisabled = true; this.log("form[" + i + "]: not filled, has autocomplete=off"); } if (autofillForm && !isFormDisabled) { if (usernameField && usernameField.value) { // If username was specified in the form, only fill in the // password if we find a matching login. var username = usernameField.value; var matchingLogin; var found = logins.some(function(l) { matchingLogin = l; return (l.username == username); }); if (found) passwordField.value = matchingLogin.password; else this.log("Password not filled. None of the stored " + "logins match the username already present."); } else if (usernameField && logins.length == 2) { // Special case, for sites which have a normal user+pass // login *and* a password-only login (eg, a PIN)... // When we have a username field and 1 of 2 available // logins is password-only, go ahead and prefill the // one with a username. if (!logins[0].username && logins[1].username) { usernameField.value = logins[1].username; passwordField.value = logins[1].password; } else if (!logins[1].username && logins[0].username) { usernameField.value = logins[0].username; passwordField.value = logins[0].password; } } else if (logins.length == 1) { if (usernameField) usernameField.value = logins[0].username; passwordField.value = logins[0].password; } else { this.log("Multiple logins for form, so not filling any."); } } } // foreach form }, /* * _attachToInput * * Hooks up autocomplete support to a username field, to allow * a user editing the field to select an existing login and have * the password field filled in. */ _attachToInput : function (element) { this.log("attaching autocomplete stuff"); element.addEventListener("blur", this._domEventListener, false); element.addEventListener("DOMAutoComplete", this._domEventListener, false); this._formFillService.markAsLoginManagerField(element); }, /* * _fillPassword * * The user has autocompleted a username field, so fill in the password. */ _fillPassword : function (usernameField) { this.log("fillPassword autocomplete username: " + usernameField.value); var form = usernameField.form; var doc = form.ownerDocument; var hostname = this._getPasswordOrigin(doc.documentURI); var formSubmitURL = this._getActionOrigin(form) // Find the password field. We should always have at least one, // or else something has gone rather wrong. var pwFields = this._getPasswordFields(form, false); if (!pwFields) { const err = "No password field for autocomplete password fill."; // We want to know about this even if debugging is disabled. if (!this._debug) dump(err); else this.log(err); return; } // If there are multiple passwords fields, we can't really figure // out what each field is for, so just fill out the last field. var passwordField = pwFields[0].element; // Temporary LoginInfo with the info we know. var currentLogin = new this._nsLoginInfo(); currentLogin.init(hostname, formSubmitURL, null, usernameField.value, null, usernameField.name, passwordField.name); // Look for a existing login and use its password. var match = null; var logins = this.findLogins({}, hostname, formSubmitURL, null); if (!logins.some(function(l) { match = l; return currentLogin.matches(l, true); })) { this.log("Can't find a login for this autocomplete result."); return; } this.log("Found a matching login, filling in password."); passwordField.value = match.password; } }; // end of LoginManager implementation // nsIAutoCompleteResult implementation function UserAutoCompleteResult (aSearchString, matchingLogins) { function loginSort(a,b) { var userA = a.username.toLowerCase(); var userB = b.username.toLowerCase(); if (userA < userB) return -1; if (userB > userA) return 1; return 0; }; this.searchString = aSearchString; this.logins = matchingLogins.sort(loginSort); this.matchCount = matchingLogins.length; if (this.matchCount > 0) { this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS; this.defaultIndex = 0; } } UserAutoCompleteResult.prototype = { QueryInterface : XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult, Ci.nsISupportsWeakReference]), // private logins : null, // Interfaces from idl... searchString : null, searchResult : Ci.nsIAutoCompleteResult.RESULT_NOMATCH, defaultIndex : -1, errorDescription : "", matchCount : 0, getValueAt : function (index) { if (index < 0 || index >= this.logins.length) throw "Index out of range."; return this.logins[index].username; }, getCommentAt : function (index) { return ""; }, getStyleAt : function (index) { return ""; }, getImageAt : function (index) { return ""; }, removeValueAt : function (index, removeFromDB) { if (index < 0 || index >= this.logins.length) throw "Index out of range."; var [removedLogin] = this.logins.splice(index, 1); this.matchCount--; if (this.defaultIndex > this.logins.length) this.defaultIndex--; if (removeFromDB) { var pwmgr = Cc["@mozilla.org/login-manager;1"]. getService(Ci.nsILoginManager); pwmgr.removeLogin(removedLogin); } }, }; var component = [LoginManager]; function NSGetModule (compMgr, fileSpec) { return XPCOMUtils.generateModule(component); } |
:: Command execute :: | |
--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0312 ]-- |