259 lines, 155 LOC, 42 covered (27%)
2 | 1 | /* ***** BEGIN LICENSE BLOCK ***** |
2 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
|
3 | * |
|
4 | * The contents of this file are subject to the Mozilla Public License Version |
|
5 | * 1.1 (the "License"); you may not use this file except in compliance with |
|
6 | * the License. You may obtain a copy of the License at |
|
7 | * http://www.mozilla.org/MPL/ |
|
8 | * |
|
9 | * Software distributed under the License is distributed on an "AS IS" basis, |
|
10 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
|
11 | * for the specific language governing rights and limitations under the |
|
12 | * License. |
|
13 | * |
|
14 | * The Original Code is Bookmarks Sync. |
|
15 | * |
|
16 | * The Initial Developer of the Original Code is Mozilla. |
|
17 | * Portions created by the Initial Developer are Copyright (C) 2008 |
|
18 | * the Initial Developer. All Rights Reserved. |
|
19 | * |
|
20 | * Contributor(s): |
|
21 | * Justin Dolske <dolske@mozilla.com> |
|
22 | * Anant Narayanan <anant@kix.in> |
|
23 | * |
|
24 | * Alternatively, the contents of this file may be used under the terms of |
|
25 | * either the GNU General Public License Version 2 or later (the "GPL"), or |
|
26 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
|
27 | * in which case the provisions of the GPL or the LGPL are applicable instead |
|
28 | * of those above. If you wish to allow use of your version of this file only |
|
29 | * under the terms of either the GPL or the LGPL, and not to allow others to |
|
30 | * use your version of this file under the terms of the MPL, indicate your |
|
31 | * decision by deleting the provisions above and replace them with the notice |
|
32 | * and other provisions required by the GPL or the LGPL. If you do not delete |
|
33 | * the provisions above, a recipient may use your version of this file under |
|
34 | * the terms of any one of the MPL, the GPL or the LGPL. |
|
35 | * |
|
36 | * ***** END LICENSE BLOCK ***** */ |
|
37 | ||
14 | 38 | const EXPORTED_SYMBOLS = ['PasswordEngine']; |
39 | ||
8 | 40 | const Cu = Components.utils; |
8 | 41 | const Cc = Components.classes; |
8 | 42 | const Ci = Components.interfaces; |
43 | ||
10 | 44 | Cu.import("resource://weave/constants.js"); |
10 | 45 | Cu.import("resource://weave/util.js"); |
10 | 46 | Cu.import("resource://weave/engines.js"); |
10 | 47 | Cu.import("resource://weave/stores.js"); |
10 | 48 | Cu.import("resource://weave/trackers.js"); |
10 | 49 | Cu.import("resource://weave/base_records/collection.js"); |
10 | 50 | Cu.import("resource://weave/ext/Observers.js"); |
10 | 51 | Cu.import("resource://weave/type_records/passwords.js"); |
52 | ||
6 | 53 | function PasswordEngine() { |
14 | 54 | SyncEngine.call(this, "Passwords"); |
55 | } |
|
4 | 56 | PasswordEngine.prototype = { |
6 | 57 | __proto__: SyncEngine.prototype, |
4 | 58 | _storeObj: PasswordStore, |
4 | 59 | _trackerObj: PasswordTracker, |
4 | 60 | _recordObj: LoginRec, |
61 | ||
4 | 62 | _syncFinish: function _syncFinish() { |
63 | SyncEngine.prototype._syncFinish.call(this); |
|
64 | ||
65 | // Delete the weave credentials from the server once |
|
66 | if (!Svc.Prefs.get("deletePwd", false)) { |
|
67 | try { |
|
68 | let ids = Svc.Login.findLogins({}, PWDMGR_HOST, "", "").map(function(info) |
|
69 | info.QueryInterface(Components.interfaces.nsILoginMetaInfo).guid); |
|
70 | let coll = new Collection(this.engineURL); |
|
71 | coll.ids = ids; |
|
72 | let ret = coll.delete(); |
|
73 | this._log.debug("Delete result: " + ret); |
|
74 | ||
75 | Svc.Prefs.set("deletePwd", true); |
|
76 | } |
|
77 | catch(ex) { |
|
78 | this._log.debug("Password deletes failed: " + Utils.exceptionStr(ex)); |
|
79 | } |
|
80 | } |
|
81 | }, |
|
82 | ||
10 | 83 | _findDupe: function _findDupe(item) { |
84 | let login = this._store._nsLoginInfoFromRecord(item); |
|
85 | let logins = Svc.Login.findLogins({}, login.hostname, login.formSubmitURL, |
|
86 | login.httpRealm); |
|
87 | ||
88 | // Look for existing logins that match the hostname but ignore the password |
|
89 | for each (let local in logins) |
|
90 | if (login.matches(local, true) && local instanceof Ci.nsILoginMetaInfo) |
|
91 | return local.guid; |
|
92 | } |
|
93 | }; |
|
94 | ||
4 | 95 | function PasswordStore(name) { |
96 | Store.call(this, name); |
|
97 | this._nsLoginInfo = new Components.Constructor( |
|
98 | "@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init"); |
|
99 | } |
|
4 | 100 | PasswordStore.prototype = { |
6 | 101 | __proto__: Store.prototype, |
102 | ||
4 | 103 | _nsLoginInfoFromRecord: function PasswordStore__nsLoginInfoRec(record) { |
104 | let info = new this._nsLoginInfo(record.hostname, |
|
105 | record.formSubmitURL, |
|
106 | record.httpRealm, |
|
107 | record.username, |
|
108 | record.password, |
|
109 | record.usernameField, |
|
110 | record.passwordField); |
|
111 | info.QueryInterface(Ci.nsILoginMetaInfo); |
|
112 | info.guid = record.id; |
|
113 | return info; |
|
114 | }, |
|
115 | ||
4 | 116 | _getLoginFromGUID: function PasswordStore__getLoginFromGUID(id) { |
117 | let prop = Cc["@mozilla.org/hash-property-bag;1"]. |
|
118 | createInstance(Ci.nsIWritablePropertyBag2); |
|
119 | prop.setPropertyAsAUTF8String("guid", id); |
|
120 | ||
121 | let logins = Svc.Login.searchLogins({}, prop); |
|
122 | if (logins.length > 0) { |
|
123 | this._log.trace(logins.length + " items matching " + id + " found."); |
|
124 | return logins[0]; |
|
125 | } else { |
|
126 | this._log.trace("No items matching " + id + " found. Ignoring"); |
|
127 | } |
|
128 | return false; |
|
129 | }, |
|
130 | ||
4 | 131 | getAllIDs: function PasswordStore__getAllIDs() { |
132 | let items = {}; |
|
133 | let logins = Svc.Login.getAllLogins({}); |
|
134 | ||
135 | for (let i = 0; i < logins.length; i++) { |
|
136 | // Skip over Weave password/passphrase entries |
|
137 | let metaInfo = logins[i].QueryInterface(Ci.nsILoginMetaInfo); |
|
138 | if (metaInfo.hostname == PWDMGR_HOST) |
|
139 | continue; |
|
140 | ||
141 | items[metaInfo.guid] = metaInfo; |
|
142 | } |
|
143 | ||
144 | return items; |
|
145 | }, |
|
146 | ||
4 | 147 | changeItemID: function PasswordStore__changeItemID(oldID, newID) { |
148 | this._log.trace("Changing item ID: " + oldID + " to " + newID); |
|
149 | ||
150 | let oldLogin = this._getLoginFromGUID(oldID); |
|
151 | if (!oldLogin) { |
|
152 | this._log.trace("Can't change item ID: item doesn't exist"); |
|
153 | return; |
|
154 | } |
|
155 | if (this._getLoginFromGUID(newID)) { |
|
156 | this._log.trace("Can't change item ID: new ID already in use"); |
|
157 | return; |
|
158 | } |
|
159 | ||
160 | let prop = Cc["@mozilla.org/hash-property-bag;1"]. |
|
161 | createInstance(Ci.nsIWritablePropertyBag2); |
|
162 | prop.setPropertyAsAUTF8String("guid", newID); |
|
163 | ||
164 | Svc.Login.modifyLogin(oldLogin, prop); |
|
165 | }, |
|
166 | ||
4 | 167 | itemExists: function PasswordStore__itemExists(id) { |
168 | if (this._getLoginFromGUID(id)) |
|
169 | return true; |
|
170 | return false; |
|
171 | }, |
|
172 | ||
4 | 173 | createRecord: function createRecord(guid) { |
174 | let record = new LoginRec(); |
|
175 | let login = this._getLoginFromGUID(guid); |
|
176 | ||
177 | if (login) { |
|
178 | record.hostname = login.hostname; |
|
179 | record.formSubmitURL = login.formSubmitURL; |
|
180 | record.httpRealm = login.httpRealm; |
|
181 | record.username = login.username; |
|
182 | record.password = login.password; |
|
183 | record.usernameField = login.usernameField; |
|
184 | record.passwordField = login.passwordField; |
|
185 | } |
|
186 | else |
|
187 | record.deleted = true; |
|
188 | return record; |
|
189 | }, |
|
190 | ||
4 | 191 | create: function PasswordStore__create(record) { |
192 | this._log.debug("Adding login for " + record.hostname); |
|
193 | Svc.Login.addLogin(this._nsLoginInfoFromRecord(record)); |
|
194 | }, |
|
195 | ||
4 | 196 | remove: function PasswordStore__remove(record) { |
197 | this._log.trace("Removing login " + record.id); |
|
198 | ||
199 | let loginItem = this._getLoginFromGUID(record.id); |
|
200 | if (!loginItem) { |
|
201 | this._log.trace("Asked to remove record that doesn't exist, ignoring"); |
|
202 | return; |
|
203 | } |
|
204 | ||
205 | Svc.Login.removeLogin(loginItem); |
|
206 | }, |
|
207 | ||
4 | 208 | update: function PasswordStore__update(record) { |
209 | let loginItem = this._getLoginFromGUID(record.id); |
|
210 | if (!loginItem) { |
|
211 | this._log.debug("Skipping update for unknown item: " + record.hostname); |
|
212 | return; |
|
213 | } |
|
214 | ||
215 | this._log.debug("Updating " + record.hostname); |
|
216 | let newinfo = this._nsLoginInfoFromRecord(record); |
|
217 | Svc.Login.modifyLogin(loginItem, newinfo); |
|
218 | }, |
|
219 | ||
10 | 220 | wipe: function PasswordStore_wipe() { |
221 | Svc.Login.removeAllLogins(); |
|
222 | } |
|
223 | }; |
|
224 | ||
6 | 225 | function PasswordTracker(name) { |
12 | 226 | Tracker.call(this, name); |
14 | 227 | Observers.add("passwordmgr-storage-changed", this); |
228 | } |
|
4 | 229 | PasswordTracker.prototype = { |
6 | 230 | __proto__: Tracker.prototype, |
231 | ||
232 | /* A single add, remove or change is 15 points, all items removed is 50 */ |
|
10 | 233 | observe: function PasswordTracker_observe(aSubject, aTopic, aData) { |
234 | if (this.ignoreAll) |
|
235 | return; |
|
236 | ||
237 | switch (aData) { |
|
238 | case 'modifyLogin': |
|
239 | aSubject = aSubject.QueryInterface(Ci.nsIArray). |
|
240 | queryElementAt(1, Ci.nsILoginMetaInfo); |
|
241 | case 'addLogin': |
|
242 | case 'removeLogin': |
|
243 | // Skip over Weave password/passphrase changes |
|
244 | aSubject.QueryInterface(Ci.nsILoginMetaInfo). |
|
245 | QueryInterface(Ci.nsILoginInfo); |
|
246 | if (aSubject.hostname == PWDMGR_HOST) |
|
247 | break; |
|
248 | ||
249 | this.score += 15; |
|
250 | this._log.trace(aData + ": " + aSubject.guid); |
|
251 | this.addChangedID(aSubject.guid); |
|
252 | break; |
|
253 | case 'removeAllLogins': |
|
254 | this._log.trace(aData); |
|
255 | this.score += 500; |
|
256 | break; |
|
257 | } |
|
258 | } |
|
2 | 259 | }; |