зеркало из https://github.com/mozilla/pjs.git
Asynchronous generator helpers rework + PKI work
* Async helpers are in a module of their own now * Async routines have simpler semantics now. onComplete handlers are taken care of by the helpers. Exceptions are bubbled up across nested asynchronous generators * Stack traces are automatically logged for unhandled exceptions * Async generators are now allowed to 'bottom out' (StopIteration is ignored) - this is configurable. * RSA key generation fixes * On login we now create an RSA keypair, encrypt the private one with PBE, and upload them to the server * Log files are now limited to 2MB (down from 5)
This commit is contained in:
Родитель
ad5f58d76e
Коммит
99333d3c98
|
@ -45,8 +45,9 @@ 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");
|
||||
|
||||
Function.prototype.async = Utils.generatorAsync;
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
function WeaveCrypto() {
|
||||
this._init();
|
||||
|
@ -210,7 +211,7 @@ WeaveCrypto.prototype = {
|
|||
|
||||
try {
|
||||
this._openssl("pkcs8", "-in", "privkey.pem", "-out", "enckey.pem",
|
||||
"-topk8", "-v2", algorithm, "-pass", "file:pass");
|
||||
"-topk8", "-v2", algorithm, "-passout", "file:pass");
|
||||
|
||||
} catch (e) {
|
||||
throw e;
|
||||
|
@ -223,7 +224,7 @@ WeaveCrypto.prototype = {
|
|||
let [cryptedKeyFIS] = Utils.open(cryptedKeyF, "<");
|
||||
let cryptedKey = Utils.readStream(cryptedKeyFIS);
|
||||
cryptedKeyFIS.close();
|
||||
cryptedKey.remove(false);
|
||||
cryptedKeyF.remove(false);
|
||||
|
||||
let [pubKeyFIS] = Utils.open(pubKeyF, "<");
|
||||
let pubKey = Utils.readStream(pubKeyFIS);
|
||||
|
@ -234,7 +235,7 @@ WeaveCrypto.prototype = {
|
|||
},
|
||||
|
||||
// returns 'input' encrypted with the 'pubkey' public RSA key
|
||||
_opensslRSAEncrypt: function Crypto__opensslRSAEncrypt(input, pubkey) {
|
||||
_opensslRSAencrypt: function Crypto__opensslRSAencrypt(input, pubkey) {
|
||||
let inputFile = Utils.getTmp("input");
|
||||
let [inputFOS] = Utils.open(inputFile, ">");
|
||||
inputFOS.writeString(input);
|
||||
|
@ -261,7 +262,7 @@ WeaveCrypto.prototype = {
|
|||
},
|
||||
|
||||
// returns 'input' decrypted with the 'privkey' private RSA key and password
|
||||
_opensslRSADecrypt: function Crypto__opensslRSADecrypt(input, privkey, password) {
|
||||
_opensslRSAdecrypt: function Crypto__opensslRSAdecrypt(input, privkey, password) {
|
||||
let inputFile = Utils.getTmp("input");
|
||||
let [inputFOS] = Utils.open(inputFile, ">");
|
||||
inputFOS.writeString(input);
|
||||
|
@ -338,131 +339,127 @@ WeaveCrypto.prototype = {
|
|||
|
||||
// Crypto
|
||||
|
||||
PBEencrypt: function Crypto_PBEencrypt(onComplete, data, identity, algorithm) {
|
||||
let [self, cont] = yield;
|
||||
let listener = new Utils.EventListener(cont);
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
PBEencrypt: function Crypto_PBEencrypt(data, identity, algorithm) {
|
||||
let self = yield;
|
||||
let ret;
|
||||
|
||||
try {
|
||||
if (!algorithm)
|
||||
algorithm = this.defaultAlgorithm;
|
||||
if (!algorithm)
|
||||
algorithm = this.defaultAlgorithm;
|
||||
|
||||
if (algorithm != "none")
|
||||
this._log.debug("Encrypting data");
|
||||
if (algorithm != "none")
|
||||
this._log.debug("Encrypting data");
|
||||
|
||||
switch (algorithm) {
|
||||
case "none":
|
||||
ret = data;
|
||||
break;
|
||||
switch (algorithm) {
|
||||
case "none":
|
||||
ret = data;
|
||||
break;
|
||||
|
||||
case "XXXTEA": // Weave 0.1.12.10 and below had this typo
|
||||
case "XXTEA": {
|
||||
let gen = this._xxtea.encrypt(data, identity.password);
|
||||
ret = gen.next();
|
||||
case "XXXTEA": // Weave 0.1.12.10 and below had this typo
|
||||
case "XXTEA": {
|
||||
let gen = this._xxtea.encrypt(data, identity.password);
|
||||
ret = gen.next();
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
try {
|
||||
while (typeof(ret) == "object") {
|
||||
timer.initWithCallback(listener, 0, timer.TYPE_ONE_SHOT);
|
||||
timer.initWithCallback(self.listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
ret = gen.next();
|
||||
}
|
||||
gen.close();
|
||||
} break;
|
||||
|
||||
case "aes-128-cbc":
|
||||
case "aes-192-cbc":
|
||||
case "aes-256-cbc":
|
||||
case "bf-cbc":
|
||||
case "des-ede3-cbc":
|
||||
ret = this._opensslPBE("-e", algorithm, data, identity.password);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown encryption algorithm: " + algorithm;
|
||||
} finally {
|
||||
timer = null;
|
||||
}
|
||||
} break;
|
||||
|
||||
if (algorithm != "none")
|
||||
this._log.debug("Done encrypting data");
|
||||
case "aes-128-cbc":
|
||||
case "aes-192-cbc":
|
||||
case "aes-256-cbc":
|
||||
case "bf-cbc":
|
||||
case "des-ede3-cbc":
|
||||
ret = this._opensslPBE("-e", algorithm, data, identity.password);
|
||||
break;
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
timer = null;
|
||||
Utils.generatorDone(this, self, onComplete, ret);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
default:
|
||||
throw "Unknown encryption algorithm: " + algorithm;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
if (algorithm != "none")
|
||||
this._log.debug("Done encrypting data");
|
||||
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
PBEdecrypt: function Crypto_PBEdecrypt(onComplete, data, identity, algorithm) {
|
||||
let [self, cont] = yield;
|
||||
let listener = new Utils.EventListener(cont);
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
PBEdecrypt: function Crypto_PBEdecrypt(data, identity, algorithm) {
|
||||
let self = yield;
|
||||
let ret;
|
||||
|
||||
try {
|
||||
if (!algorithm)
|
||||
algorithm = this.defaultAlgorithm;
|
||||
if (!algorithm)
|
||||
algorithm = this.defaultAlgorithm;
|
||||
|
||||
if (algorithm != "none")
|
||||
this._log.debug("Decrypting data");
|
||||
if (algorithm != "none")
|
||||
this._log.debug("Decrypting data");
|
||||
|
||||
switch (algorithm) {
|
||||
case "none":
|
||||
ret = data;
|
||||
break;
|
||||
switch (algorithm) {
|
||||
case "none":
|
||||
ret = data;
|
||||
break;
|
||||
|
||||
case "XXXTEA": // Weave 0.1.12.10 and below had this typo
|
||||
case "XXTEA": {
|
||||
let gen = this._xxtea.decrypt(data, identity.password);
|
||||
ret = gen.next();
|
||||
case "XXXTEA": // Weave 0.1.12.10 and below had this typo
|
||||
case "XXTEA": {
|
||||
let gen = this._xxtea.decrypt(data, identity.password);
|
||||
ret = gen.next();
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
try {
|
||||
while (typeof(ret) == "object") {
|
||||
timer.initWithCallback(listener, 0, timer.TYPE_ONE_SHOT);
|
||||
timer.initWithCallback(self.listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
ret = gen.next();
|
||||
}
|
||||
gen.close();
|
||||
} break;
|
||||
|
||||
case "aes-128-cbc":
|
||||
case "aes-192-cbc":
|
||||
case "aes-256-cbc":
|
||||
case "bf-cbc":
|
||||
case "des-ede3-cbc":
|
||||
ret = this._opensslPBE("-d", algorithm, data, identity.password);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown encryption algorithm: " + algorithm;
|
||||
} finally {
|
||||
timer = null;
|
||||
}
|
||||
} break;
|
||||
|
||||
if (algorithm != "none")
|
||||
this._log.debug("Done decrypting data");
|
||||
case "aes-128-cbc":
|
||||
case "aes-192-cbc":
|
||||
case "aes-256-cbc":
|
||||
case "bf-cbc":
|
||||
case "des-ede3-cbc":
|
||||
ret = this._opensslPBE("-d", algorithm, data, identity.password);
|
||||
break;
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
timer = null;
|
||||
Utils.generatorDone(this, self, onComplete, ret);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
default:
|
||||
throw "Unknown encryption algorithm: " + algorithm;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
if (algorithm != "none")
|
||||
this._log.debug("Done decrypting data");
|
||||
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
PBEkeygen: function Crypto_PBEkeygen() {
|
||||
return this._opensslRand();
|
||||
let self = yield;
|
||||
let ret = this._opensslRand();
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
RSAkeygen: function Crypto_RSAkeygen(password) {
|
||||
return this._opensslRSAKeyGen(password);
|
||||
let self = yield;
|
||||
let ret = this._opensslRSAKeyGen(password);
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
RSAencrypt: function Crypto_RSAencrypt(data, key) {
|
||||
return this._opensslRSAEncrypt(data, key);
|
||||
let self = yield;
|
||||
let ret = this._opensslRSAencrypt(data, key);
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
RSAdecrypt: function Crypto_RSAdecrypt(data, key, password) {
|
||||
return this._opensslRSADecrypt(data, key, password);
|
||||
let self = yield;
|
||||
let ret = this._opensslRSAdecrypt(data, key, password);
|
||||
self.done(ret);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -44,8 +44,9 @@ const Cu = Components.utils;
|
|||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://weave/log4moz.js");
|
||||
Cu.import("resource://weave/util.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
|
||||
Function.prototype.async = Utils.generatorAsync;
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
/*
|
||||
* DAV object
|
||||
|
@ -80,58 +81,50 @@ DAVCollection.prototype = {
|
|||
return this._loggedIn;
|
||||
},
|
||||
|
||||
_makeRequest: function DC__makeRequest(onComplete, op, path, headers, data) {
|
||||
let [self, cont] = yield;
|
||||
_makeRequest: function DC__makeRequest(op, path, headers, data) {
|
||||
let self = yield;
|
||||
let ret;
|
||||
|
||||
try {
|
||||
this._log.debug("Creating " + op + " request for " + this._baseURL + path);
|
||||
this._log.debug("Creating " + op + " request for " + this._baseURL + path);
|
||||
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
|
||||
request = request.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
|
||||
request = request.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
|
||||
request.addEventListener("load", new Utils.EventListener(cont, "load"), false);
|
||||
request.addEventListener("error", new Utils.EventListener(cont, "error"), false);
|
||||
request = request.QueryInterface(Ci.nsIXMLHttpRequest);
|
||||
request.open(op, this._baseURL + path, true);
|
||||
request.addEventListener("load", new Utils.EventListener(self.cb, "load"), false);
|
||||
request.addEventListener("error", new Utils.EventListener(self.cb, "error"), false);
|
||||
request = request.QueryInterface(Ci.nsIXMLHttpRequest);
|
||||
request.open(op, this._baseURL + path, true);
|
||||
|
||||
|
||||
// Force cache validation
|
||||
let channel = request.channel;
|
||||
channel = channel.QueryInterface(Ci.nsIRequest);
|
||||
let loadFlags = channel.loadFlags;
|
||||
loadFlags |= Ci.nsIRequest.VALIDATE_ALWAYS;
|
||||
channel.loadFlags = loadFlags;
|
||||
// Force cache validation
|
||||
let channel = request.channel;
|
||||
channel = channel.QueryInterface(Ci.nsIRequest);
|
||||
let loadFlags = channel.loadFlags;
|
||||
loadFlags |= Ci.nsIRequest.VALIDATE_ALWAYS;
|
||||
channel.loadFlags = loadFlags;
|
||||
|
||||
let key;
|
||||
for (key in headers) {
|
||||
if (key == 'Authorization')
|
||||
this._log.debug("HTTP Header " + key + ": ***** (suppressed)");
|
||||
else
|
||||
this._log.debug("HTTP Header " + key + ": " + headers[key]);
|
||||
request.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
|
||||
this._authProvider._authFailed = false;
|
||||
request.channel.notificationCallbacks = this._authProvider;
|
||||
|
||||
request.send(data);
|
||||
let event = yield;
|
||||
ret = event.target;
|
||||
|
||||
if (this._authProvider._authFailed)
|
||||
this._log.warn("_makeRequest: authentication failed");
|
||||
if (ret.status < 200 || ret.status >= 300)
|
||||
this._log.warn("_makeRequest: got status " + ret.status);
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
Utils.generatorDone(this, self, onComplete, ret);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
let key;
|
||||
for (key in headers) {
|
||||
if (key == 'Authorization')
|
||||
this._log.debug("HTTP Header " + key + ": ***** (suppressed)");
|
||||
else
|
||||
this._log.debug("HTTP Header " + key + ": " + headers[key]);
|
||||
request.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
this._authProvider._authFailed = false;
|
||||
request.channel.notificationCallbacks = this._authProvider;
|
||||
|
||||
request.send(data);
|
||||
let event = yield;
|
||||
ret = event.target;
|
||||
|
||||
if (this._authProvider._authFailed)
|
||||
this._log.warn("_makeRequest: authentication failed");
|
||||
if (ret.status < 200 || ret.status >= 300)
|
||||
this._log.warn("_makeRequest: got status " + ret.status);
|
||||
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
get _defaultHeaders() {
|
||||
|
@ -142,52 +135,52 @@ DAVCollection.prototype = {
|
|||
},
|
||||
|
||||
// mkdir -p
|
||||
_mkcol: function DC__mkcol(path, onComplete) {
|
||||
let [self, cont] = yield;
|
||||
let ret;
|
||||
_mkcol: function DC__mkcol(path) {
|
||||
let self = yield;
|
||||
let ok = true;
|
||||
|
||||
try {
|
||||
let components = path.split('/');
|
||||
let path2 = '';
|
||||
|
||||
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
|
||||
// trailing slashes will cause an empty path component at the end
|
||||
if (components[i] == '')
|
||||
break;
|
||||
|
||||
path2 = path2 + components[i];
|
||||
|
||||
// check if it exists first
|
||||
this._makeRequest.async(this, cont, "GET", path2 + "/", this._defaultHeaders);
|
||||
ret = yield;
|
||||
if (!(ret.status == 404 || ret.status == 500)) { // FIXME: 500 is a services.m.c oddity
|
||||
this._log.debug("Skipping creation of path " + path2 +
|
||||
" (got status " + ret.status + ")");
|
||||
} else {
|
||||
this._log.debug("Creating path: " + path2);
|
||||
let gen = this._makeRequest.async(this, cont, "MKCOL", path2,
|
||||
this._defaultHeaders);
|
||||
ret = yield;
|
||||
|
||||
if (ret.status != 201) {
|
||||
this._log.debug(ret.responseText);
|
||||
throw 'request failed: ' + ret.status;
|
||||
}
|
||||
}
|
||||
|
||||
// add slash *after* the request, trailing slashes cause a 412!
|
||||
path2 = path2 + "/";
|
||||
|
||||
// trailing slashes will cause an empty path component at the end
|
||||
if (components[i] == '')
|
||||
break;
|
||||
|
||||
path2 = path2 + components[i];
|
||||
|
||||
// check if it exists first
|
||||
this._makeRequest.async(this, self.cb, "GET", path2 + "/", this._defaultHeaders);
|
||||
let ret = yield;
|
||||
if (!(ret.status == 404 || ret.status == 500)) { // FIXME: 500 is a services.m.c oddity
|
||||
this._log.debug("Skipping creation of path " + path2 +
|
||||
" (got status " + ret.status + ")");
|
||||
} else {
|
||||
this._log.debug("Creating path: " + path2);
|
||||
this._makeRequest.async(this, self.cb, "MKCOL", path2,
|
||||
this._defaultHeaders);
|
||||
ret = yield;
|
||||
|
||||
if (ret.status != 201) {
|
||||
this._log.debug(ret.responseText);
|
||||
throw 'request failed: ' + ret.status;
|
||||
}
|
||||
}
|
||||
|
||||
// add slash *after* the request, trailing slashes cause a 412!
|
||||
path2 = path2 + "/";
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
Utils.generatorDone(this, self, onComplete, ret);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
this._log.error("Could not create directory on server");
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e) +
|
||||
" - " + (e.location? e.location : ""));
|
||||
ok = false;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
self.done(ok);
|
||||
},
|
||||
|
||||
GET: function DC_GET(path, onComplete) {
|
||||
|
@ -206,7 +199,7 @@ DAVCollection.prototype = {
|
|||
},
|
||||
|
||||
MKCOL: function DC_MKCOL(path, onComplete) {
|
||||
return this._mkcol.async(this, path, onComplete);
|
||||
return this._mkcol.async(this, onComplete, path);
|
||||
},
|
||||
|
||||
PROPFIND: function DC_PROPFIND(path, data, onComplete) {
|
||||
|
@ -233,37 +226,33 @@ DAVCollection.prototype = {
|
|||
|
||||
// Login / Logout
|
||||
|
||||
login: function DC_login(onComplete, username, password) {
|
||||
let [self, cont] = yield;
|
||||
login: function DC_login(username, password) {
|
||||
let self = yield;
|
||||
|
||||
try {
|
||||
if (this._loggedIn) {
|
||||
this._log.debug("Login requested, but already logged in");
|
||||
return;
|
||||
}
|
||||
|
||||
this._log.info("Logging in");
|
||||
|
||||
let URI = Utils.makeURI(this._baseURL);
|
||||
this._auth = "Basic " + btoa(username + ":" + password);
|
||||
|
||||
// Make a call to make sure it's working
|
||||
this.GET("", cont);
|
||||
let resp = yield;
|
||||
|
||||
if (this._authProvider._authFailed || resp.status < 200 || resp.status >= 300)
|
||||
return;
|
||||
|
||||
this._loggedIn = true;
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
Utils.generatorDone(this, self, onComplete, this._loggedIn);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
if (this._loggedIn) {
|
||||
this._log.debug("Login requested, but already logged in");
|
||||
self.done(true);
|
||||
yield;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
this._log.info("Logging in");
|
||||
|
||||
let URI = Utils.makeURI(this._baseURL);
|
||||
this._auth = "Basic " + btoa(username + ":" + password);
|
||||
|
||||
// Make a call to make sure it's working
|
||||
this.GET("", self.cb);
|
||||
let resp = yield;
|
||||
|
||||
if (this._authProvider._authFailed ||
|
||||
resp.status < 200 || resp.status >= 300) {
|
||||
self.done(false);
|
||||
yield;
|
||||
}
|
||||
|
||||
this._loggedIn = true;
|
||||
|
||||
self.done(true);
|
||||
},
|
||||
|
||||
logout: function DC_logout() {
|
||||
|
@ -274,170 +263,143 @@ DAVCollection.prototype = {
|
|||
|
||||
// Locking
|
||||
|
||||
_getActiveLock: function DC__getActiveLock(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
_getActiveLock: function DC__getActiveLock() {
|
||||
let self = yield;
|
||||
let ret = null;
|
||||
|
||||
try {
|
||||
this._log.info("Getting active lock token");
|
||||
this.PROPFIND("",
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
|
||||
"<D:propfind xmlns:D='DAV:'>" +
|
||||
" <D:prop><D:lockdiscovery/></D:prop>" +
|
||||
"</D:propfind>", cont);
|
||||
let resp = yield;
|
||||
this._log.info("Getting active lock token");
|
||||
this.PROPFIND("",
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
|
||||
"<D:propfind xmlns:D='DAV:'>" +
|
||||
" <D:prop><D:lockdiscovery/></D:prop>" +
|
||||
"</D:propfind>", self.cb);
|
||||
let resp = yield;
|
||||
|
||||
if (this._authProvider._authFailed || resp.status < 200 || resp.status >= 300)
|
||||
return;
|
||||
|
||||
let tokens = Utils.xpath(resp.responseXML, '//D:locktoken/D:href');
|
||||
let token = tokens.iterateNext();
|
||||
ret = token.textContent;
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
if (ret)
|
||||
this._log.debug("Found an active lock token");
|
||||
else
|
||||
this._log.debug("No active lock token found");
|
||||
Utils.generatorDone(this, self, onComplete, ret);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
if (this._authProvider._authFailed ||
|
||||
resp.status < 200 || resp.status >= 300) {
|
||||
self.done(false);
|
||||
yield;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
let tokens = Utils.xpath(resp.responseXML, '//D:locktoken/D:href');
|
||||
let token = tokens.iterateNext();
|
||||
ret = token.textContent;
|
||||
|
||||
if (ret)
|
||||
this._log.debug("Found an active lock token");
|
||||
else
|
||||
this._log.debug("No active lock token found");
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
lock: function DC_lock(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
lock: function DC_lock() {
|
||||
let self = yield;
|
||||
this._token = null;
|
||||
|
||||
try {
|
||||
this._log.info("Acquiring lock");
|
||||
this._log.info("Acquiring lock");
|
||||
|
||||
if (this._token) {
|
||||
this._log.debug("Lock called, but we already hold a token");
|
||||
return;
|
||||
}
|
||||
|
||||
this.LOCK("",
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
|
||||
"<D:lockinfo xmlns:D=\"DAV:\">\n" +
|
||||
" <D:locktype><D:write/></D:locktype>\n" +
|
||||
" <D:lockscope><D:exclusive/></D:lockscope>\n" +
|
||||
"</D:lockinfo>", cont);
|
||||
let resp = yield;
|
||||
|
||||
if (this._authProvider._authFailed || resp.status < 200 || resp.status >= 300)
|
||||
return;
|
||||
|
||||
let tokens = Utils.xpath(resp.responseXML, '//D:locktoken/D:href');
|
||||
let token = tokens.iterateNext();
|
||||
if (token)
|
||||
this._token = token.textContent;
|
||||
|
||||
} catch (e){
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
if (this._token)
|
||||
this._log.info("Lock acquired");
|
||||
else
|
||||
this._log.warn("Could not acquire lock");
|
||||
Utils.generatorDone(this, self, onComplete, this._token);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
if (this._token) {
|
||||
this._log.debug("Lock called, but we already hold a token");
|
||||
self.done(this._token);
|
||||
yield;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
this.LOCK("",
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" +
|
||||
"<D:lockinfo xmlns:D=\"DAV:\">\n" +
|
||||
" <D:locktype><D:write/></D:locktype>\n" +
|
||||
" <D:lockscope><D:exclusive/></D:lockscope>\n" +
|
||||
"</D:lockinfo>", self.cb);
|
||||
let resp = yield;
|
||||
|
||||
if (this._authProvider._authFailed ||
|
||||
resp.status < 200 || resp.status >= 300) {
|
||||
self.done(this._token);
|
||||
yield;
|
||||
}
|
||||
|
||||
let tokens = Utils.xpath(resp.responseXML, '//D:locktoken/D:href');
|
||||
let token = tokens.iterateNext();
|
||||
if (token)
|
||||
this._token = token.textContent;
|
||||
|
||||
if (this._token)
|
||||
this._log.debug("Lock acquired");
|
||||
else
|
||||
this._log.warn("Could not acquire lock");
|
||||
|
||||
self.done(this._token);
|
||||
},
|
||||
|
||||
unlock: function DC_unlock(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
try {
|
||||
this._log.info("Releasing lock");
|
||||
unlock: function DC_unlock() {
|
||||
let self = yield;
|
||||
|
||||
if (this._token === null) {
|
||||
this._log.debug("Unlock called, but we don't hold a token right now");
|
||||
return;
|
||||
}
|
||||
this._log.info("Releasing lock");
|
||||
|
||||
this.UNLOCK("", cont);
|
||||
let resp = yield;
|
||||
|
||||
if (this._authProvider._authFailed || resp.status < 200 || resp.status >= 300)
|
||||
return;
|
||||
|
||||
this._token = null;
|
||||
|
||||
} catch (e){
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
if (this._token) {
|
||||
this._log.info("Could not release lock");
|
||||
Utils.generatorDone(this, self, onComplete, false);
|
||||
} else {
|
||||
this._log.info("Lock released (or we didn't have one)");
|
||||
Utils.generatorDone(this, self, onComplete, true);
|
||||
}
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
if (this._token === null) {
|
||||
this._log.debug("Unlock called, but we don't hold a token right now");
|
||||
self.done(true);
|
||||
yield;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
this.UNLOCK("", self.cb);
|
||||
let resp = yield;
|
||||
|
||||
if (this._authProvider._authFailed ||
|
||||
resp.status < 200 || resp.status >= 300) {
|
||||
self.done(false);
|
||||
yield;
|
||||
}
|
||||
|
||||
this._token = null;
|
||||
|
||||
if (this._token)
|
||||
this._log.info("Could not release lock");
|
||||
else
|
||||
this._log.info("Lock released (or we didn't have one)");
|
||||
|
||||
self.done(!this._token);
|
||||
},
|
||||
|
||||
forceUnlock: function DC_forceUnlock(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
forceUnlock: function DC_forceUnlock() {
|
||||
let self = yield;
|
||||
let unlocked = true;
|
||||
|
||||
try {
|
||||
this._log.info("Forcibly releasing any server locks");
|
||||
this._log.info("Forcibly releasing any server locks");
|
||||
|
||||
this._getActiveLock.async(this, cont);
|
||||
this._token = yield;
|
||||
this._getActiveLock.async(this, self.cb);
|
||||
this._token = yield;
|
||||
|
||||
if (!this._token) {
|
||||
this._log.info("No server lock found");
|
||||
return;
|
||||
}
|
||||
|
||||
this._log.info("Server lock found, unlocking");
|
||||
this.unlock.async(this, cont);
|
||||
unlocked = yield;
|
||||
|
||||
} catch (e){
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
if (unlocked)
|
||||
this._log.debug("Lock released");
|
||||
else
|
||||
this._log.debug("No lock released");
|
||||
Utils.generatorDone(this, self, onComplete, unlocked);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
if (!this._token) {
|
||||
this._log.info("No server lock found");
|
||||
self.done(true);
|
||||
yield;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
this._log.info("Server lock found, unlocking");
|
||||
this.unlock.async(this, self.cb);
|
||||
unlocked = yield;
|
||||
|
||||
if (unlocked)
|
||||
this._log.debug("Lock released");
|
||||
else
|
||||
this._log.debug("No lock released");
|
||||
self.done(unlocked);
|
||||
},
|
||||
|
||||
stealLock: function DC_stealLock(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
stealLock: function DC_stealLock() {
|
||||
let self = yield;
|
||||
let stolen = null;
|
||||
|
||||
try {
|
||||
this.forceUnlock.async(this, cont);
|
||||
let unlocked = yield;
|
||||
this.forceUnlock.async(this, self.cb);
|
||||
let unlocked = yield;
|
||||
|
||||
if (unlocked) {
|
||||
this.lock.async(this, cont);
|
||||
stolen = yield;
|
||||
}
|
||||
|
||||
} catch (e){
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
Utils.generatorDone(this, self, onComplete, stolen);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
if (unlocked) {
|
||||
this.lock.async(this, self.cb);
|
||||
stolen = yield;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
self.done(stolen);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -48,8 +48,9 @@ Cu.import("resource://weave/util.js");
|
|||
Cu.import("resource://weave/crypto.js");
|
||||
Cu.import("resource://weave/stores.js");
|
||||
Cu.import("resource://weave/syncCores.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
|
||||
Function.prototype.async = Utils.generatorAsync;
|
||||
Function.prototype.async = Async.sugar;
|
||||
let Crypto = new WeaveCrypto();
|
||||
|
||||
function Engine(davCollection, cryptoId) {
|
||||
|
@ -68,6 +69,7 @@ Engine.prototype = {
|
|||
// These can be overridden in subclasses, but don't need to be (assuming
|
||||
// serverPrefix is not shared with anything else)
|
||||
get statusFile() { return this.serverPrefix + "status.json"; },
|
||||
get keysFile() { return this.serverPrefix + "keys.json"; },
|
||||
get snapshotFile() { return this.serverPrefix + "snapshot.json"; },
|
||||
get deltasFile() { return this.serverPrefix + "deltas.json"; },
|
||||
|
||||
|
@ -120,6 +122,29 @@ Engine.prototype = {
|
|||
this._snapshot.load();
|
||||
},
|
||||
|
||||
_getSymKey: function Engine__getCryptoId(cryptoId) {
|
||||
let self = yield;
|
||||
let done = false;
|
||||
|
||||
this._dav.GET(this.keysFile, self.cb);
|
||||
let keysResp = yield;
|
||||
Utils.ensureStatus(keysResp.status,
|
||||
"Could not get keys file.", [[200,300]]);
|
||||
let keys = keysResp.responseText;
|
||||
|
||||
if (!keys[this._userHash]) {
|
||||
this._log.error("Keyring does not contain a key for this user");
|
||||
return;
|
||||
}
|
||||
|
||||
//Crypto.RSAdecrypt.async(Crypto, self.cb,
|
||||
// keys[this._userHash],
|
||||
|
||||
self.done(done);
|
||||
yield;
|
||||
this._log.warn("_getSymKey generator not properly closed");
|
||||
},
|
||||
|
||||
_serializeCommands: function Engine__serializeCommands(commands) {
|
||||
let json = this._json.encode(commands);
|
||||
//json = json.replace(/ {action/g, "\n {action");
|
||||
|
@ -132,64 +157,52 @@ Engine.prototype = {
|
|||
return json;
|
||||
},
|
||||
|
||||
_resetServer: function Engine__resetServer(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
_resetServer: function Engine__resetServer() {
|
||||
let self = yield;
|
||||
let done = false;
|
||||
|
||||
try {
|
||||
this._log.debug("Resetting server data");
|
||||
this._os.notifyObservers(null, this._osPrefix + "reset-server:start", "");
|
||||
|
||||
this._dav.lock.async(this._dav, cont);
|
||||
this._dav.lock.async(this._dav, self.cb);
|
||||
let locked = yield;
|
||||
if (locked)
|
||||
this._log.debug("Lock acquired");
|
||||
else {
|
||||
this._log.warn("Could not acquire lock, aborting server reset");
|
||||
return;
|
||||
}
|
||||
if (!locked)
|
||||
throw "Could not acquire lock, aborting server reset";
|
||||
|
||||
// try to delete all 3, check status after
|
||||
this._dav.DELETE(this.statusFile, cont);
|
||||
this._dav.DELETE(this.statusFile, self.cb);
|
||||
let statusResp = yield;
|
||||
this._dav.DELETE(this.snapshotFile, cont);
|
||||
this._dav.DELETE(this.snapshotFile, self.cb);
|
||||
let snapshotResp = yield;
|
||||
this._dav.DELETE(this.deltasFile, cont);
|
||||
this._dav.DELETE(this.deltasFile, self.cb);
|
||||
let deltasResp = yield;
|
||||
|
||||
this._dav.unlock.async(this._dav, cont);
|
||||
this._dav.unlock.async(this._dav, self.cb);
|
||||
let unlocked = yield;
|
||||
|
||||
Utils.checkStatus(statusResp.status,
|
||||
"Could not delete status file.", [[200,300],404]);
|
||||
Utils.checkStatus(snapshotResp.status,
|
||||
"Could not delete snapshot file.", [[200,300],404]);
|
||||
Utils.checkStatus(deltasResp.status,
|
||||
"Could not delete deltas file.", [[200,300],404]);
|
||||
Utils.ensureStatus(statusResp.status,
|
||||
"Could not delete status file.", [[200,300],404]);
|
||||
Utils.ensureStatus(snapshotResp.status,
|
||||
"Could not delete snapshot file.", [[200,300],404]);
|
||||
Utils.ensureStatus(deltasResp.status,
|
||||
"Could not delete deltas file.", [[200,300],404]);
|
||||
|
||||
this._log.debug("Server files deleted");
|
||||
done = true;
|
||||
this._os.notifyObservers(null, this._osPrefix + "reset-server:success", "");
|
||||
|
||||
} catch (e) {
|
||||
if (e != 'checkStatus failed')
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
if (done) {
|
||||
this._log.debug("Server reset completed successfully");
|
||||
this._os.notifyObservers(null, this._osPrefix + "reset-server:success", "");
|
||||
} else {
|
||||
this._log.debug("Server reset failed");
|
||||
this._os.notifyObservers(null, this._osPrefix + "reset-server:error", "");
|
||||
}
|
||||
Utils.generatorDone(this, self, onComplete, done)
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
this._log.error("Could not delete server files");
|
||||
this._os.notifyObservers(null, this._osPrefix + "reset-server:error", "");
|
||||
throw e;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
self.done(done)
|
||||
},
|
||||
|
||||
_resetClient: function Engine__resetClient(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
_resetClient: function Engine__resetClient() {
|
||||
let self = yield;
|
||||
let done = false;
|
||||
|
||||
try {
|
||||
|
@ -201,7 +214,7 @@ Engine.prototype = {
|
|||
done = true;
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
if (done) {
|
||||
|
@ -211,10 +224,8 @@ Engine.prototype = {
|
|||
this._log.debug("Client reset failed");
|
||||
this._os.notifyObservers(null, this._osPrefix + "reset-client:error", "");
|
||||
}
|
||||
Utils.generatorDone(this, self, onComplete, done);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
self.done(done);
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
},
|
||||
|
||||
// original
|
||||
|
@ -245,15 +256,15 @@ Engine.prototype = {
|
|||
// 3.1) Apply local delta with server changes ("D")
|
||||
// 3.2) Append server delta to the delta file and upload ("C")
|
||||
|
||||
_sync: function BmkEngine__sync(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
_sync: function BmkEngine__sync() {
|
||||
let self = yield;
|
||||
let synced = false, locked = null;
|
||||
|
||||
try {
|
||||
this._log.info("Beginning sync");
|
||||
this._os.notifyObservers(null, this._osPrefix + "sync:start", "");
|
||||
|
||||
this._dav.lock.async(this._dav, cont);
|
||||
this._dav.lock.async(this._dav, self.cb);
|
||||
locked = yield;
|
||||
|
||||
if (locked)
|
||||
|
@ -264,12 +275,13 @@ Engine.prototype = {
|
|||
}
|
||||
|
||||
// Before we get started, make sure we have a remote directory to play in
|
||||
this._dav.MKCOL(this.serverPrefix, cont);
|
||||
this._dav.MKCOL(this.serverPrefix, self.cb);
|
||||
let ret = yield;
|
||||
Utils.checkStatus(ret.status, "Could not create remote folder.");
|
||||
if (!ret)
|
||||
throw "Could not create remote folder";
|
||||
|
||||
// 1) Fetch server deltas
|
||||
this._getServerData.async(this, cont);
|
||||
this._getServerData.async(this, self.cb);
|
||||
let server = yield;
|
||||
|
||||
this._log.info("Local snapshot version: " + this._snapshot.version);
|
||||
|
@ -287,7 +299,7 @@ Engine.prototype = {
|
|||
|
||||
let localJson = new SnapshotStore();
|
||||
localJson.data = this._store.wrap();
|
||||
this._core.detectUpdates(cont, this._snapshot.data, localJson.data);
|
||||
this._core.detectUpdates(self.cb, this._snapshot.data, localJson.data);
|
||||
let localUpdates = yield;
|
||||
|
||||
this._log.trace("local json:\n" + localJson.serialize());
|
||||
|
@ -304,7 +316,7 @@ Engine.prototype = {
|
|||
// 3) Reconcile client/server deltas and generate new deltas for them.
|
||||
|
||||
this._log.info("Reconciling client/server updates");
|
||||
this._core.reconcile(cont, localUpdates, server.updates);
|
||||
this._core.reconcile(self.cb, localUpdates, server.updates);
|
||||
ret = yield;
|
||||
|
||||
let clientChanges = ret.propagations[0];
|
||||
|
@ -351,7 +363,7 @@ Engine.prototype = {
|
|||
this._store.applyCommands(clientChanges);
|
||||
newSnapshot = this._store.wrap();
|
||||
|
||||
this._core.detectUpdates(cont, this._snapshot.data, newSnapshot);
|
||||
this._core.detectUpdates(self.cb, this._snapshot.data, newSnapshot);
|
||||
let diff = yield;
|
||||
if (diff.length != 0) {
|
||||
this._log.warn("Commands did not apply correctly");
|
||||
|
@ -372,7 +384,7 @@ Engine.prototype = {
|
|||
// conflicts, it should be the same as what the resolver returned
|
||||
|
||||
newSnapshot = this._store.wrap();
|
||||
this._core.detectUpdates(cont, server.snapshot, newSnapshot);
|
||||
this._core.detectUpdates(self.cb, server.snapshot, newSnapshot);
|
||||
let serverDelta = yield;
|
||||
|
||||
// Log an error if not the same
|
||||
|
@ -395,17 +407,17 @@ Engine.prototype = {
|
|||
|
||||
if (server.formatVersion != STORAGE_FORMAT_VERSION ||
|
||||
this._encryptionChanged) {
|
||||
this._fullUpload.async(this, cont);
|
||||
this._fullUpload.async(this, self.cb);
|
||||
let status = yield;
|
||||
if (!status)
|
||||
this._log.error("Could not upload files to server"); // eep?
|
||||
|
||||
} else {
|
||||
Crypto.PBEencrypt.async(Crypto, cont,
|
||||
Crypto.PBEencrypt.async(Crypto, self.cb,
|
||||
this._serializeCommands(server.deltas),
|
||||
this._cryptoId);
|
||||
let data = yield;
|
||||
this._dav.PUT(this.deltasFile, data, cont);
|
||||
this._dav.PUT(this.deltasFile, data, self.cb);
|
||||
let deltasPut = yield;
|
||||
|
||||
let c = 0;
|
||||
|
@ -420,7 +432,7 @@ Engine.prototype = {
|
|||
maxVersion: this._snapshot.version,
|
||||
snapEncryption: server.snapEncryption,
|
||||
deltasEncryption: Crypto.defaultAlgorithm,
|
||||
itemCount: c}), cont);
|
||||
itemCount: c}), self.cb);
|
||||
let statusPut = yield;
|
||||
|
||||
if (deltasPut.status >= 200 && deltasPut.status < 300 &&
|
||||
|
@ -439,24 +451,22 @@ Engine.prototype = {
|
|||
synced = true;
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
let ok = false;
|
||||
if (locked) {
|
||||
this._dav.unlock.async(this._dav, cont);
|
||||
this._dav.unlock.async(this._dav, self.cb);
|
||||
ok = yield;
|
||||
}
|
||||
if (ok && synced) {
|
||||
this._os.notifyObservers(null, this._osPrefix + "sync:success", "");
|
||||
Utils.generatorDone(this, self, onComplete, true);
|
||||
self.done(true);
|
||||
} else {
|
||||
this._os.notifyObservers(null, this._osPrefix + "sync:error", "");
|
||||
Utils.generatorDone(this, self, onComplete, false);
|
||||
self.done(false);
|
||||
}
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
},
|
||||
|
||||
/* Get the deltas/combined updates from the server
|
||||
|
@ -483,220 +493,205 @@ Engine.prototype = {
|
|||
* the relevant deltas (from our snapshot version to current),
|
||||
* combined into a single set.
|
||||
*/
|
||||
_getServerData: function BmkEngine__getServerData(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
_getServerData: function BmkEngine__getServerData() {
|
||||
let self = yield;
|
||||
let ret = {status: -1,
|
||||
formatVersion: null, maxVersion: null, snapVersion: null,
|
||||
snapEncryption: null, deltasEncryption: null,
|
||||
snapshot: null, deltas: null, updates: null};
|
||||
|
||||
try {
|
||||
this._log.debug("Getting status file from server");
|
||||
this._dav.GET(this.statusFile, cont);
|
||||
let resp = yield;
|
||||
let status = resp.status;
|
||||
|
||||
switch (status) {
|
||||
case 200: {
|
||||
this._log.info("Got status file from server");
|
||||
|
||||
let status = this._json.decode(resp.responseText);
|
||||
let deltas, allDeltas;
|
||||
let snap = new SnapshotStore();
|
||||
|
||||
// Bail out if the server has a newer format version than we can parse
|
||||
if (status.formatVersion > STORAGE_FORMAT_VERSION) {
|
||||
this._log.error("Server uses storage format v" + status.formatVersion +
|
||||
", this client understands up to v" + STORAGE_FORMAT_VERSION);
|
||||
Utils.generatorDone(this, self, onComplete, ret)
|
||||
return;
|
||||
}
|
||||
this._log.debug("Getting status file from server");
|
||||
this._dav.GET(this.statusFile, self.cb);
|
||||
let resp = yield;
|
||||
let status = resp.status;
|
||||
|
||||
if (status.formatVersion == 0) {
|
||||
ret.snapEncryption = status.snapEncryption = "none";
|
||||
ret.deltasEncryption = status.deltasEncryption = "none";
|
||||
}
|
||||
|
||||
if (status.GUID != this._snapshot.GUID) {
|
||||
this._log.info("Remote/local sync GUIDs do not match. " +
|
||||
"Forcing initial sync.");
|
||||
this._log.debug("Remote: " + status.GUID);
|
||||
this._log.debug("Local: " + this._snapshot.GUID);
|
||||
this._store.resetGUIDs();
|
||||
this._snapshot.data = {};
|
||||
this._snapshot.version = -1;
|
||||
this._snapshot.GUID = status.GUID;
|
||||
}
|
||||
|
||||
if (this._snapshot.version < status.snapVersion) {
|
||||
if (this._snapshot.version >= 0)
|
||||
this._log.info("Local snapshot is out of date");
|
||||
|
||||
this._log.info("Downloading server snapshot");
|
||||
this._dav.GET(this.snapshotFile, cont);
|
||||
resp = yield;
|
||||
Utils.checkStatus(resp.status, "Could not download snapshot.");
|
||||
Crypto.PBEdecrypt.async(Crypto, cont,
|
||||
resp.responseText,
|
||||
this._cryptoId,
|
||||
status.snapEncryption);
|
||||
let data = yield;
|
||||
snap.data = this._json.decode(data);
|
||||
switch (status) {
|
||||
case 200: {
|
||||
this._log.info("Got status file from server");
|
||||
|
||||
this._log.info("Downloading server deltas");
|
||||
this._dav.GET(this.deltasFile, cont);
|
||||
resp = yield;
|
||||
Utils.checkStatus(resp.status, "Could not download deltas.");
|
||||
Crypto.PBEdecrypt.async(Crypto, cont,
|
||||
resp.responseText,
|
||||
this._cryptoId,
|
||||
status.deltasEncryption);
|
||||
data = yield;
|
||||
allDeltas = this._json.decode(data);
|
||||
deltas = this._json.decode(data);
|
||||
|
||||
} else if (this._snapshot.version >= status.snapVersion &&
|
||||
this._snapshot.version < status.maxVersion) {
|
||||
snap.data = Utils.deepCopy(this._snapshot.data);
|
||||
|
||||
this._log.info("Downloading server deltas");
|
||||
this._dav.GET(this.deltasFile, cont);
|
||||
resp = yield;
|
||||
Utils.checkStatus(resp.status, "Could not download deltas.");
|
||||
Crypto.PBEdecrypt.async(Crypto, cont,
|
||||
resp.responseText,
|
||||
this._cryptoId,
|
||||
status.deltasEncryption);
|
||||
let data = yield;
|
||||
allDeltas = this._json.decode(data);
|
||||
deltas = allDeltas.slice(this._snapshot.version - status.snapVersion);
|
||||
|
||||
} else if (this._snapshot.version == status.maxVersion) {
|
||||
snap.data = Utils.deepCopy(this._snapshot.data);
|
||||
let status = this._json.decode(resp.responseText);
|
||||
let deltas, allDeltas;
|
||||
let snap = new SnapshotStore();
|
||||
|
||||
// FIXME: could optimize this case by caching deltas file
|
||||
this._log.info("Downloading server deltas");
|
||||
this._dav.GET(this.deltasFile, cont);
|
||||
resp = yield;
|
||||
Utils.checkStatus(resp.status, "Could not download deltas.");
|
||||
Crypto.PBEdecrypt.async(Crypto, cont,
|
||||
resp.responseText,
|
||||
this._cryptoId,
|
||||
status.deltasEncryption);
|
||||
let data = yield;
|
||||
allDeltas = this._json.decode(data);
|
||||
deltas = [];
|
||||
|
||||
} else { // this._snapshot.version > status.maxVersion
|
||||
this._log.error("Server snapshot is older than local snapshot");
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < deltas.length; i++) {
|
||||
snap.applyCommands(deltas[i]);
|
||||
}
|
||||
|
||||
ret.status = 0;
|
||||
ret.formatVersion = status.formatVersion;
|
||||
ret.maxVersion = status.maxVersion;
|
||||
ret.snapVersion = status.snapVersion;
|
||||
ret.snapEncryption = status.snapEncryption;
|
||||
ret.deltasEncryption = status.deltasEncryption;
|
||||
ret.snapshot = snap.data;
|
||||
ret.deltas = allDeltas;
|
||||
this._core.detectUpdates(cont, this._snapshot.data, snap.data);
|
||||
ret.updates = yield;
|
||||
} break;
|
||||
|
||||
case 404: {
|
||||
this._log.info("Server has no status file, Initial upload to server");
|
||||
|
||||
this._snapshot.data = this._store.wrap();
|
||||
this._snapshot.version = 0;
|
||||
this._snapshot.GUID = null; // in case there are other snapshots out there
|
||||
|
||||
this._fullUpload.async(this, cont);
|
||||
let uploadStatus = yield;
|
||||
if (!uploadStatus)
|
||||
return;
|
||||
|
||||
this._log.info("Initial upload to server successful");
|
||||
this._snapshot.save();
|
||||
|
||||
ret.status = 0;
|
||||
ret.formatVersion = STORAGE_FORMAT_VERSION;
|
||||
ret.maxVersion = this._snapshot.version;
|
||||
ret.snapVersion = this._snapshot.version;
|
||||
ret.snapEncryption = Crypto.defaultAlgorithm;
|
||||
ret.deltasEncryption = Crypto.defaultAlgorithm;
|
||||
ret.snapshot = Utils.deepCopy(this._snapshot.data);
|
||||
ret.deltas = [];
|
||||
ret.updates = [];
|
||||
} break;
|
||||
|
||||
default:
|
||||
this._log.error("Could not get status file: unknown HTTP status code " +
|
||||
status);
|
||||
// Bail out if the server has a newer format version than we can parse
|
||||
if (status.formatVersion > STORAGE_FORMAT_VERSION) {
|
||||
this._log.error("Server uses storage format v" + status.formatVersion +
|
||||
", this client understands up to v" + STORAGE_FORMAT_VERSION);
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
if (e != 'checkStatus failed' &&
|
||||
e != 'decrypt failed')
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
if (status.formatVersion == 0) {
|
||||
ret.snapEncryption = status.snapEncryption = "none";
|
||||
ret.deltasEncryption = status.deltasEncryption = "none";
|
||||
}
|
||||
|
||||
} finally {
|
||||
Utils.generatorDone(this, self, onComplete, ret)
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
if (status.GUID != this._snapshot.GUID) {
|
||||
this._log.info("Remote/local sync GUIDs do not match. " +
|
||||
"Forcing initial sync.");
|
||||
this._log.debug("Remote: " + status.GUID);
|
||||
this._log.debug("Local: " + this._snapshot.GUID);
|
||||
this._store.resetGUIDs();
|
||||
this._snapshot.data = {};
|
||||
this._snapshot.version = -1;
|
||||
this._snapshot.GUID = status.GUID;
|
||||
}
|
||||
|
||||
if (this._snapshot.version < status.snapVersion) {
|
||||
this._log.trace("Local snapshot version < server snapVersion");
|
||||
|
||||
if (this._snapshot.version >= 0)
|
||||
this._log.info("Local snapshot is out of date");
|
||||
|
||||
this._log.info("Downloading server snapshot");
|
||||
this._dav.GET(this.snapshotFile, self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not download snapshot.");
|
||||
Crypto.PBEdecrypt.async(Crypto, self.cb,
|
||||
resp.responseText,
|
||||
this._cryptoId,
|
||||
status.snapEncryption);
|
||||
let data = yield;
|
||||
snap.data = this._json.decode(data);
|
||||
|
||||
this._log.info("Downloading server deltas");
|
||||
this._dav.GET(this.deltasFile, self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not download deltas.");
|
||||
Crypto.PBEdecrypt.async(Crypto, self.cb,
|
||||
resp.responseText,
|
||||
this._cryptoId,
|
||||
status.deltasEncryption);
|
||||
data = yield;
|
||||
allDeltas = this._json.decode(data);
|
||||
deltas = this._json.decode(data);
|
||||
|
||||
} else if (this._snapshot.version >= status.snapVersion &&
|
||||
this._snapshot.version < status.maxVersion) {
|
||||
this._log.trace("Server snapVersion <= local snapshot version < server maxVersion");
|
||||
snap.data = Utils.deepCopy(this._snapshot.data);
|
||||
|
||||
this._log.info("Downloading server deltas");
|
||||
this._dav.GET(this.deltasFile, self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not download deltas.");
|
||||
Crypto.PBEdecrypt.async(Crypto, self.cb,
|
||||
resp.responseText,
|
||||
this._cryptoId,
|
||||
status.deltasEncryption);
|
||||
let data = yield;
|
||||
allDeltas = this._json.decode(data);
|
||||
deltas = allDeltas.slice(this._snapshot.version - status.snapVersion);
|
||||
|
||||
} else if (this._snapshot.version == status.maxVersion) {
|
||||
this._log.trace("Local snapshot version == server maxVersion");
|
||||
snap.data = Utils.deepCopy(this._snapshot.data);
|
||||
|
||||
// FIXME: could optimize this case by caching deltas file
|
||||
this._log.info("Downloading server deltas");
|
||||
this._dav.GET(this.deltasFile, self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not download deltas.");
|
||||
Crypto.PBEdecrypt.async(Crypto, self.cb,
|
||||
resp.responseText,
|
||||
this._cryptoId,
|
||||
status.deltasEncryption);
|
||||
let data = yield;
|
||||
allDeltas = this._json.decode(data);
|
||||
deltas = [];
|
||||
|
||||
} else { // this._snapshot.version > status.maxVersion
|
||||
this._log.error("Server snapshot is older than local snapshot");
|
||||
break;
|
||||
}
|
||||
|
||||
for (var i = 0; i < deltas.length; i++) {
|
||||
snap.applyCommands(deltas[i]);
|
||||
}
|
||||
|
||||
ret.status = 0;
|
||||
ret.formatVersion = status.formatVersion;
|
||||
ret.maxVersion = status.maxVersion;
|
||||
ret.snapVersion = status.snapVersion;
|
||||
ret.snapEncryption = status.snapEncryption;
|
||||
ret.deltasEncryption = status.deltasEncryption;
|
||||
ret.snapshot = snap.data;
|
||||
ret.deltas = allDeltas;
|
||||
this._core.detectUpdates(self.cb, this._snapshot.data, snap.data);
|
||||
ret.updates = yield;
|
||||
} break;
|
||||
|
||||
case 404: {
|
||||
this._log.info("Server has no status file, Initial upload to server");
|
||||
|
||||
this._snapshot.data = this._store.wrap();
|
||||
this._snapshot.version = 0;
|
||||
this._snapshot.GUID = null; // in case there are other snapshots out there
|
||||
|
||||
this._fullUpload.async(this, self.cb);
|
||||
let uploadStatus = yield;
|
||||
if (!uploadStatus)
|
||||
break;
|
||||
|
||||
this._log.info("Initial upload to server successful");
|
||||
this._snapshot.save();
|
||||
|
||||
ret.status = 0;
|
||||
ret.formatVersion = STORAGE_FORMAT_VERSION;
|
||||
ret.maxVersion = this._snapshot.version;
|
||||
ret.snapVersion = this._snapshot.version;
|
||||
ret.snapEncryption = Crypto.defaultAlgorithm;
|
||||
ret.deltasEncryption = Crypto.defaultAlgorithm;
|
||||
ret.snapshot = Utils.deepCopy(this._snapshot.data);
|
||||
ret.deltas = [];
|
||||
ret.updates = [];
|
||||
} break;
|
||||
|
||||
default:
|
||||
this._log.error("Could not get status file: unknown HTTP status code " +
|
||||
status);
|
||||
break;
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
|
||||
self.done(ret)
|
||||
},
|
||||
|
||||
_fullUpload: function Engine__fullUpload(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
_fullUpload: function Engine__fullUpload() {
|
||||
let self = yield;
|
||||
let ret = false;
|
||||
|
||||
try {
|
||||
Crypto.PBEencrypt.async(Crypto, cont,
|
||||
this._snapshot.serialize(),
|
||||
this._cryptoId);
|
||||
let data = yield;
|
||||
this._dav.PUT(this.snapshotFile, data, cont);
|
||||
resp = yield;
|
||||
Utils.checkStatus(resp.status, "Could not upload snapshot.");
|
||||
let gen = Crypto.PBEencrypt.async(Crypto, self.cb,
|
||||
this._snapshot.serialize(),
|
||||
this._cryptoId);
|
||||
let data = yield;
|
||||
if (gen.failed) throw "Encryption failed.";
|
||||
|
||||
this._dav.PUT(this.snapshotFile, data, self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not upload snapshot.");
|
||||
|
||||
this._dav.PUT(this.deltasFile, "[]", cont);
|
||||
resp = yield;
|
||||
Utils.checkStatus(resp.status, "Could not upload deltas.");
|
||||
this._dav.PUT(this.deltasFile, "[]", self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not upload deltas.");
|
||||
|
||||
let c = 0;
|
||||
for (GUID in this._snapshot.data)
|
||||
c++;
|
||||
let c = 0;
|
||||
for (GUID in this._snapshot.data)
|
||||
c++;
|
||||
|
||||
this._dav.PUT(this.statusFile,
|
||||
this._json.encode(
|
||||
{GUID: this._snapshot.GUID,
|
||||
formatVersion: STORAGE_FORMAT_VERSION,
|
||||
snapVersion: this._snapshot.version,
|
||||
maxVersion: this._snapshot.version,
|
||||
snapEncryption: Crypto.defaultAlgorithm,
|
||||
deltasEncryption: "none",
|
||||
itemCount: c}), cont);
|
||||
resp = yield;
|
||||
Utils.checkStatus(resp.status, "Could not upload status file.");
|
||||
this._dav.PUT(this.statusFile,
|
||||
this._json.encode(
|
||||
{GUID: this._snapshot.GUID,
|
||||
formatVersion: STORAGE_FORMAT_VERSION,
|
||||
snapVersion: this._snapshot.version,
|
||||
maxVersion: this._snapshot.version,
|
||||
snapEncryption: Crypto.defaultAlgorithm,
|
||||
deltasEncryption: "none",
|
||||
itemCount: c}), self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not upload status file.");
|
||||
|
||||
this._log.info("Full upload to server successful");
|
||||
ret = true;
|
||||
|
||||
} catch (e) {
|
||||
if (e != 'checkStatus failed')
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
Utils.generatorDone(this, self, onComplete, ret)
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
this._log.info("Full upload to server successful");
|
||||
ret = true;
|
||||
self.done(ret)
|
||||
},
|
||||
|
||||
sync: function Engine_sync(onComplete) {
|
||||
|
|
|
@ -66,6 +66,10 @@ Identity.prototype = {
|
|||
get username() { return this._username; },
|
||||
set username(value) { this._username = value; },
|
||||
|
||||
_key: null,
|
||||
get key() { return this._key; },
|
||||
set key(value) { this._key = value; },
|
||||
|
||||
_password: null,
|
||||
get password() {
|
||||
if (this._password === null)
|
||||
|
|
|
@ -478,7 +478,7 @@ Log4MozService.prototype = {
|
|||
return new FileAppender(file, formatter);
|
||||
case "rotating":
|
||||
// FIXME: hardcoded constants
|
||||
return new RotatingFileAppender(file, formatter, ONE_MEGABYTE * 5, 0);
|
||||
return new RotatingFileAppender(file, formatter, ONE_MEGABYTE * 2, 0);
|
||||
default:
|
||||
dump("log4moz: unknown appender kind: " + kind);
|
||||
return;
|
||||
|
|
|
@ -49,9 +49,10 @@ Cu.import("resource://weave/crypto.js");
|
|||
Cu.import("resource://weave/engines.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
Cu.import("resource://weave/identity.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
|
||||
|
||||
Function.prototype.async = Utils.generatorAsync;
|
||||
Function.prototype.async = Async.sugar;
|
||||
let Crypto = new WeaveCrypto();
|
||||
|
||||
/*
|
||||
* Service singleton
|
||||
|
@ -303,79 +304,103 @@ WeaveSyncService.prototype = {
|
|||
this._os.notifyObservers(null, "weave:service-unlock:success", "");
|
||||
},
|
||||
|
||||
_login: function WeaveSync__login(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
let success = false;
|
||||
_login: function WeaveSync__login() {
|
||||
let self = yield;
|
||||
|
||||
try {
|
||||
this._log.debug("Logging in");
|
||||
this._os.notifyObservers(null, "weave:service-login:start", "");
|
||||
|
||||
if (!this.username) {
|
||||
this._log.warn("No username set, login failed");
|
||||
return;
|
||||
}
|
||||
if (!this.password) {
|
||||
this._log.warn("No password given or found in password manager");
|
||||
return;
|
||||
}
|
||||
if (!this.username)
|
||||
throw "No username set, login failed";
|
||||
if (!this.password)
|
||||
throw "No password given or found in password manager";
|
||||
|
||||
let serverURL = this._prefs.getCharPref("serverURL");
|
||||
this._dav.baseURL = serverURL + "user/" + this.userPath + "/";
|
||||
this._log.info("Using server URL: " + this._dav.baseURL);
|
||||
|
||||
this._dav.login.async(this._dav, cont, this.username, this.password);
|
||||
success = yield;
|
||||
this._dav.login.async(this._dav, self.cb, this.username, this.password);
|
||||
let success = yield;
|
||||
|
||||
// FIXME: we want to limit this to when we get a 404!
|
||||
if (!success) {
|
||||
this._log.debug("Attempting to create user directory");
|
||||
|
||||
this._dav.baseURL = serverURL;
|
||||
this._dav.MKCOL("user/" + this.userPath, cont);
|
||||
this._dav.MKCOL("user/" + this.userPath, self.cb);
|
||||
let ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create user directory. Got status: " + ret.status;
|
||||
|
||||
if (ret.status == 201) {
|
||||
this._log.debug("Successfully created user directory. Re-attempting login.");
|
||||
this._dav.baseURL = serverURL + "user/" + this.userPath + "/";
|
||||
this._dav.login.async(this._dav, cont, this.username, this.password);
|
||||
success = yield;
|
||||
} else {
|
||||
this._log.debug("Could not create user directory. Got status: " + ret.status);
|
||||
}
|
||||
this._log.debug("Successfully created user directory. Re-attempting login.");
|
||||
this._dav.baseURL = serverURL + "user/" + this.userPath + "/";
|
||||
this._dav.login.async(this._dav, self.cb, this.username, this.password);
|
||||
success = yield;
|
||||
if (!success)
|
||||
throw "Created user directory, but login still failed. Aborting.";
|
||||
}
|
||||
|
||||
this._dav.GET("private/privkey", self.cb);
|
||||
let keyResp = yield;
|
||||
Utils.ensureStatus(keyResp.status,
|
||||
"Could not get private key from server", [[200,300],404]);
|
||||
|
||||
if (keyResp.status != 404) {
|
||||
this._cryptoId.key = keyResp.responseText;
|
||||
|
||||
} else {
|
||||
// generate a new key
|
||||
this._log.debug("Generating new RSA key");
|
||||
Crypto.RSAkeygen.async(Crypto, self.cb, this._cryptoId.password);
|
||||
let [privkey, pubkey] = yield;
|
||||
|
||||
this._cryptoId.key = privkey;
|
||||
|
||||
this._dav.MKCOL("private/", self.cb);
|
||||
ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create private key directory";
|
||||
|
||||
this._dav.MKCOL("public/", self.cb);
|
||||
ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create public key directory";
|
||||
|
||||
this._dav.PUT("private/privkey", privkey, self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not upload private key");
|
||||
|
||||
this._dav.PUT("public/pubkey", pubkey, self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not upload public key");
|
||||
}
|
||||
|
||||
this._passphrase = null;
|
||||
this._os.notifyObservers(null, "weave:service-login:success", "");
|
||||
self.done(true);
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + e.message);
|
||||
|
||||
} finally {
|
||||
this._passphrase = null;
|
||||
if (success) {
|
||||
//this._log.debug("Login successful"); // chrome prints this too, hm
|
||||
this._os.notifyObservers(null, "weave:service-login:success", "");
|
||||
} else {
|
||||
//this._log.debug("Login error");
|
||||
this._os.notifyObservers(null, "weave:service-login:error", "");
|
||||
}
|
||||
Utils.generatorDone(this, self, onComplete, success);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
this._log.warn(Async.exceptionStr(self, e));
|
||||
this._log.trace(e.trace);
|
||||
this._os.notifyObservers(null, "weave:service-login:error", "");
|
||||
self.done(false);
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
},
|
||||
|
||||
_resetLock: function WeaveSync__resetLock(onComplete) {
|
||||
let [self, cont] = yield;
|
||||
_resetLock: function WeaveSync__resetLock() {
|
||||
let self = yield;
|
||||
let success = false;
|
||||
|
||||
try {
|
||||
this._log.debug("Resetting server lock");
|
||||
this._os.notifyObservers(null, "weave:server-lock-reset:start", "");
|
||||
|
||||
this._dav.forceUnlock.async(this._dav, cont);
|
||||
this._dav.forceUnlock.async(this._dav, self.cb);
|
||||
success = yield;
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
if (success) {
|
||||
|
@ -385,14 +410,12 @@ WeaveSyncService.prototype = {
|
|||
this._log.debug("Server lock reset failed");
|
||||
this._os.notifyObservers(null, "weave:server-lock-reset:error", "");
|
||||
}
|
||||
Utils.generatorDone(this, self, onComplete, success);
|
||||
yield; // generatorDone is responsible for closing the generator
|
||||
self.done(success);
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
},
|
||||
|
||||
_sync: function WeaveSync__sync() {
|
||||
let [self, cont] = yield;
|
||||
let self = yield;
|
||||
let success = false;
|
||||
|
||||
try {
|
||||
|
@ -402,11 +425,11 @@ WeaveSyncService.prototype = {
|
|||
this._os.notifyObservers(null, "weave:service:sync:start", "");
|
||||
|
||||
if (this._prefs.getBoolPref("bookmarks")) {
|
||||
this._bmkEngine.sync(cont);
|
||||
this._bmkEngine.sync(self.cb);
|
||||
yield;
|
||||
}
|
||||
if (this._prefs.getBoolPref("history")) {
|
||||
this._histEngine.sync(cont);
|
||||
this._histEngine.sync(self.cb);
|
||||
yield;
|
||||
}
|
||||
|
||||
|
@ -414,61 +437,41 @@ WeaveSyncService.prototype = {
|
|||
this._unlock();
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
if (success)
|
||||
this._os.notifyObservers(null, "weave:service:sync:success", "");
|
||||
else
|
||||
this._os.notifyObservers(null, "weave:service:sync:error", "");
|
||||
Utils.generatorDone(this, self);
|
||||
yield; // generatorDone is responsible for closing the generator
|
||||
self.done();
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
},
|
||||
|
||||
_resetServer: function WeaveSync__resetServer() {
|
||||
let [self, cont] = yield;
|
||||
let self = yield;
|
||||
|
||||
try {
|
||||
if (!this._lock())
|
||||
return;
|
||||
if (!this._lock())
|
||||
return;
|
||||
|
||||
this._bmkEngine.resetServer(cont);
|
||||
this._histEngine.resetServer(cont);
|
||||
this._bmkEngine.resetServer(self.cb);
|
||||
this._histEngine.resetServer(self.cb);
|
||||
|
||||
this._unlock();
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
Utils.generatorDone(this, self);
|
||||
yield; // generatorDone is responsible for closing the generator
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
this._unlock();
|
||||
self.done();
|
||||
},
|
||||
|
||||
_resetClient: function WeaveSync__resetClient() {
|
||||
let [self, cont] = yield;
|
||||
let self = yield;
|
||||
|
||||
try {
|
||||
if (!this._lock())
|
||||
return;
|
||||
if (!this._lock())
|
||||
return;
|
||||
|
||||
this._bmkEngine.resetClient(cont);
|
||||
this._histEngine.resetClient(cont);
|
||||
this._bmkEngine.resetClient(self.cb);
|
||||
this._histEngine.resetClient(self.cb);
|
||||
|
||||
this._unlock();
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
|
||||
} finally {
|
||||
Utils.generatorDone(this, self);
|
||||
yield; // generatorDone is responsible for closing the generator
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
this._unlock();
|
||||
self.done();
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupports]),
|
||||
|
|
|
@ -45,8 +45,9 @@ 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");
|
||||
|
||||
Function.prototype.async = Utils.generatorAsync;
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
/*
|
||||
* SyncCore objects
|
||||
|
@ -89,9 +90,9 @@ SyncCore.prototype = {
|
|||
return this._nodeParentsInt(tree[GUID].parentGUID, tree, parents);
|
||||
},
|
||||
|
||||
_detectUpdates: function SC__detectUpdates(onComplete, a, b) {
|
||||
let [self, cont] = yield;
|
||||
let listener = new Utils.EventListener(cont);
|
||||
_detectUpdates: function SC__detectUpdates(a, b) {
|
||||
let self = yield;
|
||||
let listener = new Utils.EventListener(self.cb);
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
|
||||
let cmds = [];
|
||||
|
@ -116,6 +117,7 @@ SyncCore.prototype = {
|
|||
depth: parents.length, parents: parents});
|
||||
}
|
||||
}
|
||||
|
||||
for (let GUID in b) {
|
||||
|
||||
timer.initWithCallback(listener, 0, timer.TYPE_ONE_SHOT);
|
||||
|
@ -141,14 +143,12 @@ SyncCore.prototype = {
|
|||
});
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
timer = null;
|
||||
Utils.generatorDone(this, self, onComplete, cmds);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
self.done(cmds);
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
},
|
||||
|
||||
_commandLike: function SC__commandLike(a, b) {
|
||||
|
@ -216,9 +216,9 @@ SyncCore.prototype = {
|
|||
return false;
|
||||
},
|
||||
|
||||
_reconcile: function SC__reconcile(onComplete, listA, listB) {
|
||||
let [self, cont] = yield;
|
||||
let listener = new Utils.EventListener(cont);
|
||||
_reconcile: function SC__reconcile(listA, listB) {
|
||||
let self = yield;
|
||||
let listener = new Utils.EventListener(self.cb);
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
|
||||
let propagations = [[], []];
|
||||
|
@ -297,14 +297,13 @@ SyncCore.prototype = {
|
|||
ret = {propagations: propagations, conflicts: conflicts};
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e) +
|
||||
" - " + (e.location? e.location : "_reconcile"));
|
||||
|
||||
} finally {
|
||||
timer = null;
|
||||
Utils.generatorDone(this, self, onComplete, ret);
|
||||
yield; // onComplete is responsible for closing the generator
|
||||
self.done(ret);
|
||||
}
|
||||
this._log.warn("generator not properly closed");
|
||||
},
|
||||
|
||||
// Public methods
|
||||
|
|
|
@ -113,6 +113,23 @@ let Utils = {
|
|||
return ret;
|
||||
},
|
||||
|
||||
exceptionStr: function Weave_exceptionStr(e) {
|
||||
let message = e.message? e.message : e;
|
||||
let location = e.location? " (" + e.location + ")" : "";
|
||||
return message + location;
|
||||
},
|
||||
|
||||
stackTrace: function Weave_stackTrace(stackFrame, str) {
|
||||
if (stackFrame.caller)
|
||||
str = Utils.stackTrace(stackFrame.caller, str);
|
||||
|
||||
if (!str)
|
||||
str = "";
|
||||
str += stackFrame + "\n";
|
||||
|
||||
return str;
|
||||
},
|
||||
|
||||
checkStatus: function Weave_checkStatus(code, msg, ranges) {
|
||||
if (!ranges)
|
||||
ranges = [[200,300]];
|
||||
|
@ -120,14 +137,19 @@ let Utils = {
|
|||
for (let i = 0; i < ranges.length; i++) {
|
||||
rng = ranges[i];
|
||||
if (typeof(rng) == "object" && code >= rng[0] && code < rng[1])
|
||||
return;
|
||||
return true;
|
||||
else if (typeof(rng) == "integer" && code == rng)
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
let log = Log4Moz.Service.getLogger("Service.Util");
|
||||
log.error(msg + " Error code: " + code);
|
||||
throw 'checkStatus failed';
|
||||
return false;
|
||||
},
|
||||
|
||||
ensureStatus: function Weave_ensureStatus(args) {
|
||||
if (!Utils.checkStatus.apply(Utils, arguments))
|
||||
throw 'checkStatus failed';
|
||||
},
|
||||
|
||||
makeURI: function Weave_makeURI(URIString) {
|
||||
|
@ -147,73 +169,6 @@ let Utils = {
|
|||
Ci.nsIDOMXPathResult.ANY_TYPE, null);
|
||||
},
|
||||
|
||||
bind2: function Weave_bind2(object, method) {
|
||||
return function innerBind() { return method.apply(object, arguments); }
|
||||
},
|
||||
|
||||
// Meant to be used like this in code that imports this file:
|
||||
//
|
||||
// Function.prototype.async = generatorAsync;
|
||||
//
|
||||
// So that you can do:
|
||||
//
|
||||
// gen = fooGen.async(...);
|
||||
// ret = yield;
|
||||
//
|
||||
// where fooGen is a generator function, and gen is the running generator.
|
||||
// ret is whatever the generator 'returns' via generatorDone().
|
||||
|
||||
generatorAsync: function Weave_generatorAsync(self, extra_args) {
|
||||
try {
|
||||
let args = Array.prototype.slice.call(arguments, 1);
|
||||
let gen = this.apply(self, args);
|
||||
gen.next(); // must initialize before sending
|
||||
gen.send([gen, function(data) {Utils.continueGenerator(gen, data);}]);
|
||||
return gen;
|
||||
} catch (e) {
|
||||
if (e instanceof StopIteration) {
|
||||
dump("async warning: generator stopped unexpectedly");
|
||||
return null;
|
||||
} else {
|
||||
dump("Exception caught: " + e.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
continueGenerator: function Weave_continueGenerator(generator, data) {
|
||||
try { generator.send(data); }
|
||||
catch (e) {
|
||||
if (e instanceof StopIteration)
|
||||
dump("continueGenerator warning: generator stopped unexpectedly");
|
||||
else
|
||||
dump("Exception caught: " + e.message);
|
||||
}
|
||||
},
|
||||
|
||||
// generators created using Function.async can't simply call the
|
||||
// callback with the return value, since that would cause the calling
|
||||
// function to end up running (after the yield) from inside the
|
||||
// generator. Instead, generators can call this method which sets up
|
||||
// a timer to call the callback from a timer (and cleans up the timer
|
||||
// to avoid leaks). It also closes generators after the timeout, to
|
||||
// keep things clean.
|
||||
generatorDone: function Weave_generatorDone(object, generator, callback, retval) {
|
||||
if (object._timer)
|
||||
throw "Called generatorDone when there is a timer already set."
|
||||
|
||||
let cb = Utils.bind2(object, function(event) {
|
||||
generator.close();
|
||||
generator = null;
|
||||
object._timer = null;
|
||||
if (callback)
|
||||
callback(retval);
|
||||
});
|
||||
|
||||
object._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
object._timer.initWithCallback(new Utils.EventListener(cb),
|
||||
0, object._timer.TYPE_ONE_SHOT);
|
||||
},
|
||||
|
||||
runCmd: function Weave_runCmd() {
|
||||
var binary;
|
||||
var args = [];
|
||||
|
@ -318,6 +273,10 @@ let Utils = {
|
|||
return ret;
|
||||
},
|
||||
|
||||
bind2: function Async_bind2(object, method) {
|
||||
return function innerBind() { return method.apply(object, arguments); }
|
||||
},
|
||||
|
||||
/*
|
||||
* Event listener object
|
||||
* Used to handle XMLHttpRequest and nsITimer callbacks
|
||||
|
@ -341,7 +300,7 @@ Utils.EventListener.prototype = {
|
|||
|
||||
// nsITimerCallback
|
||||
notify: function EL_notify(timer) {
|
||||
this._log.trace("Timer fired");
|
||||
//this._log.trace("Timer fired");
|
||||
this._handler(timer);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче