move json and crypto into remote resource 'filters', so the engine doesn't have to explicitly encode/decode anything. note--known regression: filters will not use the encryption algorithm in the status file

This commit is contained in:
Dan Mills 2008-05-30 20:43:55 -07:00
parent 5ac797d47a
commit 2daecc268e
3 changed files with 191 additions and 116 deletions

View File

@ -103,7 +103,7 @@ Engine.prototype = {
get _remote() {
if (!this.__remote)
this.__remote = new RemoteStore(this.serverPrefix);
this.__remote = new RemoteStore(this.serverPrefix, this._engineId);
return this.__remote;
},
@ -180,6 +180,7 @@ Engine.prototype = {
this.__engineId = new Identity(this._pbeId.realm + " - " + this.logName,
this._pbeId.username);
this.__engineId.password = password;
this.__remote = new RemoteStore(this.serverPrefix, this._engineId); // hack
}
return this.__engineId;
},
@ -200,7 +201,7 @@ Engine.prototype = {
this._remote.keys.get(self.cb);
yield;
let keys = this._json.decode(this._remote.keys.data);
let keys = this._remote.keys.data;
if (!keys || !keys.ring || !keys.ring[this._engineId.userHash])
throw "Keyring does not contain a key for this user";
@ -209,6 +210,7 @@ Engine.prototype = {
keys.ring[this._engineId.userHash], this._pbeId);
let symkey = yield;
this._engineId.setTempPassword(symkey);
this.__remote = new RemoteStore(this.serverPrefix, this._engineId); // hack
self.done();
},
@ -421,25 +423,21 @@ Engine.prototype = {
this._log.error("Could not upload files to server"); // eep?
} else {
Crypto.PBEencrypt.async(Crypto, self.cb,
this._serializeCommands(server.deltas),
this._engineId);
let data = yield;
this._remote.deltas.put(self.cb, data);
this._remote.deltas.put(self.cb, this._serializeCommands(server.deltas));
yield;
let c = 0;
for (GUID in this._snapshot.data)
c++;
this._remote.status.put(self.cb, this._json.encode(
{GUID: this._snapshot.GUID,
formatVersion: ENGINE_STORAGE_FORMAT_VERSION,
snapVersion: server.snapVersion,
maxVersion: this._snapshot.version,
snapEncryption: server.snapEncryption,
deltasEncryption: Crypto.defaultAlgorithm,
itemCount: c}));
this._remote.status.put(self.cb,
{GUID: this._snapshot.GUID,
formatVersion: ENGINE_STORAGE_FORMAT_VERSION,
snapVersion: server.snapVersion,
maxVersion: this._snapshot.version,
snapEncryption: server.snapEncryption,
deltasEncryption: Crypto.defaultAlgorithm,
itemCount: c});
this._log.info("Successfully updated deltas and status on server");
this._snapshot.save();
@ -476,11 +474,12 @@ Engine.prototype = {
*/
_getServerData: function BmkEngine__getServerData() {
let self = yield;
let status;
try {
this._log.debug("Getting status file from server");
this._remote.status.get(self.cb);
yield;
status = yield;
this._log.info("Got status file from server");
} catch (e if e.message.status == 404) {
@ -514,7 +513,6 @@ Engine.prototype = {
formatVersion: null, maxVersion: null, snapVersion: null,
snapEncryption: null, deltasEncryption: null,
snapshot: null, deltas: null, updates: null};
let status = this._json.decode(this._remote.status.data);
let deltas, allDeltas;
let snap = new SnapshotStore();
@ -551,25 +549,13 @@ Engine.prototype = {
this._log.info("Local snapshot is out of date");
this._log.info("Downloading server snapshot");
this._remote.snapshot.get(self.cb);
yield;
Crypto.PBEdecrypt.async(Crypto, self.cb,
this._remote.snapshot.data,
this._engineId,
status.snapEncryption);
let data = yield;
snap.data = this._json.decode(data);
this._remote.snapshot.get(self.cb); // fixme: doesn't use status.snapEncryption
snap.data = yield;
this._log.info("Downloading server deltas");
this._remote.deltas.get(self.cb);
yield;
Crypto.PBEdecrypt.async(Crypto, self.cb,
this._remote.deltas.data,
this._engineId,
status.deltasEncryption);
data = yield;
allDeltas = this._json.decode(data);
deltas = this._json.decode(data);
this._remote.deltas.get(self.cb); // fixme: doesn't use status.deltasEncryption
allDeltas = yield;
deltas = allDeltas;
} else if (this._snapshot.version >= status.snapVersion &&
this._snapshot.version < status.maxVersion) {
@ -577,14 +563,8 @@ Engine.prototype = {
snap.data = Utils.deepCopy(this._snapshot.data);
this._log.info("Downloading server deltas");
this._remote.deltas.get(self.cb);
yield;
Crypto.PBEdecrypt.async(Crypto, self.cb,
this._remote.deltas.data,
this._engineId,
status.deltasEncryption);
let data = yield;
allDeltas = this._json.decode(data);
this._remote.deltas.get(self.cb); // fixme: doesn't use status.deltasEncryption
allDeltas = yield;
deltas = allDeltas.slice(this._snapshot.version - status.snapVersion);
} else if (this._snapshot.version == status.maxVersion) {
@ -593,14 +573,8 @@ Engine.prototype = {
// FIXME: could optimize this case by caching deltas file
this._log.info("Downloading server deltas");
this._remote.deltas.get(self.cb);
yield;
Crypto.PBEdecrypt.async(Crypto, self.cb,
this._remote.deltas.data,
this._engineId,
status.deltasEncryption);
let data = yield;
allDeltas = this._json.decode(data);
this._remote.deltas.get(self.cb); // fixme: doesn't use status.deltasEncryption
allDeltas = yield;
deltas = [];
} else { // this._snapshot.version > status.maxVersion
@ -658,16 +632,14 @@ Engine.prototype = {
let keys = {ring: {}};
keys.ring[this._engineId.userHash] = enckey;
this._remote.keys.put(self.cb, this._json.encode(keys));
this._remote.keys.put(self.cb, keys);
yield;
Crypto.PBEencrypt.async(Crypto, self.cb,
this._snapshot.serialize(),
this._engineId);
let data = yield;
this._remote.snapshot.put(self.cb, data);
this.__remote = new RemoteStore(this.serverPrefix, this._engineId); // hack
this._remote.snapshot.put(self.cb, this._snapshot.wrap());
yield;
this._remote.deltas.put(self.cb, "[]");
this._remote.deltas.put(self.cb, []);
yield;
let c = 0;
@ -675,14 +647,13 @@ Engine.prototype = {
c++;
this._remote.status.put(self.cb,
this._json.encode(
{GUID: this._snapshot.GUID,
formatVersion: ENGINE_STORAGE_FORMAT_VERSION,
snapVersion: this._snapshot.version,
maxVersion: this._snapshot.version,
snapEncryption: Crypto.defaultAlgorithm,
deltasEncryption: "none",
itemCount: c}));
{GUID: this._snapshot.GUID,
formatVersion: ENGINE_STORAGE_FORMAT_VERSION,
snapVersion: this._snapshot.version,
maxVersion: this._snapshot.version,
snapEncryption: Crypto.defaultAlgorithm,
deltasEncryption: "none",
itemCount: c});
yield;
this._log.info("Full upload to server successful");

View File

@ -45,6 +45,7 @@ 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/crypto.js");
Cu.import("resource://weave/async.js");
Cu.import("resource://weave/dav.js");
Cu.import("resource://weave/stores.js");
@ -69,12 +70,7 @@ RequestException.prototype = {
};
function Resource(path) {
this._identity = null; // unused
this._dav = null; // unused
this._path = path;
this._data = null;
this._downloaded = false;
this._dirty = false;
this._init(path);
}
Resource.prototype = {
get identity() { return this._identity; },
@ -95,9 +91,34 @@ Resource.prototype = {
this._data = value;
},
get lastRequest() { return this._lastRequest; },
get downloaded() { return this._downloaded; },
get dirty() { return this._dirty; },
pushFilter: function Res_pushFilter(filter) {
this._filters.push(filter);
},
popFilter: function Res_popFilter() {
return this._filters.pop();
},
clearFilters: function Res_clearFilters() {
this._filters = [];
},
_init: function Res__init(path) {
this._identity = null; // unused
this._dav = null; // unused
this._path = path;
this._data = null;
this._downloaded = false;
this._dirty = false;
this._filters = [];
this._lastRequest = null;
this._log = Log4Moz.Service.getLogger("Service.Resource");
},
_sync: function Res__sync() {
let self = yield;
let ret;
@ -125,7 +146,13 @@ Resource.prototype = {
let self = yield;
let listener, timer;
let iter = 0;
let ret;
if ("PUT" == action) {
for each (let filter in this._filters) {
filter.beforePUT.async(filter, self.cb, data);
data = yield;
}
}
while (true) {
switch (action) {
@ -141,28 +168,29 @@ Resource.prototype = {
default:
throw "Unknown request action for Resource";
}
ret = yield;
this._lastRequest = yield;
if (action == "DELETE" &&
Utils.checkStatus(ret.status, null, [[200,300],404])) {
Utils.checkStatus(this._lastRequest.status, null, [[200,300],404])) {
this._dirty = false;
this._data = null;
break;
} else if (Utils.checkStatus(ret.status)) {
} else if (Utils.checkStatus(this._lastRequest.status)) {
this._log.debug(action + " request successful");
this._dirty = false;
if (action == "GET")
this._data = ret.responseText;
this._data = this._lastRequest.responseText;
else if (action == "PUT")
this._data = data;
break;
} else if (action == "GET" && ret.status == 404) {
throw new RequestException(this, action, ret);
} else if (action == "GET" && this._lastRequest.status == 404) {
throw new RequestException(this, action, this._lastRequest);
} else if (iter >= 10) {
// iter too big? bail
throw new RequestException(this, action, ret);
throw new RequestException(this, action, this._lastRequest);
} else {
// wait for a bit and try again
@ -176,7 +204,14 @@ Resource.prototype = {
}
}
self.done(ret);
if ("GET" == action) {
for each (let filter in this._filters.reverse()) {
filter.afterGET.async(filter, self.cb, this._data);
this._data = yield;
}
}
self.done(this._data);
},
get: function Res_get(onComplete) {
@ -192,40 +227,109 @@ Resource.prototype = {
}
};
function JsonResource(path) {
function ResourceFilter() {
this._log = Log4Moz.Service.getLogger("Service.ResourceFilter");
}
JsonResource.prototype = {
__proto__: new Resource(),
_get: function(onComplete) {
ResourceFilter.prototype = {
beforePUT: function ResFilter_beforePUT(data) {
let self = yield;
this.__proto__.get(onComplete);
yield;
this._log.debug("Doing absolutely nothing")
self.done(data);
},
get: function JRes_get(onComplete) {
foo.async();
afterGET: function ResFilter_afterGET(data) {
let self = yield;
this._log.debug("Doing absolutely nothing")
self.done(data);
}
};
function RemoteStore(serverPrefix) {
function JsonFilter() {
this._log = Log4Moz.Service.getLogger("Service.JsonFilter");
}
JsonFilter.prototype = {
__proto__: new ResourceFilter(),
get _json() {
let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
this.__defineGetter__("_json", function() json);
return this._json;
},
beforePUT: function JsonFilter_beforePUT(data) {
let self = yield;
this._log.debug("Encoding data as JSON")
self.done(this._json.encode(data));
},
afterGET: function JsonFilter_afterGET(data) {
let self = yield;
this._log.debug("Decoding JSON data")
self.done(this._json.decode(data));
}
};
// FIXME: doesn't use the crypto algorithm stored in the status file
function PBECryptoFilter(identity) {
this._identity = identity;
this._log = Log4Moz.Service.getLogger("Service.PBECryptoFilter");
}
PBECryptoFilter.prototype = {
__proto__: new ResourceFilter(),
beforePUT: function PBEFilter_beforePUT(data) {
let self = yield;
this._log.debug("Encrypting data")
Crypto.PBEencrypt.async(Crypto, self.cb, data, this._identity);
let ret = yield;
self.done(ret);
},
afterGET: function PBEFilter_afterGET(data) {
let self = yield;
this._log.debug("Decrypting data")
Crypto.PBEdecrypt.async(Crypto, self.cb, data, this._identity);
let ret = yield;
self.done(ret);
}
};
function RemoteStore(serverPrefix, cryptoId) {
this._prefix = serverPrefix;
this._status = new Resource(serverPrefix + "status.json");
this._keys = new Resource(serverPrefix + "keys.json");
this._snapshot = new Resource(serverPrefix + "snapshot.json");
this._deltas = new Resource(serverPrefix + "deltas.json");
this._cryptoId = cryptoId;
this._init();
}
RemoteStore.prototype = {
get status() {
return this._status;
_init: function Remote__init(serverPrefix, cryptoId) {
if (!this._prefix || !this._cryptoId)
return;
let json = new JsonFilter();
let crypto = new PBECryptoFilter(this._cryptoId);
this._status = new Resource(this._prefix + "status.json");
this._status.pushFilter(json);
this._keys = new Resource(this._prefix + "keys.json");
this._keys.pushFilter(new JsonFilter());
this._snapshot = new Resource(this._prefix + "snapshot.json");
this._snapshot.pushFilter(json);
this._snapshot.pushFilter(crypto);
this._deltas = new Resource(this._prefix + "deltas.json");
this._deltas.pushFilter(json);
this._deltas.pushFilter(crypto);
},
get keys() {
return this._keys;
get status() this._status,
get keys() this._keys,
get snapshot() this._snapshot,
get deltas() this._deltas,
get serverPrefix() this._prefix,
set serverPrefix(value) {
this._prefix = value;
this._init();
},
get snapshot() {
return this._snapshot;
},
get deltas() {
return this._deltas;
get cryptoId() this._cryptoId,
set cryptoId(value) {
this._cryptoId = value;
this._init();
}
};

View File

@ -89,7 +89,7 @@ Store.prototype = {
yield; // Yield to main loop
}
var command = commandList[i];
this._log.debug("Processing command: " + this._json.encode(command));
this._log.trace("Processing command: " + this._json.encode(command));
switch (command["action"]) {
case "create":
this._createCommand(command);
@ -900,7 +900,7 @@ CookieStore.prototype = {
if (cookie.QueryInterface( Ci.nsICookie )){
// String used to identify cookies is
// host:path:name
if ( cookie.isSession ) {
if ( cookie.isSession ) {
/* Skip session-only cookies, sync only persistent cookies. */
continue;
}
@ -1041,7 +1041,7 @@ function FormStore() {
}
FormStore.prototype = {
_logName: "FormStore",
__formDB: null,
get _formDB() {
if (!this.__formDB) {
@ -1055,45 +1055,45 @@ FormStore.prototype = {
}
return this.__formDB;
},
__formHistory: null,
get _formHistory() {
if (!this.__formHistory)
if (!this.__formHistory)
this.__formHistory = Cc["@mozilla.org/satchel/form-history;1"].
getService(Ci.nsIFormHistory2);
return this.__formHistory;
},
_createCommand: function FormStore__createCommand(command) {
this._log.info("FormStore got createCommand: " + command );
this._formHistory.addEntry(command.data.name, command.data.value);
},
_removeCommand: function FormStore__removeCommand(command) {
this._log.info("FormStore got removeCommand: " + command );
this._formHistory.removeEntry(command.data.name, command.data.value);
},
_editCommand: function FormStore__editCommand(command) {
this._log.info("FormStore got editCommand: " + command );
this._log.warn("Form syncs are expected to only be create/remove!");
},
wrap: function FormStore_wrap() {
var items = [];
var stmnt = this._formDB.createStatement("SELECT * FROM moz_formhistory");
while (stmnt.executeStep()) {
var nam = stmnt.getUTF8String(1);
var val = stmnt.getUTF8String(2);
var key = Utils.sha1(nam + val);
items[key] = { name: nam, value: val };
}
return items;
},
wipe: function FormStore_wipe() {
this._formHistory.removeAllEntries();
},