gecko/services/sync/modules/engines/passwords.js
Edward Lee 6ddbaa9a8e Bug 506795 - Handle adding duplicate logins. r=thunder
Refactor reconcile to call findLikeId that by default will look for recordLike in the outgoing queue as it does now. Override findLikeId for password engine to search local logins.
2009-07-28 10:06:02 -07:00

251 lines
7.9 KiB
JavaScript

/* ***** 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 Bookmarks Sync.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Dolske <dolske@mozilla.com>
* Anant Narayanan <anant@kix.in>
*
* 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 EXPORTED_SYMBOLS = ['PasswordEngine'];
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
Cu.import("resource://weave/log4moz.js");
Cu.import("resource://weave/util.js");
Cu.import("resource://weave/engines.js");
Cu.import("resource://weave/stores.js");
Cu.import("resource://weave/trackers.js");
Cu.import("resource://weave/ext/Observers.js");
Cu.import("resource://weave/type_records/passwords.js");
function PasswordEngine() {
this._init();
}
PasswordEngine.prototype = {
__proto__: SyncEngine.prototype,
name: "passwords",
displayName: "Passwords",
logName: "Passwords",
_storeObj: PasswordStore,
_trackerObj: PasswordTracker,
_recordObj: LoginRec,
_findLikeId: function PasswordEngine__findLikeId(item) {
let login = this._store._nsLoginInfoFromRecord(item);
let logins = Svc.Login.findLogins({}, login.hostname, login.formSubmitURL,
login.httpRealm);
// Look for existing logins that match the hostname but ignore the password
for each (let local in logins)
if (login.matches(local, true) && local instanceof Ci.nsILoginMetaInfo)
return local.guid;
}
};
function PasswordStore() {
this._init();
}
PasswordStore.prototype = {
__proto__: Store.prototype,
name: "passwords",
_logName: "PasswordStore",
_nsLoginInfo: null,
_init: function PasswordStore_init() {
Store.prototype._init.call(this);
this._nsLoginInfo = new Components.Constructor(
"@mozilla.org/login-manager/loginInfo;1",
Ci.nsILoginInfo,
"init"
);
},
_nsLoginInfoFromRecord: function PasswordStore__nsLoginInfoRec(record) {
let info = new this._nsLoginInfo(record.hostname,
record.formSubmitURL,
record.httpRealm,
record.username,
record.password,
record.usernameField,
record.passwordField);
info.QueryInterface(Ci.nsILoginMetaInfo);
info.guid = record.id;
return info;
},
_getLoginFromGUID: function PasswordStore__getLoginFromGUID(id) {
let prop = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag2);
prop.setPropertyAsAUTF8String("guid", id);
let logins = Svc.Login.searchLogins({}, prop);
if (logins.length > 0) {
this._log.debug(logins.length + " items matching " + id + " found.");
return logins[0];
} else {
this._log.trace("No items matching " + id + " found. Ignoring");
}
return false;
},
getAllIDs: function PasswordStore__getAllIDs() {
let items = {};
let logins = Svc.Login.getAllLogins({});
for (let i = 0; i < logins.length; i++) {
let metaInfo = logins[i].QueryInterface(Ci.nsILoginMetaInfo);
items[metaInfo.guid] = metaInfo;
}
return items;
},
changeItemID: function PasswordStore__changeItemID(oldID, newID) {
this._log.debug("Changing item ID: " + oldID + " to " + newID);
let oldLogin = this._getLoginFromGUID(oldID);
if (!oldLogin) {
this._log.trace("Can't change item ID: item doesn't exist");
return;
}
if (this._getLoginFromGUID(newID)) {
this._log.trace("Can't change item ID: new ID already in use");
return;
}
let prop = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag2);
prop.setPropertyAsAUTF8String("guid", newID);
Svc.Login.modifyLogin(oldLogin, prop);
},
itemExists: function PasswordStore__itemExists(id) {
if (this._getLoginFromGUID(id))
return true;
return false;
},
createRecord: function PasswordStore__createRecord(guid, cryptoMetaURL) {
let record = new LoginRec();
let login = this._getLoginFromGUID(guid);
record.id = guid;
if (login) {
record.encryption = cryptoMetaURL;
record.hostname = login.hostname;
record.formSubmitURL = login.formSubmitURL;
record.httpRealm = login.httpRealm;
record.username = login.username;
record.password = login.password;
record.usernameField = login.usernameField;
record.passwordField = login.passwordField;
}
else
record.deleted = true;
return record;
},
create: function PasswordStore__create(record) {
this._log.debug("Adding login for " + record.hostname);
Svc.Login.addLogin(this._nsLoginInfoFromRecord(record));
},
remove: function PasswordStore__remove(record) {
this._log.debug("Removing login " + record.id);
let loginItem = this._getLoginFromGUID(record.id);
if (!loginItem) {
this._log.debug("Asked to remove record that doesn't exist, ignoring");
return;
}
Svc.Login.removeLogin(loginItem);
},
update: function PasswordStore__update(record) {
let loginItem = this._getLoginFromGUID(record.id);
if (!loginItem) {
this._log.debug("Skipping update for unknown item: " + record.id);
return;
}
this._log.debug("Updating " + record.id);
let newinfo = this._nsLoginInfoFromRecord(record);
Svc.Login.modifyLogin(loginItem, newinfo);
},
wipe: function PasswordStore_wipe() {
Svc.Login.removeAllLogins();
}
};
function PasswordTracker() {
this._init();
}
PasswordTracker.prototype = {
__proto__: Tracker.prototype,
_logName: "PasswordTracker",
name: "passwords",
file: "password",
_init: function PasswordTracker_init() {
Tracker.prototype._init.call(this);
Observers.add("passwordmgr-storage-changed", this);
},
/* A single add, remove or change is 15 points, all items removed is 50 */
observe: function PasswordTracker_observe(aSubject, aTopic, aData) {
if (this.ignoreAll)
return;
switch (aData) {
case 'modifyLogin':
aSubject = aSubject.QueryInterface(Ci.nsIArray).
queryElementAt(1, Ci.nsILoginMetaInfo);
case 'addLogin':
case 'removeLogin':
aSubject.QueryInterface(Ci.nsILoginMetaInfo);
this._score += 15;
this._log.debug(aData + ": " + aSubject.guid);
this.addChangedID(aSubject.guid);
break;
case 'removeAllLogins':
this._log.debug(aData);
this._score += 50;
break;
}
}
};