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:
Dan Mills 2008-03-07 01:56:36 -08:00
Родитель ad5f58d76e
Коммит 99333d3c98
8 изменённых файлов: 681 добавлений и 762 удалений

Просмотреть файл

@ -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);
}
}