From b5847e6adf7798ff5f18ece57ab3717e51005c73 Mon Sep 17 00:00:00 2001 From: Anant Narayanan Date: Mon, 4 Aug 2008 17:23:23 -0700 Subject: [PATCH 1/4] Asynchronous bookmark sharing (bug 449113, r=thunder) --- services/sync/modules/dav.js | 8 ++++++ services/sync/modules/engines/bookmarks.js | 33 +++++++++++++++++++++- services/sync/modules/sharing.js | 29 +++++++++++++++---- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/services/sync/modules/dav.js b/services/sync/modules/dav.js index ceb2f2860fed..bec181c789c2 100644 --- a/services/sync/modules/dav.js +++ b/services/sync/modules/dav.js @@ -253,6 +253,14 @@ DAVCollection.prototype = { return this._makeRequest.async(this, onComplete, "POST", path, this._defaultHeaders, data); }, + + formPost: function DC_formPOST(path, data, onComplete) { + let headers = {'Content-type': 'application/x-www-form-urlencoded'}; + headers.__proto__ = this._defaultHeaders; + + return this._makeRequest.async(this, onComplete, "POST", path, + headers, data); + }, PUT: function DC_PUT(path, data, onComplete) { return this._makeRequest.async(this, onComplete, "PUT", path, diff --git a/services/sync/modules/engines/bookmarks.js b/services/sync/modules/engines/bookmarks.js index 623de6d9d0a8..a9733873b2fb 100644 --- a/services/sync/modules/engines/bookmarks.js +++ b/services/sync/modules/engines/bookmarks.js @@ -20,6 +20,7 @@ * Contributor(s): * Dan Mills * Jono DiCarlo + * Anant Narayanan * * 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 @@ -290,6 +291,34 @@ BookmarksSharingManager.prototype = { self.done( true ); }, + /* FIXME! Gets all shares, not just the new ones. Doesn't impact + functionality because _incomingShareOffer does not create + duplicates, but annoys the user by showing notification of ALL + shares on EVERY sync :( + */ + getNewShares: function BmkSharing_getNewShares(onComplete) { + this._getNewShares.async(this, onComplete); + }, + _getNewShares: function BmkSharing__getNewShares() { + let self = yield; + + let sharingApi = new Sharing.Api( DAV ); + let result = yield sharingApi.getShares(self.cb); + + this._log.info("Got Shares: " + result); + let shares = result.split(','); + if (shares.length > 1) { + this._log.info('Found shares'); + for (var i = 0; i < shares.length - 1; i++) { + let share = shares[i].split(':'); + let name = share[0]; + let user = share[1]; + let path = share[2]; + this._incomingShareOffer(user, '/user/' + user + '/' + path, name); + } + } + }, + updateAllIncomingShares: function BmkSharing_updateAllIncoming(onComplete) { this._updateAllIncomingShares.async(this, onComplete); }, @@ -436,8 +465,9 @@ BookmarksSharingManager.prototype = { // Call Atul's js api for setting htaccess: let sharingApi = new Sharing.Api( DAV ); let result = yield sharingApi.shareWithUsers( serverPath, - [username], + [username], folderName, self.cb ); + this._log.info(result.errorText); // return the server path: self.done( serverPath ); }, @@ -725,6 +755,7 @@ BookmarksEngine.prototype = { /* After syncing the regular bookmark folder contents, * also update both the incoming and outgoing shared folders. */ let self = yield; + let ret = yield this._sharing.getNewShares(self.cb); this.__proto__.__proto__._sync.async(this, self.cb ); yield; this._sharing.updateAllOutgoingShares(self.cb); diff --git a/services/sync/modules/sharing.js b/services/sync/modules/sharing.js index 794febc5d8c8..787deff99a64 100644 --- a/services/sync/modules/sharing.js +++ b/services/sync/modules/sharing.js @@ -19,6 +19,7 @@ * * Contributor(s): * Atul Varma + * Anant Narayanan * * 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 @@ -50,14 +51,29 @@ function Api(dav) { } Api.prototype = { - shareWithUsers: function Api_shareWithUsers(path, users, onComplete) { - this._shareGenerator.async(this, + shareWithUsers: function Api_shareWithUsers(path, users, folder, onComplete) { + return this._shareGenerator.async(this, onComplete, path, - users); + users, folder); }, - _shareGenerator: function Api__shareGenerator(path, users) { + getShares: function Api_getShares(onComplete) { + return this._getShareGenerator.async(this, onComplete); + }, + + _getShareGenerator: function Api__getShareGenerator() { + let self = yield; + let id = ID.get(this._dav.identity); + + this._dav.formPost("/api/share/get.php", ("uid=" + escape(id.username) + + "&password=" + escape(id.password)), + self.cb); + let xhr = yield; + self.done(xhr.responseText); + }, + + _shareGenerator: function Api__shareGenerator(path, users, folder) { let self = yield; let id = ID.get(this._dav.identity); @@ -67,10 +83,11 @@ Api.prototype = { let jsonSvc = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); let json = jsonSvc.encode(cmd); - this._dav.POST("/api/share/", + this._dav.formPost("/api/share/", ("cmd=" + escape(json) + "&uid=" + escape(id.username) + - "&password=" + escape(id.password)), + "&password=" + escape(id.password) + + "&name=" + escape(folder)), self.cb); let xhr = yield; From f02be430795107c5cfec13e968d359d466eb8760 Mon Sep 17 00:00:00 2001 From: Anant Narayanan Date: Mon, 4 Aug 2008 17:34:21 -0700 Subject: [PATCH 2/4] Client-side OAuth support (bug 444528, r=thunder) --- services/sync/locales/en-US/oauth.dtd | 18 +++ services/sync/locales/en-US/oauth.properties | 10 ++ services/sync/modules/oauth.js | 156 +++++++++++++++++++ services/sync/modules/remote.js | 24 ++- services/sync/modules/service.js | 5 + 5 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 services/sync/locales/en-US/oauth.dtd create mode 100644 services/sync/locales/en-US/oauth.properties create mode 100644 services/sync/modules/oauth.js diff --git a/services/sync/locales/en-US/oauth.dtd b/services/sync/locales/en-US/oauth.dtd new file mode 100644 index 000000000000..87b9843cc37a --- /dev/null +++ b/services/sync/locales/en-US/oauth.dtd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/sync/locales/en-US/oauth.properties b/services/sync/locales/en-US/oauth.properties new file mode 100644 index 000000000000..bb2f02fb718c --- /dev/null +++ b/services/sync/locales/en-US/oauth.properties @@ -0,0 +1,10 @@ +intro.uidmsg = You are logged in as %S +conf.conmsg = A third party identifying itself as %S is requesting access to your Weave Data. +conf.error = Sorry, but the third party's request was invalid: %S +conf.error1 = a request token was not issued. +conf.error2 = the third party is unregistered. +conf.error3 = the request token has expired. +conf.error4 = your account details could not be verified. +final.step1 = Unwrapping symmetric key... +final.step2 = Adding third party to your keyring... +final.step3 = Done! \ No newline at end of file diff --git a/services/sync/modules/oauth.js b/services/sync/modules/oauth.js new file mode 100644 index 000000000000..8621f1ffb2bf --- /dev/null +++ b/services/sync/modules/oauth.js @@ -0,0 +1,156 @@ +/* ***** 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): + * Anant Narayanan + * + * 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 = ['OAuth']; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://weave/log4moz.js"); +Cu.import("resource://weave/constants.js"); +Cu.import("resource://weave/util.js"); +Cu.import("resource://weave/async.js"); +Cu.import("resource://weave/identity.js"); +Cu.import("resource://weave/engines.js"); + +Function.prototype.async = Async.sugar; + +Utils.lazy(this, 'OAuth', OAuthSvc); + +function OAuthSvc() { + this._init(); +} +OAuthSvc.prototype = { + _logName: "OAuth", + _keyring: null, + _consKey: null, + _bulkID: null, + _rsaKey: null, + _token: null, + _cback: null, + _uid: null, + _pwd: null, + _pas: null, + _cb1: null, + _cb2: null, + + _init: function OAuth__init() { + this._log = Log4Moz.Service.getLogger("Service." + this._logName); + this._log.level = "Debug"; + this._log.info("OAuth Module Initialized"); + }, + + setToken: function OAuth_setToken(token, cback) { + this._token = token; + this._cback = cback; + }, + + setUser: function OAuth_setUser(username, password, passphrase) { + this._uid = username; + this._pwd = password; + this._pas = passphrase; + }, + + validate: function OAuth_getName(obj, cb) { + if (!this._token || !this._uid || !this._pwd) + cb(obj, false); + + var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(Ci.nsIXMLHttpRequest); + var key = btoa(this._uid + ":" + this._pwd); + + req.onreadystatechange = function(e) { + if (req.readyState == 4) { + if (req.status == 200) { + var fields = req.responseText.split(','); + cb(obj, fields[0], fields[1], fields[2]); + } else { + cb(obj, req.responseText); + } + } + }; + req.open('GET', 'https://services.mozilla.com/api/oauth/info.php?token=' + this._token + '&key=' + key); + req.send(null); + }, + + finalize: function OAuth_finalize(cb1, cb2, bundle) { + this._cb1 = cb1; + this._cb2 = cb2; + this._bundle = bundle; + + var bmkEngine = Engines.get('bookmarks'); + var bmkRstore = bmkEngine._remote; + + this._keyring = bmkRstore.keys; + this._keyring.getKeyAndIV(Utils.bind2(this, this._gotBulkKey), ID.get('WeaveID')); + }, + + _gotBulkKey: function OAuth_gotBulkKey() { + let consID = new Identity(); + consID.pubkey = this._rsaKey; + consID.username = this._consKey; + this._cb1(this._bundle); + this._log.info("Updating keyring for 3rd party access"); + this._keyring.setKey(Utils.bind2(this, this._done), ID.get('WeaveID'), consID); + }, + + _done: function OAuth__done() { + var cb = this._cb2; + var bu = this._bundle; + + if (!this._token || !this._uid || !this._pwd) + cb(this._bundle, false); + + var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(Ci.nsIXMLHttpRequest); + var key = btoa(this._uid + ":" + this._pwd); + + req.onreadystatechange = function(e) { + if (req.readyState == 4) { + if (req.status == 200 && req.responseText == "1") { + cb(bu, true); + } else { + cb(bu, false); + } + } + }; + req.open('GET', 'https://services.mozilla.com/api/oauth/update.php?token=' + this._token + '&key=' + key); + req.send(null); + } +}; diff --git a/services/sync/modules/remote.js b/services/sync/modules/remote.js index 81c43d079f8c..f06f16189bc5 100644 --- a/services/sync/modules/remote.js +++ b/services/sync/modules/remote.js @@ -418,10 +418,32 @@ Keychain.prototype = { identity.bulkKey = symkey; identity.bulkIV = iv; }, + _setKey: function KeyChain__setKey(bulkID, newID) { + /* FIXME!: It's possible that the keyring is changed on the server + after we do a GET. Then we're just uploading this new local keyring, + thereby losing any changes made on the server keyring since this GET. + + Also, if this.data was not instantiated properly (i.e. you're + using KeyChain directly instead of getting it from the engine), + you run the risk of wiping the server-side keychain. + */ + let self = yield; + + this.get(self.cb); + yield; + + let wrappedKey = yield Crypto.wrapKey.async(Crypto, self.cb, + bulkID.bulkKey, newID); + this.data.ring[newID.username] = wrappedKey; + this.put(self.cb, this.data); + yield; + }, getKeyAndIV: function Keychain_getKeyAndIV(onComplete, identity) { this._getKeyAndIV.async(this, onComplete, identity); + }, + setKey: function Keychain_setKey(onComplete, bulkID, newID) { + this._setKey.async(this, onComplete, bulkID, newID); } - // FIXME: implement setKey() }; function RemoteStore(engine) { diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index 63645dc0dea0..b2eee3a1e105 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -72,6 +72,7 @@ Cu.import("resource://weave/wrap.js"); Cu.import("resource://weave/faultTolerance.js"); Cu.import("resource://weave/crypto.js"); Cu.import("resource://weave/engines.js"); +Cu.import("resource://weave/oauth.js"); Cu.import("resource://weave/dav.js"); Cu.import("resource://weave/identity.js"); Cu.import("resource://weave/async.js"); @@ -99,6 +100,7 @@ Cu.import("resource://weave/dav.js", Weave); Cu.import("resource://weave/stores.js", Weave); Cu.import("resource://weave/syncCores.js", Weave); Cu.import("resource://weave/engines.js", Weave); +Cu.import("resource://weave/oauth.js", Weave); Cu.import("resource://weave/service.js", Weave); Cu.import("resource://weave/engines/cookies.js", Weave); Cu.import("resource://weave/engines/passwords.js", Weave); @@ -550,6 +552,9 @@ WeaveSvc.prototype = { this._log.debug("Verifying passphrase"); + this.username = username; + ID.get('WeaveID').setTempPassword(password); + let id = new Identity('Passphrase Verification', username); id.setTempPassword(passphrase); From 3670146b3a305ccee275009c59c8c1281b2fa939 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Wed, 6 Aug 2008 14:51:41 -0700 Subject: [PATCH 3/4] Fix a typo that could cause an error in appendDelta() --- services/sync/modules/remote.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/sync/modules/remote.js b/services/sync/modules/remote.js index f06f16189bc5..1e56cc40d137 100644 --- a/services/sync/modules/remote.js +++ b/services/sync/modules/remote.js @@ -677,7 +677,7 @@ RemoteStore.prototype = { if ((id - this.status.data.snapVersion) > KEEP_DELTAS) { this._os.notifyObservers(null, "weave:service:sync:status", "status.uploading-snapshot"); - yield this.snapshot.put(self.cb, snapshot.data); + yield this._snapshot.put(self.cb, snapshot.data); this.status.data.snapVersion = id; } From 201ede3f4dfccb5c8a8897aea7ef3da9aa2f6823 Mon Sep 17 00:00:00 2001 From: Anant Narayanan Date: Thu, 7 Aug 2008 20:00:35 -0700 Subject: [PATCH 4/4] Syncing for Awesome bar (bug 437133, r=thunder) --- services/sync/locales/en-US/preferences.dtd | 1 + services/sync/modules/engines/history.js | 51 +++- services/sync/modules/engines/input.js | 295 ++++++++++++++++++++ services/sync/modules/service.js | 2 + services/sync/services-sync.js | 1 + 5 files changed, 346 insertions(+), 4 deletions(-) create mode 100644 services/sync/modules/engines/input.js diff --git a/services/sync/locales/en-US/preferences.dtd b/services/sync/locales/en-US/preferences.dtd index 3634efa0ae76..ea5a87ad2c4e 100644 --- a/services/sync/locales/en-US/preferences.dtd +++ b/services/sync/locales/en-US/preferences.dtd @@ -37,6 +37,7 @@ + diff --git a/services/sync/modules/engines/history.js b/services/sync/modules/engines/history.js index b611192f4fd0..4004f2103a11 100644 --- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -121,6 +121,7 @@ function HistoryStore() { } HistoryStore.prototype = { _logName: "HistStore", + _lookup: null, __hsvc: null, get _hsvc() { @@ -133,6 +134,20 @@ HistoryStore.prototype = { return this.__hsvc; }, + __histDB: null, + get _histDB() { + if (!this.__histDB) { + let file = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + file.append("places.sqlite"); + let stor = Cc["@mozilla.org/storage/service;1"]. + getService(Ci.mozIStorageService); + this.__histDB = stor.openDatabase(file); + } + return this.__histDB; + }, + _itemExists: function HistStore__itemExists(GUID) { // we don't care about already-existing items; just try to re-add them return false; @@ -142,8 +157,12 @@ HistoryStore.prototype = { this._log.debug(" -> creating history entry: " + command.GUID); try { let uri = Utils.makeURI(command.data.URI); + let redirect = false; + if (command.data.transition == 5 || command.data.transition == 6) + redirect = true; + this._hsvc.addVisit(uri, command.data.time, null, - this._hsvc.TRANSITION_TYPED, false, null); + command.data.transition, redirect, 0); this._hsvc.setPageTitle(uri, command.data.title); } catch (e) { this._log.error("Exception caught: " + (e.message? e.message : e)); @@ -178,20 +197,44 @@ HistoryStore.prototype = { return root; }, + /* UGLY, UGLY way of syncing visit type ! + We'll just have to wait for bug #320831 */ + _getVisitType: function HistStore__getVisitType(uri) { + let visitStmnt = this._histDB.createStatement("SELECT visit_type FROM moz_historyvisits WHERE place_id = ?1"); + let pidStmnt = this._histDB.createStatement("SELECT id FROM moz_places WHERE url = ?1"); + + pidStmnt.bindUTF8StringParameter(0, uri); + + let placeID = null; + if (pidStmnt.executeStep()) { + placeID = pidStmnt.getInt32(0); + } + + if (placeID) { + visitStmnt.bindInt32Parameter(0, placeID); + if (visitStmnt.executeStep()) + return visitStmnt.getInt32(0); + } + return null; + }, + wrap: function HistStore_wrap() { let root = this._historyRoot(); root.containerOpen = true; let items = {}; for (let i = 0; i < root.childCount; i++) { let item = root.getChild(i); - let guid = item.time + ":" + item.uri + let guid = item.time + ":" + item.uri; + let vType = this._getVisitType(item.uri); items[guid] = {parentGUID: '', title: item.title, URI: item.uri, - time: item.time + time: item.time, + transition: vType }; - // FIXME: sync transition type - requires FULL_VISITs } + + this._lookup = items; return items; }, diff --git a/services/sync/modules/engines/input.js b/services/sync/modules/engines/input.js new file mode 100644 index 000000000000..14d981445fe0 --- /dev/null +++ b/services/sync/modules/engines/input.js @@ -0,0 +1,295 @@ +/* ***** 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): + * Anant Narayanan + * + * 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 = ['InputEngine']; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://weave/log4moz.js"); +Cu.import("resource://weave/async.js"); +Cu.import("resource://weave/util.js"); +Cu.import("resource://weave/engines.js"); +Cu.import("resource://weave/syncCores.js"); +Cu.import("resource://weave/stores.js"); +Cu.import("resource://weave/trackers.js"); + +Function.prototype.async = Async.sugar; + +function InputEngine(pbeId) { + this._init(pbeId); +} +InputEngine.prototype = { + __proto__: new SyncEngine(), + + get name() { return "input"; }, + get displayName() { return "Input History"; }, + get logName() { return "InputEngine"; }, + get serverPrefix() { return "user-data/input/"; }, + + __store: null, + get _store() { + if (!this.__store) + this.__store = new InputStore(); + return this.__store; + }, + + __core: null, + get _core() { + if (!this.__core) + this.__core = new InputSyncCore(this._store); + return this.__core; + }, + + __tracker: null, + get _tracker() { + if (!this.__tracker) + this.__tracker = new InputTracker(); + return this.__tracker; + } +}; + +function InputSyncCore(store) { + this._store = store; + this._init(); +} +InputSyncCore.prototype = { + _logName: "InputSync", + _store: null, + + _commandLike: function FSC_commandLike(a, b) { + /* Not required as GUIDs for similar data sets will be the same */ + return false; + } +}; +InputSyncCore.prototype.__proto__ = new SyncCore(); + +function InputStore() { + this._init(); +} +InputStore.prototype = { + _logName: "InputStore", + _lookup: null, + + __placeDB: null, + get _placeDB() { + if (!this.__placeDB) { + let file = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + file.append("places.sqlite"); + let stor = Cc["@mozilla.org/storage/service;1"]. + getService(Ci.mozIStorageService); + this.__histDB = stor.openDatabase(file); + } + return this.__histDB; + }, + + _getIDfromURI: function InputStore__getIDfromURI(uri) { + let pidStmnt = this._placeDB.createStatement("SELECT id FROM moz_places WHERE url = ?1"); + pidStmnt.bindUTF8StringParameter(0, uri); + if (pidStmnt.executeStep()) + return pidStmnt.getInt32(0); + else + return null; + }, + + _getInputHistory: function InputStore__getInputHistory(id) { + let ipStmnt = this._placeDB.createStatement("SELECT input, use_count FROM moz_inputhistory WHERE place_id = ?1"); + ipStmnt.bindInt32Parameter(0, id); + + let input = []; + while (ipStmnt.executeStep()) { + let ip = ipStmnt.getUTF8String(0); + let cnt = ipStmnt.getInt32(1); + input[input.length] = {'input': ip, 'count': cnt}; + } + + return input; + }, + + _createCommand: function InputStore__createCommand(command) { + this._log.info("InputStore got createCommand: " + command); + + let placeID = this._getIDfromURI(command.GUID); + if (placeID) { + let createStmnt = this._placeDB.createStatement("INSERT INTO moz_inputhistory (?1, ?2, ?3)"); + createStmnt.bindInt32Parameter(0, placeID); + createStmnt.bindUTF8StringParameter(1, command.data.input); + createStmnt.bindInt32Parameter(2, command.data.count); + + createStmnt.execute(); + } + }, + + _removeCommand: function InputStore__removeCommand(command) { + this._log.info("InputStore got removeCommand: " + command); + + if (!(command.GUID in this._lookup)) { + this._log.warn("Invalid GUID found, ignoring remove request."); + return; + } + + let placeID = this._getIDfromURI(command.GUID); + let remStmnt = this._placeDB.createStatement("DELETE FROM moz_inputhistory WHERE place_id = ?1 AND input = ?2"); + + remStmnt.bindInt32Parameter(0, placeID); + remStmnt.bindUTF8StringParameter(1, command.data.input); + remStmnt.execute(); + + delete this._lookup[command.GUID]; + }, + + _editCommand: function InputStore__editCommand(command) { + this._log.info("InputStore got editCommand: " + command); + + if (!(command.GUID in this._lookup)) { + this._log.warn("Invalid GUID found, ignoring remove request."); + return; + } + + let placeID = this._getIDfromURI(command.GUID); + let editStmnt = this._placeDB.createStatement("UPDATE moz_inputhistory SET input = ?1, use_count = ?2 WHERE place_id = ?3"); + + if ('input' in command.data) { + editStmnt.bindUTF8StringParameter(0, command.data.input); + } else { + editStmnt.bindUTF8StringParameter(0, this._lookup[command.GUID].input); + } + + if ('count' in command.data) { + editStmnt.bindInt32Parameter(1, command.data.count); + } else { + editStmnt.bindInt32Parameter(1, this._lookup[command.GUID].count); + } + + editStmnt.bindInt32Parameter(2, placeID); + editStmnt.execute(); + }, + + wrap: function InputStore_wrap() { + this._lookup = {}; + let stmnt = this._placeDB.createStatement("SELECT * FROM moz_inputhistory"); + + while (stmnt.executeStep()) { + let pid = stmnt.getInt32(0); + let inp = stmnt.getUTF8String(1); + let cnt = stmnt.getInt32(2); + + let idStmnt = this._placeDB.createStatement("SELECT url FROM moz_places WHERE id = ?1"); + idStmnt.bindInt32Parameter(0, pid); + if (idStmnt.executeStep()) { + let key = idStmnt.getUTF8String(0); + this._lookup[key] = { 'input': inp, 'count': cnt }; + } + } + + return this._lookup; + }, + + wipe: function InputStore_wipe() { + var stmnt = this._placeDB.createStatement("DELETE FROM moz_inputhistory"); + stmnt.execute(); + }, + + _resetGUIDs: function InputStore__resetGUIDs() { + let self = yield; + // Not needed. + } +}; +InputStore.prototype.__proto__ = new Store(); + +function InputTracker() { + this._init(); +} +InputTracker.prototype = { + _logName: "InputTracker", + + __placeDB: null, + get _placeDB() { + if (!this.__placeDB) { + let file = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + file.append("places.sqlite"); + let stor = Cc["@mozilla.org/storage/service;1"]. + getService(Ci.mozIStorageService); + this.__histDB = stor.openDatabase(file); + } + return this.__histDB; + }, + + /* + * To calculate scores, we just count the changes in + * the database since the last time we were asked. + * + * Each change is worth 5 points. + */ + _rowCount: 0, + get score() { + var stmnt = this._placeDB.createStatement("SELECT COUNT(place_id) FROM moz_inputhistory"); + stmnt.executeStep(); + var count = stmnt.getInt32(0); + stmnt.reset(); + + this._score = Math.abs(this._rowCount - count) * 5; + + if (this._score >= 100) + return 100; + else + return this._score; + }, + + resetScore: function InputTracker_resetScore() { + var stmnt = this._placeDB.createStatement("SELECT COUNT(place_id) FROM moz_inputhistory"); + stmnt.executeStep(); + this._rowCount = stmnt.getInt32(0); + stmnt.reset(); + this._score = 0; + }, + + _init: function InputTracker__init() { + this._log = Log4Moz.Service.getLogger("Service." + this._logName); + this._score = 0; + + var stmnt = this._placeDB.createStatement("SELECT COUNT(place_id) FROM moz_inputhistory"); + stmnt.executeStep(); + this._rowCount = stmnt.getInt32(0); + stmnt.reset(); + } +}; +InputTracker.prototype.__proto__ = new Tracker(); diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index b2eee3a1e105..78d10388232e 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -83,6 +83,7 @@ Cu.import("resource://weave/engines/history.js"); Cu.import("resource://weave/engines/passwords.js"); Cu.import("resource://weave/engines/forms.js"); Cu.import("resource://weave/engines/tabs.js"); +Cu.import("resource://weave/engines/input.js"); Function.prototype.async = Async.sugar; @@ -108,6 +109,7 @@ Cu.import("resource://weave/engines/bookmarks.js", Weave); Cu.import("resource://weave/engines/history.js", Weave); Cu.import("resource://weave/engines/forms.js", Weave); Cu.import("resource://weave/engines/tabs.js", Weave); +Cu.import("resource://weave/engines/input.js", Weave); Utils.lazy(Weave, 'Service', WeaveSvc); diff --git a/services/sync/services-sync.js b/services/sync/services-sync.js index a19032c29a0f..eb1fd3ea59d1 100644 --- a/services/sync/services-sync.js +++ b/services/sync/services-sync.js @@ -21,6 +21,7 @@ pref("extensions.weave.engine.cookies", true ); pref("extensions.weave.engine.passwords", false); pref("extensions.weave.engine.forms", false); pref("extensions.weave.engine.tabs", true); +pref("extensions.weave.engine.input", false); pref("extensions.weave.log.appender.console", "Warn"); pref("extensions.weave.log.appender.dump", "Error");