2009-01-07 00:54:18 +03:00
|
|
|
/* ***** 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 Weave
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Mozilla.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Dan Mills <thunder@mozilla.com>
|
2011-01-19 03:23:20 +03:00
|
|
|
* Philipp von Weitershausen <philipp@weitershausen.de>
|
2009-01-07 00:54:18 +03:00
|
|
|
*
|
|
|
|
* 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 ***** */
|
|
|
|
|
2011-01-19 03:23:20 +03:00
|
|
|
const EXPORTED_SYMBOLS = ["Clients", "ClientsRec"];
|
2009-01-07 00:54:18 +03:00
|
|
|
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cu = Components.utils;
|
|
|
|
|
2010-06-17 01:30:08 +04:00
|
|
|
Cu.import("resource://services-sync/constants.js");
|
|
|
|
Cu.import("resource://services-sync/engines.js");
|
|
|
|
Cu.import("resource://services-sync/ext/StringBundle.js");
|
2011-01-19 03:23:30 +03:00
|
|
|
Cu.import("resource://services-sync/record.js");
|
2010-06-17 01:30:08 +04:00
|
|
|
Cu.import("resource://services-sync/util.js");
|
2009-01-07 00:54:18 +03:00
|
|
|
|
2011-01-19 03:23:20 +03:00
|
|
|
const CLIENTS_TTL = 1814400; // 21 days
|
2011-01-15 00:22:20 +03:00
|
|
|
const CLIENTS_TTL_REFRESH = 604800; // 7 days
|
|
|
|
|
2011-01-19 03:23:20 +03:00
|
|
|
function ClientsRec(collection, id) {
|
|
|
|
CryptoWrapper.call(this, collection, id);
|
|
|
|
}
|
|
|
|
ClientsRec.prototype = {
|
|
|
|
__proto__: CryptoWrapper.prototype,
|
|
|
|
_logName: "Record.Clients",
|
|
|
|
ttl: CLIENTS_TTL
|
|
|
|
};
|
|
|
|
|
|
|
|
Utils.deferGetSet(ClientsRec, "cleartext", ["name", "type", "commands"]);
|
|
|
|
|
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
Utils.lazy(this, "Clients", ClientEngine);
|
2009-01-07 00:54:18 +03:00
|
|
|
|
|
|
|
function ClientEngine() {
|
2010-02-12 02:29:15 +03:00
|
|
|
SyncEngine.call(this, "Clients");
|
2010-02-12 02:25:31 +03:00
|
|
|
|
|
|
|
// Reset the client on every startup so that we fetch recent clients
|
|
|
|
this._resetClient();
|
2009-01-07 00:54:18 +03:00
|
|
|
}
|
|
|
|
ClientEngine.prototype = {
|
|
|
|
__proto__: SyncEngine.prototype,
|
|
|
|
_storeObj: ClientStore,
|
2010-03-17 02:39:08 +03:00
|
|
|
_recordObj: ClientsRec,
|
|
|
|
|
2010-05-06 04:16:17 +04:00
|
|
|
// Always sync client data as it controls other sync behavior
|
|
|
|
get enabled() true,
|
|
|
|
|
2011-01-15 00:22:20 +03:00
|
|
|
get lastRecordUpload() {
|
|
|
|
return Svc.Prefs.get(this.name + ".lastRecordUpload", 0);
|
|
|
|
},
|
|
|
|
set lastRecordUpload(value) {
|
|
|
|
Svc.Prefs.set(this.name + ".lastRecordUpload", Math.floor(value));
|
|
|
|
},
|
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
// Aggregate some stats on the composition of clients on this account
|
|
|
|
get stats() {
|
|
|
|
let stats = {
|
|
|
|
hasMobile: this.localType == "mobile",
|
|
|
|
names: [this.localName],
|
|
|
|
numClients: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
for each (let {name, type} in this._store._remoteClients) {
|
|
|
|
stats.hasMobile = stats.hasMobile || type == "mobile";
|
|
|
|
stats.names.push(name);
|
|
|
|
stats.numClients++;
|
|
|
|
}
|
2009-01-24 02:09:21 +03:00
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
return stats;
|
2009-01-24 02:09:21 +03:00
|
|
|
},
|
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
// Remove any commands for the local client and mark it for upload
|
|
|
|
clearCommands: function clearCommands() {
|
|
|
|
delete this.localCommands;
|
|
|
|
this._tracker.addChangedID(this.localID);
|
2009-01-24 02:09:21 +03:00
|
|
|
},
|
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
// Send a command+args pair to each remote client
|
|
|
|
sendCommand: function sendCommand(command, args) {
|
|
|
|
// Helper to determine if the client already has this command
|
|
|
|
let notDupe = function(other) other.command != command ||
|
|
|
|
JSON.stringify(other.args) != JSON.stringify(args);
|
|
|
|
|
|
|
|
// Package the command/args pair into an object
|
|
|
|
let action = {
|
|
|
|
command: command,
|
|
|
|
args: args,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Send the command to each remote client
|
|
|
|
for (let [id, client] in Iterator(this._store._remoteClients)) {
|
|
|
|
// Set the action to be a new commands array if none exists
|
|
|
|
if (client.commands == null)
|
|
|
|
client.commands = [action];
|
|
|
|
// Add the new action if there are no duplicates
|
|
|
|
else if (client.commands.every(notDupe))
|
|
|
|
client.commands.push(action);
|
|
|
|
// Must have been a dupe.. skip!
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
|
|
|
|
this._log.trace("Client " + id + " got a new action: " + [command, args]);
|
|
|
|
this._tracker.addChangedID(id);
|
|
|
|
}
|
2009-01-24 02:09:21 +03:00
|
|
|
},
|
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
get localID() {
|
|
|
|
// Generate a random GUID id we don't have one
|
|
|
|
let localID = Svc.Prefs.get("client.GUID", "");
|
|
|
|
return localID == "" ? this.localID = Utils.makeGUID() : localID;
|
2009-01-24 02:09:21 +03:00
|
|
|
},
|
2010-03-17 02:39:08 +03:00
|
|
|
set localID(value) Svc.Prefs.set("client.GUID", value),
|
2009-01-24 02:09:21 +03:00
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
get localName() {
|
|
|
|
let localName = Svc.Prefs.get("client.name", "");
|
|
|
|
if (localName != "")
|
|
|
|
return localName;
|
2009-11-20 10:31:04 +03:00
|
|
|
|
|
|
|
// Generate a client name if we don't have a useful one yet
|
2010-06-11 22:36:51 +04:00
|
|
|
let user = Svc.Env.get("USER") || Svc.Env.get("USERNAME") ||
|
2010-10-14 12:59:08 +04:00
|
|
|
Svc.Prefs.get("account") || Svc.Prefs.get("username");
|
2010-06-11 22:36:51 +04:00
|
|
|
let brand = new StringBundle("chrome://branding/locale/brand.properties");
|
|
|
|
let app = brand.get("brandShortName");
|
|
|
|
|
2010-09-08 22:55:57 +04:00
|
|
|
let system = Svc.SysInfo.get("device") ||
|
|
|
|
Cc["@mozilla.org/network/protocol;1?name=http"]
|
|
|
|
.getService(Ci.nsIHttpProtocolHandler).oscpu;
|
|
|
|
|
|
|
|
return this.localName = Str.sync.get("client.name2", [user, app, system]);
|
2009-11-20 10:31:04 +03:00
|
|
|
},
|
2010-03-17 02:39:08 +03:00
|
|
|
set localName(value) Svc.Prefs.set("client.name", value),
|
|
|
|
|
2010-06-17 02:11:40 +04:00
|
|
|
get localType() Svc.Prefs.get("client.type", "desktop"),
|
2010-03-17 02:39:08 +03:00
|
|
|
set localType(value) Svc.Prefs.set("client.type", value),
|
2009-02-27 09:36:14 +03:00
|
|
|
|
2010-03-13 03:14:09 +03:00
|
|
|
isMobile: function isMobile(id) {
|
2010-04-29 06:20:08 +04:00
|
|
|
if (this._store._remoteClients[id])
|
|
|
|
return this._store._remoteClients[id].type == "mobile";
|
|
|
|
return false;
|
2010-03-13 03:14:09 +03:00
|
|
|
},
|
|
|
|
|
2011-01-15 00:22:20 +03:00
|
|
|
_syncStartup: function _syncStartup() {
|
|
|
|
// Reupload new client record periodically.
|
|
|
|
if (Date.now() / 1000 - this.lastRecordUpload > CLIENTS_TTL_REFRESH) {
|
|
|
|
this._tracker.addChangedID(this.localID);
|
|
|
|
this.lastRecordUpload = Date.now() / 1000;
|
|
|
|
}
|
|
|
|
SyncEngine.prototype._syncStartup.call(this);
|
|
|
|
},
|
|
|
|
|
2009-12-11 05:39:51 +03:00
|
|
|
// Always process incoming items because they might have commands
|
|
|
|
_reconcile: function _reconcile() {
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
2010-02-20 00:36:42 +03:00
|
|
|
// Treat reset the same as wiping for locally cached clients
|
|
|
|
_resetClient: function _resetClient() this._wipeClient(),
|
|
|
|
|
|
|
|
_wipeClient: function _wipeClient() {
|
2009-12-03 01:44:17 +03:00
|
|
|
SyncEngine.prototype._resetClient.call(this);
|
2009-02-27 09:36:14 +03:00
|
|
|
this._store.wipe();
|
2009-02-27 02:06:57 +03:00
|
|
|
}
|
2009-01-07 00:54:18 +03:00
|
|
|
};
|
|
|
|
|
2010-02-12 02:29:15 +03:00
|
|
|
function ClientStore(name) {
|
|
|
|
Store.call(this, name);
|
2009-01-07 00:54:18 +03:00
|
|
|
}
|
|
|
|
ClientStore.prototype = {
|
|
|
|
__proto__: Store.prototype,
|
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
create: function create(record) this.update(record),
|
2009-04-01 10:56:32 +04:00
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
update: function update(record) {
|
2010-05-06 04:16:17 +04:00
|
|
|
// Only grab commands from the server; local name/type always wins
|
|
|
|
if (record.id == Clients.localID)
|
2010-03-17 02:39:08 +03:00
|
|
|
Clients.localCommands = record.commands;
|
|
|
|
else
|
|
|
|
this._remoteClients[record.id] = record.cleartext;
|
2009-01-07 00:54:18 +03:00
|
|
|
},
|
|
|
|
|
2010-11-30 03:41:17 +03:00
|
|
|
createRecord: function createRecord(id, collection) {
|
|
|
|
let record = new ClientsRec(collection, id);
|
2009-02-20 12:52:07 +03:00
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
// Package the individual components into a record for the local client
|
2010-11-30 03:41:17 +03:00
|
|
|
if (id == Clients.localID) {
|
2010-03-17 02:39:08 +03:00
|
|
|
record.name = Clients.localName;
|
|
|
|
record.type = Clients.localType;
|
|
|
|
record.commands = Clients.localCommands;
|
|
|
|
}
|
|
|
|
else
|
2010-11-30 03:41:17 +03:00
|
|
|
record.cleartext = this._remoteClients[id];
|
2009-01-07 00:54:18 +03:00
|
|
|
|
2009-04-01 10:56:32 +04:00
|
|
|
return record;
|
2009-01-07 00:54:18 +03:00
|
|
|
},
|
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
itemExists: function itemExists(id) id in this.getAllIDs(),
|
2009-01-07 00:54:18 +03:00
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
getAllIDs: function getAllIDs() {
|
|
|
|
let ids = {};
|
|
|
|
ids[Clients.localID] = true;
|
|
|
|
for (let id in this._remoteClients)
|
|
|
|
ids[id] = true;
|
|
|
|
return ids;
|
2009-01-07 00:54:18 +03:00
|
|
|
},
|
|
|
|
|
2010-03-17 02:39:08 +03:00
|
|
|
wipe: function wipe() {
|
|
|
|
this._remoteClients = {};
|
2009-04-01 10:56:32 +04:00
|
|
|
},
|
2009-01-07 00:54:18 +03:00
|
|
|
};
|