зеркало из https://github.com/mozilla/pjs.git
Merged changes and resolved conflicts between my cookie stuff and r282.
This commit is contained in:
Коммит
b70fbb03c2
|
@ -1,4 +1,5 @@
|
|||
<!ENTITY dialog.title "Mozilla Weave Sync - Activity Log">
|
||||
<!ENTITY saveAsButton.label "Save As...">
|
||||
<!ENTITY clearButton.label "Clear Logs">
|
||||
<!ENTITY briefLogTab.label "Brief Log">
|
||||
<!ENTITY verboseLogTab.label "Verbose Log">
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!ENTITY syncItemsList.label "Synchronize these items:">
|
||||
<!ENTITY bookmarksCheckbox.label "Bookmarks">
|
||||
<!ENTITY historyCheckbox.label "Browsing History">
|
||||
<!ENTITY cookiesCheckbox.label "Cookies (Not recommended!)">
|
||||
<!ENTITY cookiesCheckbox.label "Cookies (Not recommended)">
|
||||
|
||||
<!ENTITY addonsGroupbox.description "This is where you will find, install, and manage Weave add-ons.">
|
||||
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
# %S is the username of the signed in user
|
||||
signedIn.description = Signed in as %S
|
||||
reset.server.warning.title = Warning
|
||||
reset.server.warning = This will delete all data on the server.\n\nYou *must* restart this and any other instances of Firefox you may have running (on any computer).
|
||||
reset.client.warning.title = Warning
|
||||
reset.client.warning = This will delete all bookmarks and history data. Are you sure you want to do this?
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!ENTITY dialog.title "Weave Sharing">
|
||||
<!ENTITY close.button.label "Close">
|
||||
<!ENTITY share.button.label "Share">
|
||||
<!ENTITY description.top "Enter a the ID of the user you wish to share your bookmarks with:">
|
||||
<!ENTITY username.label "Weave ID (Email):">
|
||||
<!ENTITY status.waiting "Waiting...">
|
|
@ -0,0 +1,3 @@
|
|||
status.ok = Weave sharing complete!
|
||||
status.error = Error. Please check the Weave ID and try again.
|
||||
status.working = Working...
|
|
@ -2,5 +2,8 @@
|
|||
<!ENTITY logInItem.label "Sign In...">
|
||||
<!ENTITY logOutItem.label "Sign Out">
|
||||
<!ENTITY syncNowItem.label "Sync Now">
|
||||
<!ENTITY shareItem.label "Share Bookmarks...">
|
||||
<!ENTITY openPrefsItem.label "Weave Preferences...">
|
||||
<!ENTITY openLogItem.label "Activity Log...">
|
||||
<!ENTITY status.offline.label "Offline">
|
||||
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
# %S is the date and time at which the last sync successfully completed
|
||||
lastSync.label = Last Update: %S
|
||||
status.idle = Idle
|
||||
status.active = Working...
|
||||
status.offline = Offline
|
||||
status.error = Error
|
||||
|
|
|
@ -74,6 +74,8 @@ AsyncException.prototype = {
|
|||
|
||||
function Generator(object, method, onComplete, args) {
|
||||
this._log = Log4Moz.Service.getLogger("Async.Generator");
|
||||
this._log.level =
|
||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.async")];
|
||||
this._object = object;
|
||||
this._method = method;
|
||||
this.onComplete = onComplete;
|
||||
|
@ -98,7 +100,7 @@ Generator.prototype = {
|
|||
set errorOnStop(value) { this._errorOnStop = value; },
|
||||
|
||||
get cb() {
|
||||
let cb = Utils.bind2(this, function(data) { this.cont(data); });
|
||||
let self = this, cb = function(data) { self.cont(data); };
|
||||
cb.parentGenerator = this;
|
||||
return cb;
|
||||
},
|
||||
|
@ -121,7 +123,9 @@ Generator.prototype = {
|
|||
get onComplete() {
|
||||
if (this._onComplete)
|
||||
return this._onComplete;
|
||||
return function() { this._log.trace("Generator " + this.name + " has no onComplete"); };
|
||||
return function() {
|
||||
//this._log.trace("Generator " + this.name + " has no onComplete");
|
||||
};
|
||||
},
|
||||
set onComplete(value) {
|
||||
if (value && typeof value != "function")
|
||||
|
@ -137,34 +141,25 @@ Generator.prototype = {
|
|||
_handleException: function AsyncGen__handleException(e) {
|
||||
if (e instanceof StopIteration) {
|
||||
if (this.errorOnStop) {
|
||||
this._log.error("Generator stopped unexpectedly");
|
||||
this._log.error("[" + this.name + "] Generator stopped unexpectedly");
|
||||
this._log.trace("Stack trace:\n" + this.trace);
|
||||
this._exception = "Generator stopped unexpectedly"; // don't propagate StopIteration
|
||||
}
|
||||
|
||||
} else if (this.onComplete.parentGenerator instanceof Generator) {
|
||||
//this._log.trace("Saving exception and stack trace");
|
||||
this._log.trace("[" + this.name + "] Saving exception and stack trace");
|
||||
this._log.trace(Async.exceptionStr(this, e));
|
||||
|
||||
switch (typeof e) {
|
||||
case "string":
|
||||
if (e instanceof AsyncException)
|
||||
e.trace = this.trace + e.trace? "\n" + e.trace : "";
|
||||
else
|
||||
e = new AsyncException(this, e);
|
||||
break;
|
||||
case "object":
|
||||
if (e.trace) // means we're re-throwing up the stack
|
||||
e.trace = this.trace + "\n" + e.trace;
|
||||
else
|
||||
e.trace = this.trace;
|
||||
break;
|
||||
default:
|
||||
this._log.debug("Unknown exception type: " + typeof(e));
|
||||
break;
|
||||
}
|
||||
|
||||
this._exception = e;
|
||||
|
||||
} else {
|
||||
this._log.error(Async.exceptionStr(this, e));
|
||||
this._log.trace("Stack trace:\n" + this.trace +
|
||||
this._log.debug("Stack trace:\n" + this.trace +
|
||||
(e.trace? "\n" + e.trace : ""));
|
||||
}
|
||||
|
||||
|
@ -172,7 +167,10 @@ Generator.prototype = {
|
|||
// in the case of StopIteration we could return an error right
|
||||
// away, but instead it's easiest/best to let the caller handle
|
||||
// the error after a yield / in a callback.
|
||||
this.done();
|
||||
if (!this._timer) {
|
||||
this._log.trace("[" + this.name + "] running done() from _handleException()");
|
||||
this.done();
|
||||
}
|
||||
},
|
||||
|
||||
run: function AsyncGen_run() {
|
||||
|
@ -181,18 +179,25 @@ Generator.prototype = {
|
|||
this.generator.next(); // must initialize before sending
|
||||
this.generator.send(this);
|
||||
} catch (e) {
|
||||
this._handleException(e);
|
||||
if (!(e instanceof StopIteration) || !this._timer)
|
||||
this._handleException(e);
|
||||
}
|
||||
},
|
||||
|
||||
cont: function AsyncGen_cont(data) {
|
||||
try { this.generator.send(data); }
|
||||
catch (e) { this._handleException(e); }
|
||||
catch (e) {
|
||||
if (!(e instanceof StopIteration) || !this._timer)
|
||||
this._handleException(e);
|
||||
}
|
||||
},
|
||||
|
||||
throw: function AsyncGen_throw(exception) {
|
||||
try { this.generator.throw(exception); }
|
||||
catch (e) { this._handleException(e); }
|
||||
catch (e) {
|
||||
if (!(e instanceof StopIteration) || !this._timer)
|
||||
this._handleException(e);
|
||||
}
|
||||
},
|
||||
|
||||
// async generators can't simply call a callback with the return
|
||||
|
@ -213,15 +218,22 @@ Generator.prototype = {
|
|||
},
|
||||
|
||||
_done: function AsyncGen__done(retval) {
|
||||
this._generator.close();
|
||||
if (!this._generator) {
|
||||
this._log.error("Async method '" + this.name + "' is missing a 'yield' call " +
|
||||
"(or called done() after being finalized)");
|
||||
this._log.trace("Initial stack trace:\n" + this.trace);
|
||||
} else {
|
||||
this._generator.close();
|
||||
}
|
||||
this._generator = null;
|
||||
this._timer = null;
|
||||
|
||||
if (this._exception) {
|
||||
this._log.trace("Propagating exception to parent generator");
|
||||
this._log.trace("[" + this.name + "] Propagating exception to parent generator");
|
||||
this.onComplete.parentGenerator.throw(this._exception);
|
||||
} else {
|
||||
try {
|
||||
this._log.trace("[" + this.name + "] Running onComplete()");
|
||||
this.onComplete(retval);
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught from onComplete handler of " +
|
||||
|
@ -243,7 +255,7 @@ Async = {
|
|||
// where fooGen is a generator function, and gen is a Generator instance
|
||||
// ret is whatever the generator 'returns' via Generator.done().
|
||||
|
||||
run: function Async_run(object, method, onComplete, args) {
|
||||
run: function Async_run(object, method, onComplete /* , arg1, arg2, ... */) {
|
||||
let args = Array.prototype.slice.call(arguments, 3);
|
||||
let gen = new Generator(object, method, onComplete, args);
|
||||
gen.run();
|
||||
|
@ -263,7 +275,7 @@ Async = {
|
|||
// Note that 'this' refers to the method being called, not the
|
||||
// Async object.
|
||||
|
||||
sugar: function Async_sugar(object, onComplete, extra_args) {
|
||||
sugar: function Async_sugar(object, onComplete /* , arg1, arg2, ... */) {
|
||||
let args = Array.prototype.slice.call(arguments, 1);
|
||||
args.unshift(object, this);
|
||||
Async.run.apply(Async, args);
|
||||
|
|
|
@ -35,14 +35,16 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const EXPORTED_SYMBOLS = ['WEAVE_VERSION', 'STORAGE_FORMAT_VERSION',
|
||||
'ENGINE_STORAGE_FORMAT_VERSION',
|
||||
'PREFS_BRANCH',
|
||||
'MODE_RDONLY', 'MODE_WRONLY',
|
||||
'MODE_CREATE', 'MODE_APPEND', 'MODE_TRUNCATE',
|
||||
'PERMS_FILE', 'PERMS_PASSFILE', 'PERMS_DIRECTORY',
|
||||
'ONE_BYTE', 'ONE_KILOBYTE', 'ONE_MEGABYTE'];
|
||||
|
||||
const WEAVE_VERSION = "0.1.20";
|
||||
const WEAVE_VERSION = "0.1.27";
|
||||
const STORAGE_FORMAT_VERSION = 2;
|
||||
const ENGINE_STORAGE_FORMAT_VERSION = 2;
|
||||
|
||||
const PREFS_BRANCH = "extensions.weave.";
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const EXPORTED_SYMBOLS = ['WeaveCrypto'];
|
||||
const EXPORTED_SYMBOLS = ['Crypto'];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
@ -49,10 +49,12 @@ Cu.import("resource://weave/async.js");
|
|||
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
function WeaveCrypto() {
|
||||
Utils.lazy(this, 'Crypto', CryptoSvc);
|
||||
|
||||
function CryptoSvc() {
|
||||
this._init();
|
||||
}
|
||||
WeaveCrypto.prototype = {
|
||||
CryptoSvc.prototype = {
|
||||
_logName: "Crypto",
|
||||
|
||||
__os: null,
|
||||
|
@ -88,6 +90,8 @@ WeaveCrypto.prototype = {
|
|||
|
||||
_init: function Crypto__init() {
|
||||
this._log = Log4Moz.Service.getLogger("Service." + this._logName);
|
||||
this._log.level =
|
||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.service.crypto")];
|
||||
let branch = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefBranch2);
|
||||
branch.addObserver("extensions.weave.encryption", this, false);
|
||||
|
@ -163,7 +167,7 @@ WeaveCrypto.prototype = {
|
|||
// generates a random string that can be used as a passphrase
|
||||
_opensslRand: function Crypto__opensslRand(length) {
|
||||
if (!length)
|
||||
length = 256;
|
||||
length = 128;
|
||||
|
||||
let outputFile = Utils.getTmp("output");
|
||||
if (outputFile.exists())
|
||||
|
@ -180,7 +184,7 @@ WeaveCrypto.prototype = {
|
|||
},
|
||||
|
||||
// generates an rsa public/private key pair, with the private key encrypted
|
||||
_opensslRSAKeyGen: function Crypto__opensslRSAKeyGen(password, algorithm, bits) {
|
||||
_opensslRSAKeyGen: function Crypto__opensslRSAKeyGen(identity, algorithm, bits) {
|
||||
if (!algorithm)
|
||||
algorithm = "aes-256-cbc";
|
||||
if (!bits)
|
||||
|
@ -206,7 +210,7 @@ WeaveCrypto.prototype = {
|
|||
// nsIProcess doesn't support stdin, so we write a file instead
|
||||
let passFile = Utils.getTmp("pass");
|
||||
let [passFOS] = Utils.open(passFile, ">", PERMS_PASSFILE);
|
||||
passFOS.writeString(password);
|
||||
passFOS.writeString(identity.password);
|
||||
passFOS.close();
|
||||
|
||||
try {
|
||||
|
@ -235,7 +239,7 @@ WeaveCrypto.prototype = {
|
|||
},
|
||||
|
||||
// returns 'input' encrypted with the 'pubkey' public RSA key
|
||||
_opensslRSAencrypt: function Crypto__opensslRSAencrypt(input, pubkey) {
|
||||
_opensslRSAencrypt: function Crypto__opensslRSAencrypt(input, identity) {
|
||||
let inputFile = Utils.getTmp("input");
|
||||
let [inputFOS] = Utils.open(inputFile, ">");
|
||||
inputFOS.writeString(input);
|
||||
|
@ -243,18 +247,23 @@ WeaveCrypto.prototype = {
|
|||
|
||||
let keyFile = Utils.getTmp("key");
|
||||
let [keyFOS] = Utils.open(keyFile, ">");
|
||||
keyFOS.writeString(pubkey);
|
||||
keyFOS.writeString(identity.pubkey);
|
||||
keyFOS.close();
|
||||
|
||||
let tmpFile = Utils.getTmp("tmp-output");
|
||||
if (tmpFile.exists())
|
||||
tmpFile.remove(false);
|
||||
|
||||
let outputFile = Utils.getTmp("output");
|
||||
if (outputFile.exists())
|
||||
outputFile.remove(false);
|
||||
|
||||
this._openssl("rsautl", "-encrypt", "-pubin", "-inkey", "key",
|
||||
"-in", "input", "-out", "output");
|
||||
"-in", "input", "-out", "tmp-output");
|
||||
this._openssl("base64", "-in", "tmp-output", "-out", "output");
|
||||
|
||||
let [outputFIS] = Utils.open(outputFile, "<");
|
||||
let output = Utils.readStream(outpusFIS);
|
||||
let output = Utils.readStream(outputFIS);
|
||||
outputFIS.close();
|
||||
outputFile.remove(false);
|
||||
|
||||
|
@ -262,7 +271,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, identity) {
|
||||
let inputFile = Utils.getTmp("input");
|
||||
let [inputFOS] = Utils.open(inputFile, ">");
|
||||
inputFOS.writeString(input);
|
||||
|
@ -270,7 +279,58 @@ WeaveCrypto.prototype = {
|
|||
|
||||
let keyFile = Utils.getTmp("key");
|
||||
let [keyFOS] = Utils.open(keyFile, ">");
|
||||
keyFOS.writeString(privkey);
|
||||
keyFOS.writeString(identity.privkey);
|
||||
keyFOS.close();
|
||||
|
||||
let tmpKeyFile = Utils.getTmp("tmp-key");
|
||||
if (tmpKeyFile.exists())
|
||||
tmpKeyFile.remove(false);
|
||||
|
||||
let tmpFile = Utils.getTmp("tmp-output");
|
||||
if (tmpFile.exists())
|
||||
tmpFile.remove(false);
|
||||
|
||||
let outputFile = Utils.getTmp("output");
|
||||
if (outputFile.exists())
|
||||
outputFile.remove(false);
|
||||
|
||||
// nsIProcess doesn't support stdin, so we write a file instead
|
||||
let passFile = Utils.getTmp("pass");
|
||||
let [passFOS] = Utils.open(passFile, ">", PERMS_PASSFILE);
|
||||
passFOS.writeString(identity.password);
|
||||
passFOS.close();
|
||||
|
||||
try {
|
||||
this._openssl("base64", "-d", "-in", "input", "-out", "tmp-output");
|
||||
// FIXME: this is because openssl.exe (in windows only) doesn't
|
||||
// seem to support -passin for rsautl, but it works for rsa.
|
||||
this._openssl("rsa", "-in", "key", "-out", "tmp-key", "-passin", "file:pass");
|
||||
this._openssl("rsautl", "-decrypt", "-inkey", "tmp-key",
|
||||
"-in", "tmp-output", "-out", "output");
|
||||
|
||||
} catch(e) {
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
passFile.remove(false);
|
||||
tmpKeyFile.remove(false);
|
||||
tmpFile.remove(false);
|
||||
keyFile.remove(false);
|
||||
}
|
||||
|
||||
let [outputFIS] = Utils.open(outputFile, "<");
|
||||
let output = Utils.readStream(outputFIS);
|
||||
outputFIS.close();
|
||||
outputFile.remove(false);
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
// returns the public key from the private key
|
||||
_opensslRSAkeydecrypt: function Crypto__opensslRSAkeydecrypt(identity) {
|
||||
let keyFile = Utils.getTmp("key");
|
||||
let [keyFOS] = Utils.open(keyFile, ">");
|
||||
keyFOS.writeString(identity.privkey);
|
||||
keyFOS.close();
|
||||
|
||||
let outputFile = Utils.getTmp("output");
|
||||
|
@ -280,12 +340,12 @@ WeaveCrypto.prototype = {
|
|||
// nsIProcess doesn't support stdin, so we write a file instead
|
||||
let passFile = Utils.getTmp("pass");
|
||||
let [passFOS] = Utils.open(passFile, ">", PERMS_PASSFILE);
|
||||
passFOS.writeString(password);
|
||||
passFOS.writeString(identity.password);
|
||||
passFOS.close();
|
||||
|
||||
try {
|
||||
this._openssl("rsautl", "-decrypt", "-inkey", "key", "-pass",
|
||||
"file:pass", "-in", "input", "-out", "output");
|
||||
this._openssl("rsa", "-in", "key", "-pubout", "-out", "output",
|
||||
"-passin", "file:pass");
|
||||
|
||||
} catch(e) {
|
||||
throw e;
|
||||
|
@ -295,7 +355,7 @@ WeaveCrypto.prototype = {
|
|||
}
|
||||
|
||||
let [outputFIS] = Utils.open(outputFile, "<");
|
||||
let output = Utils.readStream(outpusFIS);
|
||||
let output = Utils.readStream(outputFIS);
|
||||
outputFIS.close();
|
||||
outputFile.remove(false);
|
||||
|
||||
|
@ -445,21 +505,27 @@ WeaveCrypto.prototype = {
|
|||
self.done(ret);
|
||||
},
|
||||
|
||||
RSAkeygen: function Crypto_RSAkeygen(password) {
|
||||
RSAkeygen: function Crypto_RSAkeygen(identity) {
|
||||
let self = yield;
|
||||
let ret = this._opensslRSAKeyGen(password);
|
||||
let ret = this._opensslRSAKeyGen(identity);
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
RSAencrypt: function Crypto_RSAencrypt(data, key) {
|
||||
RSAencrypt: function Crypto_RSAencrypt(data, identity) {
|
||||
let self = yield;
|
||||
let ret = this._opensslRSAencrypt(data, key);
|
||||
let ret = this._opensslRSAencrypt(data, identity);
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
RSAdecrypt: function Crypto_RSAdecrypt(data, key, password) {
|
||||
RSAdecrypt: function Crypto_RSAdecrypt(data, identity) {
|
||||
let self = yield;
|
||||
let ret = this._opensslRSAdecrypt(data, key, password);
|
||||
let ret = this._opensslRSAdecrypt(data, identity);
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
RSAkeydecrypt: function Crypto_RSAkeydecrypt(identity) {
|
||||
let self = yield;
|
||||
let ret = this._opensslRSAkeydecrypt(identity);
|
||||
self.done(ret);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const EXPORTED_SYMBOLS = ['DAVCollection'];
|
||||
const EXPORTED_SYMBOLS = ['DAV', 'DAVCollection'];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
@ -45,9 +45,12 @@ 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");
|
||||
Cu.import("resource://weave/constants.js");
|
||||
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
Utils.lazy(this, 'DAV', DAVCollection);
|
||||
|
||||
/*
|
||||
* DAV object
|
||||
* Abstracts the raw DAV commands
|
||||
|
@ -57,8 +60,11 @@ function DAVCollection(baseURL) {
|
|||
this._baseURL = baseURL;
|
||||
this._authProvider = new DummyAuthProvider();
|
||||
this._log = Log4Moz.Service.getLogger("Service.DAV");
|
||||
this._log.level =
|
||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.service.dav")];
|
||||
}
|
||||
DAVCollection.prototype = {
|
||||
|
||||
__dp: null,
|
||||
get _dp() {
|
||||
if (!this.__dp)
|
||||
|
@ -73,6 +79,8 @@ DAVCollection.prototype = {
|
|||
return this._baseURL;
|
||||
},
|
||||
set baseURL(value) {
|
||||
if (value[value.length-1] != '/')
|
||||
value = value + '/';
|
||||
this._baseURL = value;
|
||||
},
|
||||
|
||||
|
@ -81,15 +89,27 @@ DAVCollection.prototype = {
|
|||
return this._loggedIn;
|
||||
},
|
||||
|
||||
get locked() {
|
||||
return !this._lockAllowed || this._token != null;
|
||||
},
|
||||
|
||||
_lockAllowed: true,
|
||||
get allowLock() {
|
||||
return this._lockAllowed;
|
||||
},
|
||||
set allowLock(value) {
|
||||
this._lockAllowed = value;
|
||||
},
|
||||
|
||||
_makeRequest: function DC__makeRequest(op, path, headers, data) {
|
||||
let self = yield;
|
||||
let ret;
|
||||
|
||||
this._log.debug("Creating " + op + " request for " + this._baseURL + path);
|
||||
this._log.debug(op + " request for " + path);
|
||||
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
|
||||
request = request.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
|
||||
|
||||
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);
|
||||
|
@ -106,9 +126,9 @@ DAVCollection.prototype = {
|
|||
let key;
|
||||
for (key in headers) {
|
||||
if (key == 'Authorization')
|
||||
this._log.debug("HTTP Header " + key + ": ***** (suppressed)");
|
||||
this._log.trace("HTTP Header " + key + ": ***** (suppressed)");
|
||||
else
|
||||
this._log.debug("HTTP Header " + key + ": " + headers[key]);
|
||||
this._log.trace("HTTP Header " + key + ": " + headers[key]);
|
||||
request.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
|
||||
|
@ -142,33 +162,33 @@ DAVCollection.prototype = {
|
|||
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, 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 +
|
||||
if (ret.status != 404) {
|
||||
this._log.trace("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 + "/";
|
||||
}
|
||||
|
@ -211,6 +231,9 @@ DAVCollection.prototype = {
|
|||
},
|
||||
|
||||
LOCK: function DC_LOCK(path, data, onComplete) {
|
||||
if (!this._lockAllowed)
|
||||
throw "Cannot acquire lock (internal lock)";
|
||||
|
||||
let headers = {'Content-type': 'text/xml; charset="utf-8"',
|
||||
'Depth': 'infinity',
|
||||
'Timeout': 'Second-600'};
|
||||
|
@ -264,8 +287,8 @@ DAVCollection.prototype = {
|
|||
self.done(true);
|
||||
yield;
|
||||
}
|
||||
|
||||
this._log.info("Logging in");
|
||||
|
||||
this._log.debug("Logging in");
|
||||
|
||||
let URI = Utils.makeURI(this._baseURL);
|
||||
this._auth = "Basic " + btoa(username + ":" + password);
|
||||
|
@ -286,7 +309,7 @@ DAVCollection.prototype = {
|
|||
},
|
||||
|
||||
logout: function DC_logout() {
|
||||
this._log.debug("Logging out (forgetting auth header)");
|
||||
this._log.trace("Logging out (forgetting auth header)");
|
||||
this._loggedIn = false;
|
||||
this.__auth = null;
|
||||
},
|
||||
|
@ -297,7 +320,7 @@ DAVCollection.prototype = {
|
|||
let self = yield;
|
||||
let ret = null;
|
||||
|
||||
this._log.info("Getting active lock token");
|
||||
this._log.debug("Getting active lock token");
|
||||
this.PROPFIND("",
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
|
||||
"<D:propfind xmlns:D='DAV:'>" +
|
||||
|
@ -313,12 +336,13 @@ DAVCollection.prototype = {
|
|||
|
||||
let tokens = Utils.xpath(resp.responseXML, '//D:locktoken/D:href');
|
||||
let token = tokens.iterateNext();
|
||||
ret = token.textContent;
|
||||
if (token)
|
||||
ret = token.textContent;
|
||||
|
||||
if (ret)
|
||||
this._log.debug("Found an active lock token");
|
||||
this._log.trace("Found an active lock token");
|
||||
else
|
||||
this._log.debug("No active lock token found");
|
||||
this._log.trace("No active lock token found");
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
|
@ -326,7 +350,7 @@ DAVCollection.prototype = {
|
|||
let self = yield;
|
||||
this._token = null;
|
||||
|
||||
this._log.info("Acquiring lock");
|
||||
this._log.trace("Acquiring lock");
|
||||
|
||||
if (this._token) {
|
||||
this._log.debug("Lock called, but we already hold a token");
|
||||
|
@ -354,7 +378,7 @@ DAVCollection.prototype = {
|
|||
this._token = token.textContent;
|
||||
|
||||
if (this._token)
|
||||
this._log.debug("Lock acquired");
|
||||
this._log.trace("Lock acquired");
|
||||
else
|
||||
this._log.warn("Could not acquire lock");
|
||||
|
||||
|
@ -364,9 +388,9 @@ DAVCollection.prototype = {
|
|||
unlock: function DC_unlock() {
|
||||
let self = yield;
|
||||
|
||||
this._log.info("Releasing lock");
|
||||
this._log.trace("Releasing lock");
|
||||
|
||||
if (this._token === null) {
|
||||
if (!this.locked) {
|
||||
this._log.debug("Unlock called, but we don't hold a token right now");
|
||||
self.done(true);
|
||||
yield;
|
||||
|
@ -384,9 +408,9 @@ DAVCollection.prototype = {
|
|||
this._token = null;
|
||||
|
||||
if (this._token)
|
||||
this._log.info("Could not release lock");
|
||||
this._log.trace("Could not release lock");
|
||||
else
|
||||
this._log.info("Lock released (or we didn't have one)");
|
||||
this._log.trace("Lock released (or we didn't have one)");
|
||||
|
||||
self.done(!this._token);
|
||||
},
|
||||
|
@ -395,25 +419,25 @@ DAVCollection.prototype = {
|
|||
let self = yield;
|
||||
let unlocked = true;
|
||||
|
||||
this._log.info("Forcibly releasing any server locks");
|
||||
this._log.debug("Forcibly releasing any server locks");
|
||||
|
||||
this._getActiveLock.async(this, self.cb);
|
||||
this._token = yield;
|
||||
|
||||
if (!this._token) {
|
||||
this._log.info("No server lock found");
|
||||
this._log.debug("No server lock found");
|
||||
self.done(true);
|
||||
yield;
|
||||
}
|
||||
|
||||
this._log.info("Server lock found, unlocking");
|
||||
this._log.trace("Server lock found, unlocking");
|
||||
this.unlock.async(this, self.cb);
|
||||
unlocked = yield;
|
||||
|
||||
if (unlocked)
|
||||
this._log.debug("Lock released");
|
||||
this._log.trace("Lock released");
|
||||
else
|
||||
this._log.debug("No lock released");
|
||||
this._log.trace("No lock released");
|
||||
self.done(unlocked);
|
||||
},
|
||||
|
||||
|
@ -443,7 +467,7 @@ function DummyAuthProvider() {}
|
|||
DummyAuthProvider.prototype = {
|
||||
// Implement notification callback interfaces so we can suppress UI
|
||||
// and abort loads for bad SSL certs and HTTP authorization requests.
|
||||
|
||||
|
||||
// Interfaces this component implements.
|
||||
interfaces: [Ci.nsIBadCertListener,
|
||||
Ci.nsIAuthPromptProvider,
|
||||
|
@ -479,7 +503,7 @@ DummyAuthProvider.prototype = {
|
|||
},
|
||||
|
||||
// nsIInterfaceRequestor
|
||||
|
||||
|
||||
getInterface: function DAP_getInterface(iid) {
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
@ -487,7 +511,7 @@ DummyAuthProvider.prototype = {
|
|||
// nsIBadCertListener
|
||||
|
||||
// Suppress UI and abort secure loads from servers with bad SSL certificates.
|
||||
|
||||
|
||||
confirmUnknownIssuer: function DAP_confirmUnknownIssuer(socketInfo, cert, certAddType) {
|
||||
return false;
|
||||
},
|
||||
|
@ -504,7 +528,7 @@ DummyAuthProvider.prototype = {
|
|||
},
|
||||
|
||||
// nsIAuthPromptProvider
|
||||
|
||||
|
||||
getAuthPrompt: function(aPromptReason, aIID) {
|
||||
this._authFailed = true;
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
|
|
|
@ -46,15 +46,16 @@ Cu.import("resource://weave/log4moz.js");
|
|||
Cu.import("resource://weave/constants.js");
|
||||
Cu.import("resource://weave/util.js");
|
||||
Cu.import("resource://weave/crypto.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
Cu.import("resource://weave/identity.js");
|
||||
Cu.import("resource://weave/stores.js");
|
||||
Cu.import("resource://weave/syncCores.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
|
||||
Function.prototype.async = Async.sugar;
|
||||
let Crypto = new WeaveCrypto();
|
||||
|
||||
function Engine(davCollection, cryptoId) {
|
||||
//this._init(davCollection, cryptoId);
|
||||
function Engine(davCollection, pbeId) {
|
||||
//this._init(davCollection, pbeId);
|
||||
}
|
||||
Engine.prototype = {
|
||||
// "default-engine";
|
||||
|
@ -114,35 +115,35 @@ Engine.prototype = {
|
|||
this.__snapshot = value;
|
||||
},
|
||||
|
||||
_init: function Engine__init(davCollection, cryptoId) {
|
||||
this._dav = davCollection;
|
||||
this._cryptoId = cryptoId;
|
||||
_init: function Engine__init(davCollection, pbeId) {
|
||||
this._pbeId = pbeId;
|
||||
this._engineId = new Identity(pbeId.realm + " - " + this.logName,
|
||||
pbeId.username);
|
||||
this._log = Log4Moz.Service.getLogger("Service." + this.logName);
|
||||
this._log.level =
|
||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.service.engine")];
|
||||
this._osPrefix = "weave:" + this.name + ":";
|
||||
this._snapshot.load();
|
||||
},
|
||||
|
||||
_getSymKey: function Engine__getCryptoId(cryptoId) {
|
||||
_getSymKey: function Engine__getSymKey() {
|
||||
let self = yield;
|
||||
let done = false;
|
||||
|
||||
this._dav.GET(this.keysFile, self.cb);
|
||||
DAV.GET(this.keysFile, self.cb);
|
||||
let keysResp = yield;
|
||||
Utils.ensureStatus(keysResp.status,
|
||||
"Could not get keys file.", [[200,300]]);
|
||||
let keys = keysResp.responseText;
|
||||
let keys = this._json.decode(keysResp.responseText);
|
||||
|
||||
if (!keys[this._userHash]) {
|
||||
this._log.error("Keyring does not contain a key for this user");
|
||||
return;
|
||||
}
|
||||
if (!keys || !keys.ring || !keys.ring[this._engineId.userHash])
|
||||
throw "Keyring does not contain a key for this user";
|
||||
|
||||
//Crypto.RSAdecrypt.async(Crypto, self.cb,
|
||||
// keys[this._userHash],
|
||||
|
||||
self.done(done);
|
||||
yield;
|
||||
this._log.warn("_getSymKey generator not properly closed");
|
||||
Crypto.RSAdecrypt.async(Crypto, self.cb,
|
||||
keys.ring[this._engineId.userHash], this._pbeId);
|
||||
let symkey = yield;
|
||||
this._engineId.setTempPassword(symkey);
|
||||
|
||||
self.done(true);
|
||||
},
|
||||
|
||||
_serializeCommands: function Engine__serializeCommands(commands) {
|
||||
|
@ -150,7 +151,7 @@ Engine.prototype = {
|
|||
//json = json.replace(/ {action/g, "\n {action");
|
||||
return json;
|
||||
},
|
||||
|
||||
|
||||
_serializeConflicts: function Engine__serializeConflicts(conflicts) {
|
||||
let json = this._json.encode(conflicts);
|
||||
//json = json.replace(/ {action/g, "\n {action");
|
||||
|
@ -165,22 +166,14 @@ Engine.prototype = {
|
|||
this._log.debug("Resetting server data");
|
||||
this._os.notifyObservers(null, this._osPrefix + "reset-server:start", "");
|
||||
|
||||
this._dav.lock.async(this._dav, self.cb);
|
||||
let locked = yield;
|
||||
if (!locked)
|
||||
throw "Could not acquire lock, aborting server reset";
|
||||
|
||||
// try to delete all 3, check status after
|
||||
this._dav.DELETE(this.statusFile, self.cb);
|
||||
DAV.DELETE(this.statusFile, self.cb);
|
||||
let statusResp = yield;
|
||||
this._dav.DELETE(this.snapshotFile, self.cb);
|
||||
DAV.DELETE(this.snapshotFile, self.cb);
|
||||
let snapshotResp = yield;
|
||||
this._dav.DELETE(this.deltasFile, self.cb);
|
||||
DAV.DELETE(this.deltasFile, self.cb);
|
||||
let deltasResp = yield;
|
||||
|
||||
this._dav.unlock.async(this._dav, self.cb);
|
||||
let unlocked = yield;
|
||||
|
||||
Utils.ensureStatus(statusResp.status,
|
||||
"Could not delete status file.", [[200,300],404]);
|
||||
Utils.ensureStatus(snapshotResp.status,
|
||||
|
@ -191,7 +184,7 @@ Engine.prototype = {
|
|||
this._log.debug("Server files deleted");
|
||||
done = true;
|
||||
this._os.notifyObservers(null, this._osPrefix + "reset-server:success", "");
|
||||
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Could not delete server files");
|
||||
this._os.notifyObservers(null, this._osPrefix + "reset-server:error", "");
|
||||
|
@ -258,215 +251,186 @@ Engine.prototype = {
|
|||
|
||||
_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._log.info("Beginning sync");
|
||||
|
||||
this._dav.lock.async(this._dav, self.cb);
|
||||
locked = yield;
|
||||
// Before we get started, make sure we have a remote directory to play in
|
||||
DAV.MKCOL(this.serverPrefix, self.cb);
|
||||
let ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create remote folder";
|
||||
|
||||
if (locked)
|
||||
this._log.info("Lock acquired");
|
||||
else {
|
||||
this._log.warn("Could not acquire lock, aborting sync");
|
||||
return;
|
||||
}
|
||||
// 1) Fetch server deltas
|
||||
this._getServerData.async(this, self.cb);
|
||||
let server = yield;
|
||||
|
||||
// Before we get started, make sure we have a remote directory to play in
|
||||
this._dav.MKCOL(this.serverPrefix, self.cb);
|
||||
let ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create remote folder";
|
||||
this._log.info("Local snapshot version: " + this._snapshot.version);
|
||||
this._log.info("Server status: " + server.status);
|
||||
this._log.info("Server maxVersion: " + server.maxVersion);
|
||||
this._log.info("Server snapVersion: " + server.snapVersion);
|
||||
|
||||
// 1) Fetch server deltas
|
||||
this._getServerData.async(this, self.cb);
|
||||
let server = yield;
|
||||
if (server.status != 0) {
|
||||
this._log.fatal("Sync error: could not get server status, " +
|
||||
"or initial upload failed. Aborting sync.");
|
||||
return;
|
||||
}
|
||||
|
||||
this._log.info("Local snapshot version: " + this._snapshot.version);
|
||||
this._log.info("Server status: " + server.status);
|
||||
this._log.info("Server maxVersion: " + server.maxVersion);
|
||||
this._log.info("Server snapVersion: " + server.snapVersion);
|
||||
// 2) Generate local deltas from snapshot -> current client status
|
||||
|
||||
if (server.status != 0) {
|
||||
this._log.fatal("Sync error: could not get server status, " +
|
||||
"or initial upload failed. Aborting sync.");
|
||||
return;
|
||||
}
|
||||
let localJson = new SnapshotStore();
|
||||
localJson.data = this._store.wrap();
|
||||
this._core.detectUpdates(self.cb, this._snapshot.data, localJson.data);
|
||||
let localUpdates = yield;
|
||||
|
||||
// 2) Generate local deltas from snapshot -> current client status
|
||||
this._log.trace("local json:\n" + localJson.serialize());
|
||||
this._log.trace("Local updates: " + this._serializeCommands(localUpdates));
|
||||
this._log.trace("Server updates: " + this._serializeCommands(server.updates));
|
||||
|
||||
let localJson = new SnapshotStore();
|
||||
localJson.data = this._store.wrap();
|
||||
this._core.detectUpdates(self.cb, this._snapshot.data, localJson.data);
|
||||
let localUpdates = yield;
|
||||
if (server.updates.length == 0 && localUpdates.length == 0) {
|
||||
this._snapshot.version = server.maxVersion;
|
||||
this._log.info("Sync complete: no changes needed on client or server");
|
||||
self.done(true);
|
||||
return;
|
||||
}
|
||||
|
||||
this._log.trace("local json:\n" + localJson.serialize());
|
||||
this._log.trace("Local updates: " + this._serializeCommands(localUpdates));
|
||||
this._log.trace("Server updates: " + this._serializeCommands(server.updates));
|
||||
// 3) Reconcile client/server deltas and generate new deltas for them.
|
||||
|
||||
if (server.updates.length == 0 && localUpdates.length == 0) {
|
||||
this._snapshot.version = server.maxVersion;
|
||||
this._log.info("Sync complete (1): no changes needed on client or server");
|
||||
synced = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 3) Reconcile client/server deltas and generate new deltas for them.
|
||||
this._log.info("Reconciling client/server updates");
|
||||
this._core.reconcile(self.cb, localUpdates, server.updates);
|
||||
ret = yield;
|
||||
|
||||
this._log.info("Reconciling client/server updates");
|
||||
this._core.reconcile(self.cb, localUpdates, server.updates);
|
||||
ret = yield;
|
||||
let clientChanges = ret.propagations[0];
|
||||
let serverChanges = ret.propagations[1];
|
||||
let clientConflicts = ret.conflicts[0];
|
||||
let serverConflicts = ret.conflicts[1];
|
||||
|
||||
let clientChanges = ret.propagations[0];
|
||||
let serverChanges = ret.propagations[1];
|
||||
let clientConflicts = ret.conflicts[0];
|
||||
let serverConflicts = ret.conflicts[1];
|
||||
this._log.info("Changes for client: " + clientChanges.length);
|
||||
this._log.info("Predicted changes for server: " + serverChanges.length);
|
||||
this._log.info("Client conflicts: " + clientConflicts.length);
|
||||
this._log.info("Server conflicts: " + serverConflicts.length);
|
||||
this._log.trace("Changes for client: " + this._serializeCommands(clientChanges));
|
||||
this._log.trace("Predicted changes for server: " + this._serializeCommands(serverChanges));
|
||||
this._log.trace("Client conflicts: " + this._serializeConflicts(clientConflicts));
|
||||
this._log.trace("Server conflicts: " + this._serializeConflicts(serverConflicts));
|
||||
|
||||
this._log.info("Changes for client: " + clientChanges.length);
|
||||
this._log.info("Predicted changes for server: " + serverChanges.length);
|
||||
this._log.info("Client conflicts: " + clientConflicts.length);
|
||||
this._log.info("Server conflicts: " + serverConflicts.length);
|
||||
this._log.trace("Changes for client: " + this._serializeCommands(clientChanges));
|
||||
this._log.trace("Predicted changes for server: " + this._serializeCommands(serverChanges));
|
||||
this._log.trace("Client conflicts: " + this._serializeConflicts(clientConflicts));
|
||||
this._log.trace("Server conflicts: " + this._serializeConflicts(serverConflicts));
|
||||
if (!(clientChanges.length || serverChanges.length ||
|
||||
clientConflicts.length || serverConflicts.length)) {
|
||||
this._log.info("Sync complete: no changes needed on client or server");
|
||||
this._snapshot.data = localJson.data;
|
||||
this._snapshot.version = server.maxVersion;
|
||||
this._snapshot.save();
|
||||
self.done(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(clientChanges.length || serverChanges.length ||
|
||||
clientConflicts.length || serverConflicts.length)) {
|
||||
this._log.info("Sync complete (2): no changes needed on client or server");
|
||||
this._snapshot.data = localJson.data;
|
||||
this._snapshot.version = server.maxVersion;
|
||||
this._snapshot.save();
|
||||
synced = true;
|
||||
return;
|
||||
}
|
||||
if (clientConflicts.length || serverConflicts.length) {
|
||||
this._log.warn("Conflicts found! Discarding server changes");
|
||||
}
|
||||
|
||||
if (clientConflicts.length || serverConflicts.length) {
|
||||
this._log.warn("Conflicts found! Discarding server changes");
|
||||
}
|
||||
let savedSnap = Utils.deepCopy(this._snapshot.data);
|
||||
let savedVersion = this._snapshot.version;
|
||||
let newSnapshot;
|
||||
|
||||
let savedSnap = Utils.deepCopy(this._snapshot.data);
|
||||
let savedVersion = this._snapshot.version;
|
||||
let newSnapshot;
|
||||
|
||||
// 3.1) Apply server changes to local store
|
||||
if (clientChanges.length) {
|
||||
this._log.info("Applying changes locally");
|
||||
// Note that we need to need to apply client changes to the
|
||||
// current tree, not the saved snapshot
|
||||
|
||||
localJson.applyCommands(clientChanges);
|
||||
this._snapshot.data = localJson.data;
|
||||
this._snapshot.version = server.maxVersion;
|
||||
this._store.applyCommands(clientChanges);
|
||||
newSnapshot = this._store.wrap();
|
||||
|
||||
this._core.detectUpdates(self.cb, this._snapshot.data, newSnapshot);
|
||||
let diff = yield;
|
||||
if (diff.length != 0) {
|
||||
this._log.warn("Commands did not apply correctly");
|
||||
this._log.debug("Diff from snapshot+commands -> " +
|
||||
"new snapshot after commands:\n" +
|
||||
this._serializeCommands(diff));
|
||||
// FIXME: do we really want to revert the snapshot here?
|
||||
this._snapshot.data = Utils.deepCopy(savedSnap);
|
||||
this._snapshot.version = savedVersion;
|
||||
}
|
||||
this._snapshot.save();
|
||||
}
|
||||
|
||||
// 3.2) Append server delta to the delta file and upload
|
||||
|
||||
// Generate a new diff, from the current server snapshot to the
|
||||
// current client snapshot. In the case where there are no
|
||||
// conflicts, it should be the same as what the resolver returned
|
||||
// 3.1) Apply server changes to local store
|
||||
if (clientChanges.length) {
|
||||
this._log.info("Applying changes locally");
|
||||
// Note that we need to need to apply client changes to the
|
||||
// current tree, not the saved snapshot
|
||||
|
||||
localJson.applyCommands.async(localJson, self.cb, clientChanges);
|
||||
yield;
|
||||
this._snapshot.data = localJson.data;
|
||||
this._snapshot.version = server.maxVersion;
|
||||
this._store.applyCommands.async(this._store, self.cb, clientChanges);
|
||||
yield;
|
||||
newSnapshot = this._store.wrap();
|
||||
this._core.detectUpdates(self.cb, server.snapshot, newSnapshot);
|
||||
let serverDelta = yield;
|
||||
|
||||
// Log an error if not the same
|
||||
if (!(serverConflicts.length ||
|
||||
Utils.deepEquals(serverChanges, serverDelta)))
|
||||
this._log.warn("Predicted server changes differ from " +
|
||||
"actual server->client diff (can be ignored in many cases)");
|
||||
|
||||
this._log.info("Actual changes for server: " + serverDelta.length);
|
||||
this._log.debug("Actual changes for server: " +
|
||||
this._serializeCommands(serverDelta));
|
||||
|
||||
if (serverDelta.length) {
|
||||
this._log.info("Uploading changes to server");
|
||||
|
||||
this._snapshot.data = newSnapshot;
|
||||
this._snapshot.version = ++server.maxVersion;
|
||||
|
||||
server.deltas.push(serverDelta);
|
||||
|
||||
if (server.formatVersion != STORAGE_FORMAT_VERSION ||
|
||||
this._encryptionChanged) {
|
||||
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, self.cb,
|
||||
this._serializeCommands(server.deltas),
|
||||
this._cryptoId);
|
||||
let data = yield;
|
||||
this._dav.PUT(this.deltasFile, data, self.cb);
|
||||
let deltasPut = yield;
|
||||
|
||||
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: server.snapVersion,
|
||||
maxVersion: this._snapshot.version,
|
||||
snapEncryption: server.snapEncryption,
|
||||
deltasEncryption: Crypto.defaultAlgorithm,
|
||||
itemCount: c}), self.cb);
|
||||
let statusPut = yield;
|
||||
|
||||
if (deltasPut.status >= 200 && deltasPut.status < 300 &&
|
||||
statusPut.status >= 200 && statusPut.status < 300) {
|
||||
this._log.info("Successfully updated deltas and status on server");
|
||||
this._snapshot.save();
|
||||
} else {
|
||||
// FIXME: revert snapshot here? - can't, we already applied
|
||||
// updates locally! - need to save and retry
|
||||
this._log.error("Could not update deltas on server");
|
||||
}
|
||||
}
|
||||
this._core.detectUpdates(self.cb, this._snapshot.data, newSnapshot);
|
||||
let diff = yield;
|
||||
if (diff.length != 0) {
|
||||
this._log.warn("Commands did not apply correctly");
|
||||
this._log.debug("Diff from snapshot+commands -> " +
|
||||
"new snapshot after commands:\n" +
|
||||
this._serializeCommands(diff));
|
||||
// FIXME: do we really want to revert the snapshot here?
|
||||
this._snapshot.data = Utils.deepCopy(savedSnap);
|
||||
this._snapshot.version = savedVersion;
|
||||
}
|
||||
this._snapshot.save();
|
||||
}
|
||||
|
||||
this._log.info("Sync complete");
|
||||
synced = true;
|
||||
// 3.2) Append server delta to the delta file and upload
|
||||
|
||||
} catch (e) {
|
||||
throw e;
|
||||
// Generate a new diff, from the current server snapshot to the
|
||||
// current client snapshot. In the case where there are no
|
||||
// conflicts, it should be the same as what the resolver returned
|
||||
|
||||
newSnapshot = this._store.wrap();
|
||||
this._core.detectUpdates(self.cb, server.snapshot, newSnapshot);
|
||||
let serverDelta = yield;
|
||||
|
||||
// Log an error if not the same
|
||||
if (!(serverConflicts.length ||
|
||||
Utils.deepEquals(serverChanges, serverDelta)))
|
||||
this._log.warn("Predicted server changes differ from " +
|
||||
"actual server->client diff (can be ignored in many cases)");
|
||||
|
||||
this._log.info("Actual changes for server: " + serverDelta.length);
|
||||
this._log.debug("Actual changes for server: " +
|
||||
this._serializeCommands(serverDelta));
|
||||
|
||||
if (serverDelta.length) {
|
||||
this._log.info("Uploading changes to server");
|
||||
|
||||
this._snapshot.data = newSnapshot;
|
||||
this._snapshot.version = ++server.maxVersion;
|
||||
|
||||
server.deltas.push(serverDelta);
|
||||
|
||||
if (server.formatVersion != ENGINE_STORAGE_FORMAT_VERSION ||
|
||||
this._encryptionChanged) {
|
||||
this._fullUpload.async(this, self.cb);
|
||||
let status = yield;
|
||||
if (!status)
|
||||
this._log.error("Could not upload files to server"); // eep?
|
||||
|
||||
} finally {
|
||||
let ok = false;
|
||||
if (locked) {
|
||||
this._dav.unlock.async(this._dav, self.cb);
|
||||
ok = yield;
|
||||
}
|
||||
if (ok && synced) {
|
||||
this._os.notifyObservers(null, this._osPrefix + "sync:success", "");
|
||||
self.done(true);
|
||||
} else {
|
||||
this._os.notifyObservers(null, this._osPrefix + "sync:error", "");
|
||||
self.done(false);
|
||||
Crypto.PBEencrypt.async(Crypto, self.cb,
|
||||
this._serializeCommands(server.deltas),
|
||||
this._engineId);
|
||||
let data = yield;
|
||||
DAV.PUT(this.deltasFile, data, self.cb);
|
||||
let deltasPut = yield;
|
||||
|
||||
let c = 0;
|
||||
for (GUID in this._snapshot.data)
|
||||
c++;
|
||||
|
||||
DAV.PUT(this.statusFile,
|
||||
this._json.encode(
|
||||
{GUID: this._snapshot.GUID,
|
||||
formatVersion: ENGINE_STORAGE_FORMAT_VERSION,
|
||||
snapVersion: server.snapVersion,
|
||||
maxVersion: this._snapshot.version,
|
||||
snapEncryption: server.snapEncryption,
|
||||
deltasEncryption: Crypto.defaultAlgorithm,
|
||||
itemCount: c}), self.cb);
|
||||
let statusPut = yield;
|
||||
|
||||
if (deltasPut.status >= 200 && deltasPut.status < 300 &&
|
||||
statusPut.status >= 200 && statusPut.status < 300) {
|
||||
this._log.info("Successfully updated deltas and status on server");
|
||||
this._snapshot.save();
|
||||
} else {
|
||||
// FIXME: revert snapshot here? - can't, we already applied
|
||||
// updates locally! - need to save and retry
|
||||
this._log.error("Could not update deltas on server");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._log.info("Sync complete");
|
||||
self.done(true);
|
||||
},
|
||||
|
||||
/* Get the deltas/combined updates from the server
|
||||
|
@ -501,7 +465,7 @@ Engine.prototype = {
|
|||
snapshot: null, deltas: null, updates: null};
|
||||
|
||||
this._log.debug("Getting status file from server");
|
||||
this._dav.GET(this.statusFile, self.cb);
|
||||
DAV.GET(this.statusFile, self.cb);
|
||||
let resp = yield;
|
||||
let status = resp.status;
|
||||
|
||||
|
@ -514,12 +478,15 @@ Engine.prototype = {
|
|||
let snap = new SnapshotStore();
|
||||
|
||||
// Bail out if the server has a newer format version than we can parse
|
||||
if (status.formatVersion > STORAGE_FORMAT_VERSION) {
|
||||
if (status.formatVersion > ENGINE_STORAGE_FORMAT_VERSION) {
|
||||
this._log.error("Server uses storage format v" + status.formatVersion +
|
||||
", this client understands up to v" + STORAGE_FORMAT_VERSION);
|
||||
", this client understands up to v" + ENGINE_STORAGE_FORMAT_VERSION);
|
||||
break;
|
||||
}
|
||||
|
||||
this._getSymKey.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
if (status.formatVersion == 0) {
|
||||
ret.snapEncryption = status.snapEncryption = "none";
|
||||
ret.deltasEncryption = status.deltasEncryption = "none";
|
||||
|
@ -543,23 +510,23 @@ Engine.prototype = {
|
|||
this._log.info("Local snapshot is out of date");
|
||||
|
||||
this._log.info("Downloading server snapshot");
|
||||
this._dav.GET(this.snapshotFile, self.cb);
|
||||
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,
|
||||
this._engineId,
|
||||
status.snapEncryption);
|
||||
let data = yield;
|
||||
snap.data = this._json.decode(data);
|
||||
|
||||
this._log.info("Downloading server deltas");
|
||||
this._dav.GET(this.deltasFile, self.cb);
|
||||
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,
|
||||
this._engineId,
|
||||
status.deltasEncryption);
|
||||
data = yield;
|
||||
allDeltas = this._json.decode(data);
|
||||
|
@ -571,12 +538,12 @@ Engine.prototype = {
|
|||
snap.data = Utils.deepCopy(this._snapshot.data);
|
||||
|
||||
this._log.info("Downloading server deltas");
|
||||
this._dav.GET(this.deltasFile, self.cb);
|
||||
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,
|
||||
this._engineId,
|
||||
status.deltasEncryption);
|
||||
let data = yield;
|
||||
allDeltas = this._json.decode(data);
|
||||
|
@ -588,12 +555,12 @@ Engine.prototype = {
|
|||
|
||||
// FIXME: could optimize this case by caching deltas file
|
||||
this._log.info("Downloading server deltas");
|
||||
this._dav.GET(this.deltasFile, self.cb);
|
||||
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,
|
||||
this._engineId,
|
||||
status.deltasEncryption);
|
||||
let data = yield;
|
||||
allDeltas = this._json.decode(data);
|
||||
|
@ -605,7 +572,8 @@ Engine.prototype = {
|
|||
}
|
||||
|
||||
for (var i = 0; i < deltas.length; i++) {
|
||||
snap.applyCommands(deltas[i]);
|
||||
snap.applyCommands.async(snap, self.cb, deltas[i]);
|
||||
yield;
|
||||
}
|
||||
|
||||
ret.status = 0;
|
||||
|
@ -636,7 +604,7 @@ Engine.prototype = {
|
|||
this._snapshot.save();
|
||||
|
||||
ret.status = 0;
|
||||
ret.formatVersion = STORAGE_FORMAT_VERSION;
|
||||
ret.formatVersion = ENGINE_STORAGE_FORMAT_VERSION;
|
||||
ret.maxVersion = this._snapshot.version;
|
||||
ret.snapVersion = this._snapshot.version;
|
||||
ret.snapEncryption = Crypto.defaultAlgorithm;
|
||||
|
@ -659,16 +627,34 @@ Engine.prototype = {
|
|||
let self = yield;
|
||||
let ret = false;
|
||||
|
||||
Crypto.PBEkeygen.async(Crypto, self.cb);
|
||||
let symkey = yield;
|
||||
this._engineId.setTempPassword(symkey);
|
||||
if (!this._engineId.password)
|
||||
throw "Could not generate a symmetric encryption key";
|
||||
|
||||
Crypto.RSAencrypt.async(Crypto, self.cb,
|
||||
this._engineId.password, this._pbeId);
|
||||
let enckey = yield;
|
||||
if (!enckey)
|
||||
throw "Could not encrypt symmetric encryption key";
|
||||
|
||||
let keys = {ring: {}};
|
||||
keys.ring[this._engineId.userHash] = enckey;
|
||||
DAV.PUT(this.keysFile, this._json.encode(keys), self.cb);
|
||||
let resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not upload keyring file.");
|
||||
|
||||
Crypto.PBEencrypt.async(Crypto, self.cb,
|
||||
this._snapshot.serialize(),
|
||||
this._cryptoId);
|
||||
this._engineId);
|
||||
let data = yield;
|
||||
|
||||
this._dav.PUT(this.snapshotFile, data, self.cb);
|
||||
|
||||
DAV.PUT(this.snapshotFile, data, self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not upload snapshot.");
|
||||
|
||||
this._dav.PUT(this.deltasFile, "[]", self.cb);
|
||||
DAV.PUT(this.deltasFile, "[]", self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not upload deltas.");
|
||||
|
||||
|
@ -676,10 +662,10 @@ Engine.prototype = {
|
|||
for (GUID in this._snapshot.data)
|
||||
c++;
|
||||
|
||||
this._dav.PUT(this.statusFile,
|
||||
DAV.PUT(this.statusFile,
|
||||
this._json.encode(
|
||||
{GUID: this._snapshot.GUID,
|
||||
formatVersion: STORAGE_FORMAT_VERSION,
|
||||
formatVersion: ENGINE_STORAGE_FORMAT_VERSION,
|
||||
snapVersion: this._snapshot.version,
|
||||
maxVersion: this._snapshot.version,
|
||||
snapEncryption: Crypto.defaultAlgorithm,
|
||||
|
@ -693,10 +679,99 @@ Engine.prototype = {
|
|||
self.done(ret)
|
||||
},
|
||||
|
||||
_share: function Engine__share(username) {
|
||||
let self = yield;
|
||||
let base = DAV.baseURL;
|
||||
|
||||
this._log.debug("Sharing bookmarks with " + username);
|
||||
|
||||
this._getSymKey.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
// copied from getSymKey
|
||||
DAV.GET(this.keysFile, self.cb);
|
||||
let ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not get keys file.");
|
||||
let keys = this._json.decode(ret.responseText);
|
||||
|
||||
// get the other user's pubkey
|
||||
let hash = Utils.sha1(username);
|
||||
let serverURL = Utils.prefs.getCharPref("serverURL");
|
||||
|
||||
try {
|
||||
DAV.baseURL = serverURL + "user/" + hash + "/"; //FIXME: very ugly!
|
||||
DAV.GET("public/pubkey", self.cb);
|
||||
ret = yield;
|
||||
}
|
||||
catch (e) { throw e; }
|
||||
finally { DAV.baseURL = base; }
|
||||
|
||||
Utils.ensureStatus(ret.status, "Could not get public key for " + username);
|
||||
|
||||
let id = new Identity();
|
||||
id.pubkey = ret.responseText;
|
||||
|
||||
// now encrypt the symkey with their pubkey and upload the new keyring
|
||||
Crypto.RSAencrypt.async(Crypto, self.cb, this._engineId.password, id);
|
||||
let enckey = yield;
|
||||
if (!enckey)
|
||||
throw "Could not encrypt symmetric encryption key";
|
||||
|
||||
keys.ring[hash] = enckey;
|
||||
DAV.PUT(this.keysFile, this._json.encode(keys), self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not upload keyring file.");
|
||||
|
||||
this._createShare(username, username);
|
||||
|
||||
this._log.debug("All done sharing!");
|
||||
|
||||
self.done(true);
|
||||
},
|
||||
|
||||
// FIXME: EEK bookmarks specific
|
||||
_createShare: function Engine__createShare(id, title) {
|
||||
let bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
|
||||
getService(Ci.nsINavBookmarksService);
|
||||
let ans = Cc["@mozilla.org/browser/annotation-service;1"].
|
||||
getService(Ci.nsIAnnotationService);
|
||||
|
||||
let root;
|
||||
let a = ans.getItemsWithAnnotation("weave/mounted-shares-folder", {});
|
||||
if (a.length == 1)
|
||||
root = a[0];
|
||||
|
||||
if (!root) {
|
||||
root = bms.createFolder(bms.toolbarFolder, "Shared Folders",
|
||||
bms.DEFAULT_INDEX);
|
||||
ans.setItemAnnotation(root, "weave/mounted-shares-folder", true, 0,
|
||||
ans.EXPIRE_NEVER);
|
||||
}
|
||||
|
||||
let item
|
||||
a = ans.getItemsWithAnnotation("weave/mounted-share-id", {});
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
if (ans.getItemAnnotation(a[i], "weave/mounted-share-id") == id) {
|
||||
item = a[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
let newId = bms.createFolder(root, title, bms.DEFAULT_INDEX);
|
||||
ans.setItemAnnotation(newId, "weave/mounted-share-id", id, 0,
|
||||
ans.EXPIRE_NEVER);
|
||||
}
|
||||
},
|
||||
|
||||
sync: function Engine_sync(onComplete) {
|
||||
return this._sync.async(this, onComplete);
|
||||
},
|
||||
|
||||
share: function Engine_share(onComplete, username) {
|
||||
return this._share.async(this, onComplete, username);
|
||||
},
|
||||
|
||||
resetServer: function Engine_resetServer(onComplete) {
|
||||
return this._resetServer.async(this, onComplete);
|
||||
},
|
||||
|
@ -706,8 +781,8 @@ Engine.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
function BookmarksEngine(davCollection, cryptoId) {
|
||||
this._init(davCollection, cryptoId);
|
||||
function BookmarksEngine(davCollection, pbeId) {
|
||||
this._init(davCollection, pbeId);
|
||||
}
|
||||
BookmarksEngine.prototype = {
|
||||
get name() { return "bookmarks-engine"; },
|
||||
|
@ -726,12 +801,99 @@ BookmarksEngine.prototype = {
|
|||
if (!this.__store)
|
||||
this.__store = new BookmarksStore();
|
||||
return this.__store;
|
||||
},
|
||||
|
||||
syncMounts: function BmkEngine_syncMounts(onComplete) {
|
||||
this._syncMounts.async(this, onComplete);
|
||||
},
|
||||
_syncMounts: function BmkEngine__syncMounts() {
|
||||
let self = yield;
|
||||
let mounts = this._store.findMounts();
|
||||
|
||||
for (i = 0; i < mounts.length; i++) {
|
||||
try {
|
||||
this._syncOneMount.async(this, self.cb, mounts[i]);
|
||||
yield;
|
||||
} catch (e) {
|
||||
this._log.warn("Could not sync shared folder from " + mounts[i].userid);
|
||||
this._log.trace(Utils.stackTrace(e));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_syncOneMount: function BmkEngine__syncOneMount(mountData) {
|
||||
let self = yield;
|
||||
let user = mountData.userid;
|
||||
let base = DAV.baseURL;
|
||||
let serverURL = Utils.prefs.getCharPref("serverURL");
|
||||
let snap = new SnapshotStore();
|
||||
|
||||
this._log.debug("Syncing shared folder from user " + user);
|
||||
|
||||
try {
|
||||
let hash = Utils.sha1(user);
|
||||
DAV.baseURL = serverURL + "user/" + hash + "/"; //FIXME: very ugly!
|
||||
|
||||
this._getSymKey.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
this._log.trace("Getting status file for " + user);
|
||||
DAV.GET(this.statusFile, self.cb);
|
||||
let resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not download status file.");
|
||||
let status = this._json.decode(resp.responseText);
|
||||
|
||||
this._log.trace("Downloading server snapshot for " + user);
|
||||
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._engineId, status.snapEncryption);
|
||||
let data = yield;
|
||||
snap.data = this._json.decode(data);
|
||||
|
||||
this._log.trace("Downloading server deltas for " + user);
|
||||
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._engineId, status.deltasEncryption);
|
||||
data = yield;
|
||||
deltas = this._json.decode(data);
|
||||
}
|
||||
catch (e) { throw e; }
|
||||
finally { DAV.baseURL = base; }
|
||||
|
||||
// apply deltas to get current snapshot
|
||||
for (var i = 0; i < deltas.length; i++) {
|
||||
snap.applyCommands.async(snap, self.cb, deltas[i]);
|
||||
yield;
|
||||
}
|
||||
|
||||
// prune tree / get what we want
|
||||
for (let guid in snap.data) {
|
||||
if (snap.data[guid].type != "bookmark")
|
||||
delete snap.data[guid];
|
||||
else
|
||||
snap.data[guid].parentGUID = mountData.rootGUID;
|
||||
}
|
||||
|
||||
this._log.trace("Got bookmarks fror " + user + ", comparing with local copy");
|
||||
this._core.detectUpdates(self.cb, mountData.snapshot, snap.data);
|
||||
let diff = yield;
|
||||
|
||||
// FIXME: should make sure all GUIDs here live under the mountpoint
|
||||
this._log.trace("Applying changes to folder from " + user);
|
||||
this._store.applyCommands.async(this._store, self.cb, diff);
|
||||
yield;
|
||||
|
||||
this._log.trace("Shared folder from " + user + " successfully synced!");
|
||||
}
|
||||
};
|
||||
BookmarksEngine.prototype.__proto__ = new Engine();
|
||||
|
||||
function HistoryEngine(davCollection, cryptoId) {
|
||||
this._init(davCollection, cryptoId);
|
||||
function HistoryEngine(davCollection, pbeId) {
|
||||
this._init(davCollection, pbeId);
|
||||
}
|
||||
HistoryEngine.prototype = {
|
||||
get name() { return "history-engine"; },
|
||||
|
@ -754,7 +916,6 @@ HistoryEngine.prototype = {
|
|||
};
|
||||
HistoryEngine.prototype.__proto__ = new Engine();
|
||||
|
||||
|
||||
// Jono: the following is copy-and-paste code
|
||||
function CookieEngine(davCollection, cryptoId) {
|
||||
this._init(davCollection, cryptoId);
|
||||
|
|
|
@ -66,13 +66,20 @@ 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; },
|
||||
get userHash() { return Utils.sha1(this.username); },
|
||||
|
||||
_privkey: null,
|
||||
get privkey() { return this._privkey; },
|
||||
set privkey(value) { this._privkey = value; },
|
||||
|
||||
// FIXME: get this from the privkey using crypto.js?
|
||||
_pubkey: null,
|
||||
get pubkey() { return this._pubkey; },
|
||||
set pubkey(value) { this._pubkey = value; },
|
||||
|
||||
_password: null,
|
||||
get password() {
|
||||
if (this._password === null)
|
||||
if (!this._password)
|
||||
return findPassword(this.realm, this.username);
|
||||
return this._password;
|
||||
},
|
||||
|
|
|
@ -58,28 +58,36 @@ const ONE_BYTE = 1;
|
|||
const ONE_KILOBYTE = 1024 * ONE_BYTE;
|
||||
const ONE_MEGABYTE = 1024 * ONE_KILOBYTE;
|
||||
|
||||
const Log4Moz = {};
|
||||
Log4Moz.Level = {};
|
||||
Log4Moz.Level.Fatal = 70;
|
||||
Log4Moz.Level.Error = 60;
|
||||
Log4Moz.Level.Warn = 50;
|
||||
Log4Moz.Level.Info = 40;
|
||||
Log4Moz.Level.Config = 30;
|
||||
Log4Moz.Level.Debug = 20;
|
||||
Log4Moz.Level.Trace = 10;
|
||||
Log4Moz.Level.All = 0;
|
||||
let Log4Moz = {
|
||||
Level: {
|
||||
Fatal: 70,
|
||||
Error: 60,
|
||||
Warn: 50,
|
||||
Info: 40,
|
||||
Config: 30,
|
||||
Debug: 20,
|
||||
Trace: 10,
|
||||
All: 0,
|
||||
Desc: {
|
||||
70: "FATAL",
|
||||
60: "ERROR",
|
||||
50: "WARN",
|
||||
40: "INFO",
|
||||
30: "CONFIG",
|
||||
20: "DEBUG",
|
||||
10: "TRACE",
|
||||
0: "ALL"
|
||||
}
|
||||
},
|
||||
|
||||
Log4Moz.Level.Desc = {
|
||||
70: "FATAL",
|
||||
60: "ERROR",
|
||||
50: "WARN",
|
||||
40: "INFO",
|
||||
30: "CONFIG",
|
||||
20: "DEBUG",
|
||||
10: "TRACE",
|
||||
0: "ALL"
|
||||
get Service() {
|
||||
delete Log4Moz.Service;
|
||||
Log4Moz.Service = new Log4MozService();
|
||||
return Log4Moz.Service;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* LogMessage
|
||||
* Encapsulates a single log event's data
|
||||
|
@ -389,6 +397,11 @@ FileAppender.prototype = {
|
|||
} catch(e) {
|
||||
dump("Error writing file:\n" + e);
|
||||
}
|
||||
},
|
||||
|
||||
clear: function FApp_clear() {
|
||||
this.closeStream();
|
||||
this._file.remove(false);
|
||||
}
|
||||
};
|
||||
FileAppender.prototype.__proto__ = new Appender();
|
||||
|
@ -495,5 +508,3 @@ Log4MozService.prototype = {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
Log4Moz.Service = new Log4MozService();
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const EXPORTED_SYMBOLS = ['WeaveSyncService'];
|
||||
const EXPORTED_SYMBOLS = ['Weave'];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
@ -45,6 +45,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
Cu.import("resource://weave/log4moz.js");
|
||||
Cu.import("resource://weave/constants.js");
|
||||
Cu.import("resource://weave/util.js");
|
||||
Cu.import("resource://weave/wrap.js");
|
||||
Cu.import("resource://weave/crypto.js");
|
||||
Cu.import("resource://weave/engines.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
|
@ -52,26 +53,45 @@ Cu.import("resource://weave/identity.js");
|
|||
Cu.import("resource://weave/async.js");
|
||||
|
||||
Function.prototype.async = Async.sugar;
|
||||
let Crypto = new WeaveCrypto();
|
||||
|
||||
// for export
|
||||
let Weave = {};
|
||||
Cu.import("resource://weave/constants.js", Weave);
|
||||
Cu.import("resource://weave/util.js", Weave);
|
||||
Cu.import("resource://weave/async.js", Weave);
|
||||
Cu.import("resource://weave/crypto.js", Weave);
|
||||
Cu.import("resource://weave/identity.js", Weave);
|
||||
Cu.import("resource://weave/dav.js", Weave);
|
||||
Cu.import("resource://weave/stores.js", Weave);
|
||||
Cu.import("resource://weave/syncCores.js", Weave);
|
||||
Cu.import("resource://weave/engines.js", Weave);
|
||||
Cu.import("resource://weave/service.js", Weave);
|
||||
Utils.lazy(Weave, 'Service', WeaveSvc);
|
||||
|
||||
/*
|
||||
* Service singleton
|
||||
* Main entry point into Weave's sync framework
|
||||
*/
|
||||
|
||||
function WeaveSyncService() { this._init(); }
|
||||
WeaveSyncService.prototype = {
|
||||
function WeaveSvc() {
|
||||
this._initLogs();
|
||||
this._log.info("Weave Sync Service Initializing");
|
||||
|
||||
__prefs: null,
|
||||
get _prefs() {
|
||||
if (!this.__prefs) {
|
||||
this.__prefs = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService);
|
||||
this.__prefs = this.__prefs.getBranch(PREFS_BRANCH);
|
||||
this.__prefs.QueryInterface(Ci.nsIPrefBranch2);
|
||||
}
|
||||
return this.__prefs;
|
||||
},
|
||||
Utils.prefs.addObserver("", this, false);
|
||||
|
||||
if (!this.enabled) {
|
||||
this._log.info("Weave Sync disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
this._setSchedule(this.schedule);
|
||||
}
|
||||
WeaveSvc.prototype = {
|
||||
|
||||
_notify: Wrap.notify,
|
||||
_lock: Wrap.lock,
|
||||
_localLock: Wrap.localLock,
|
||||
_osPrefix: "weave:service:",
|
||||
|
||||
__os: null,
|
||||
get _os() {
|
||||
|
@ -89,38 +109,29 @@ WeaveSyncService.prototype = {
|
|||
return this.__dirSvc;
|
||||
},
|
||||
|
||||
__dav: null,
|
||||
get _dav() {
|
||||
if (!this.__dav)
|
||||
this.__dav = new DAVCollection();
|
||||
return this.__dav;
|
||||
},
|
||||
|
||||
// FIXME: engines should be loaded dynamically somehow / need API to register
|
||||
|
||||
__bmkEngine: null,
|
||||
get _bmkEngine() {
|
||||
if (!this.__bmkEngine)
|
||||
this.__bmkEngine = new BookmarksEngine(this._dav, this._cryptoId);
|
||||
this.__bmkEngine = new BookmarksEngine(DAV, this._cryptoId);
|
||||
return this.__bmkEngine;
|
||||
},
|
||||
|
||||
__histEngine: null,
|
||||
get _histEngine() {
|
||||
if (!this.__histEngine)
|
||||
this.__histEngine = new HistoryEngine(this._dav, this._cryptoId);
|
||||
this.__histEngine = new HistoryEngine(DAV, this._cryptoId);
|
||||
return this.__histEngine;
|
||||
},
|
||||
|
||||
__cookieEngine: null,
|
||||
get _cookieEngine() {
|
||||
if (!this.__cookieEngine)
|
||||
this.__cookieEngine = new CookieEngine(this._dav, this._cryptoId);
|
||||
this.__cookieEngine = new CookieEngine(DAV, this._cryptoId);
|
||||
return this.__cookieEngine;
|
||||
},
|
||||
|
||||
// Logger object
|
||||
_log: null,
|
||||
|
||||
// Timer object for automagically syncing
|
||||
_scheduleTimer: null,
|
||||
|
@ -141,13 +152,13 @@ WeaveSyncService.prototype = {
|
|||
},
|
||||
|
||||
get username() {
|
||||
return this._prefs.getCharPref("username");
|
||||
return Utils.prefs.getCharPref("username");
|
||||
},
|
||||
set username(value) {
|
||||
if (value)
|
||||
this._prefs.setCharPref("username", value);
|
||||
Utils.prefs.setCharPref("username", value);
|
||||
else
|
||||
this._prefs.clearUserPref("username");
|
||||
Utils.prefs.clearUserPref("username");
|
||||
|
||||
// fixme - need to loop over all Identity objects - needs some rethinking...
|
||||
this._mozId.username = value;
|
||||
|
@ -160,59 +171,22 @@ WeaveSyncService.prototype = {
|
|||
get passphrase() { return this._cryptoId.password; },
|
||||
set passphrase(value) { this._cryptoId.password = value; },
|
||||
|
||||
get userPath() {
|
||||
this._log.info("Hashing username " + this.username);
|
||||
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Ci.nsICryptoHash);
|
||||
hasher.init(hasher.SHA1);
|
||||
|
||||
let data = converter.convertToByteArray(this.username, {});
|
||||
hasher.update(data, data.length);
|
||||
let rawHash = hasher.finish(false);
|
||||
|
||||
// return the two-digit hexadecimal code for a byte
|
||||
function toHexString(charCode) {
|
||||
return ("0" + charCode.toString(16)).slice(-2);
|
||||
}
|
||||
|
||||
let hash = [toHexString(rawHash.charCodeAt(i)) for (i in rawHash)].join("");
|
||||
this._log.debug("Username hashes to " + hash);
|
||||
return hash;
|
||||
},
|
||||
get userPath() { return this._mozId.userHash; },
|
||||
|
||||
get currentUser() {
|
||||
if (this._dav.loggedIn)
|
||||
if (DAV.loggedIn)
|
||||
return this.username;
|
||||
return null;
|
||||
},
|
||||
|
||||
get enabled() {
|
||||
return this._prefs.getBoolPref("enabled");
|
||||
return Utils.prefs.getBoolPref("enabled");
|
||||
},
|
||||
|
||||
get schedule() {
|
||||
if (!this.enabled)
|
||||
return 0; // manual/off
|
||||
return this._prefs.getIntPref("schedule");
|
||||
},
|
||||
|
||||
_init: function WeaveSync__init() {
|
||||
this._initLogs();
|
||||
this._log.info("Weave Sync Service Initializing");
|
||||
|
||||
this._prefs.addObserver("", this, false);
|
||||
|
||||
if (!this.enabled) {
|
||||
this._log.info("Weave Sync disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
this._setSchedule(this.schedule);
|
||||
return Utils.prefs.getIntPref("schedule");
|
||||
},
|
||||
|
||||
_setSchedule: function Weave__setSchedule(schedule) {
|
||||
|
@ -259,17 +233,19 @@ WeaveSyncService.prototype = {
|
|||
|
||||
_initLogs: function WeaveSync__initLogs() {
|
||||
this._log = Log4Moz.Service.getLogger("Service.Main");
|
||||
this._log.level =
|
||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.service.main")];
|
||||
|
||||
let formatter = Log4Moz.Service.newFormatter("basic");
|
||||
let root = Log4Moz.Service.rootLogger;
|
||||
root.level = Log4Moz.Level[this._prefs.getCharPref("log.rootLogger")];
|
||||
root.level = Log4Moz.Level[Utils.prefs.getCharPref("log.rootLogger")];
|
||||
|
||||
let capp = Log4Moz.Service.newAppender("console", formatter);
|
||||
capp.level = Log4Moz.Level[this._prefs.getCharPref("log.appender.console")];
|
||||
capp.level = Log4Moz.Level[Utils.prefs.getCharPref("log.appender.console")];
|
||||
root.addAppender(capp);
|
||||
|
||||
let dapp = Log4Moz.Service.newAppender("dump", formatter);
|
||||
dapp.level = Log4Moz.Level[this._prefs.getCharPref("log.appender.dump")];
|
||||
dapp.level = Log4Moz.Level[Utils.prefs.getCharPref("log.appender.dump")];
|
||||
root.addAppender(dapp);
|
||||
|
||||
let brief = this._dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
@ -285,222 +261,123 @@ WeaveSyncService.prototype = {
|
|||
if (!verbose.exists())
|
||||
verbose.create(verbose.NORMAL_FILE_TYPE, PERMS_FILE);
|
||||
|
||||
let fapp = Log4Moz.Service.newFileAppender("rotating", brief, formatter);
|
||||
fapp.level = Log4Moz.Level[this._prefs.getCharPref("log.appender.briefLog")];
|
||||
root.addAppender(fapp);
|
||||
let vapp = Log4Moz.Service.newFileAppender("rotating", verbose, formatter);
|
||||
vapp.level = Log4Moz.Level[this._prefs.getCharPref("log.appender.debugLog")];
|
||||
root.addAppender(vapp);
|
||||
this._briefApp = Log4Moz.Service.newFileAppender("rotating", brief, formatter);
|
||||
this._briefApp.level = Log4Moz.Level[Utils.prefs.getCharPref("log.appender.briefLog")];
|
||||
root.addAppender(this._briefApp);
|
||||
this._debugApp = Log4Moz.Service.newFileAppender("rotating", verbose, formatter);
|
||||
this._debugApp.level = Log4Moz.Level[Utils.prefs.getCharPref("log.appender.debugLog")];
|
||||
root.addAppender(this._debugApp);
|
||||
},
|
||||
|
||||
_lock: function weaveSync__lock() {
|
||||
if (this._locked) {
|
||||
this._log.warn("Service lock failed: already locked");
|
||||
this._os.notifyObservers(null, "weave:service-lock:error", "");
|
||||
return false;
|
||||
}
|
||||
this._locked = true;
|
||||
this._log.debug("Service lock acquired");
|
||||
this._os.notifyObservers(null, "weave:service-lock:success", "");
|
||||
return true;
|
||||
clearLogs: function WeaveSvc_clearLogs() {
|
||||
this._briefApp.clear();
|
||||
this._debugApp.clear();
|
||||
},
|
||||
|
||||
_unlock: function WeaveSync__unlock() {
|
||||
this._locked = false;
|
||||
this._log.debug("Service lock released");
|
||||
this._os.notifyObservers(null, "weave:service-unlock:success", "");
|
||||
},
|
||||
|
||||
_login: function WeaveSync__login() {
|
||||
_uploadVersion: function WeaveSync__uploadVersion() {
|
||||
let self = yield;
|
||||
|
||||
try {
|
||||
this._log.debug("Logging in");
|
||||
this._os.notifyObservers(null, "weave:service-login:start", "");
|
||||
DAV.MKCOL("meta", self.cb);
|
||||
let ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create meta information directory";
|
||||
|
||||
if (!this.username)
|
||||
throw "No username set, login failed";
|
||||
if (!this.password)
|
||||
throw "No password given or found in password manager";
|
||||
DAV.PUT("meta/version", STORAGE_FORMAT_VERSION, self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not upload server version file");
|
||||
},
|
||||
|
||||
let serverURL = this._prefs.getCharPref("serverURL");
|
||||
this._dav.baseURL = serverURL + "user/" + this.userPath + "/";
|
||||
this._log.info("Using server URL: " + this._dav.baseURL);
|
||||
// force a server wipe when the version is lower than ours (or there is none)
|
||||
_versionCheck: function WeaveSync__versionCheck() {
|
||||
let self = yield;
|
||||
|
||||
this._dav.login.async(this._dav, self.cb, this.username, this.password);
|
||||
let success = yield;
|
||||
DAV.GET("meta/version", self.cb);
|
||||
let ret = yield;
|
||||
|
||||
// FIXME: we want to limit this to when we get a 404!
|
||||
if (!success) {
|
||||
this._log.debug("Attempting to create user directory");
|
||||
if (!Utils.checkStatus(ret.status)) {
|
||||
this._log.info("Server has no version file. Wiping server data.");
|
||||
this._serverWipe.async(this, self.cb);
|
||||
yield;
|
||||
this._uploadVersion.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
this._dav.baseURL = serverURL;
|
||||
this._dav.MKCOL("user/" + this.userPath, self.cb);
|
||||
let ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create user directory. Got status: " + ret.status;
|
||||
} else if (ret.responseText < STORAGE_FORMAT_VERSION) {
|
||||
this._log.info("Server version too low. Wiping server data.");
|
||||
this._serverWipe.async(this, self.cb);
|
||||
yield;
|
||||
this._uploadVersion.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
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 {
|
||||
// FIXME: hack to wipe everyone's server data... needs to be removed at some point
|
||||
this._serverWipe.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
// 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.warn(Async.exceptionStr(self, e));
|
||||
this._log.trace(e.trace);
|
||||
this._os.notifyObservers(null, "weave:service-login:error", "");
|
||||
self.done(false);
|
||||
} else if (ret.responseText > STORAGE_FORMAT_VERSION) {
|
||||
// FIXME: should we do something here?
|
||||
}
|
||||
},
|
||||
|
||||
_resetLock: function WeaveSync__resetLock() {
|
||||
_checkUserDir: function WeaveSvc__checkUserDir() {
|
||||
let self = yield;
|
||||
let success = false;
|
||||
|
||||
try {
|
||||
this._log.debug("Resetting server lock");
|
||||
this._os.notifyObservers(null, "weave:server-lock-reset:start", "");
|
||||
this._log.trace("Checking user directory exists");
|
||||
|
||||
this._dav.forceUnlock.async(this._dav, self.cb);
|
||||
success = yield;
|
||||
let serverURL = Utils.prefs.getCharPref("serverURL");
|
||||
if (serverURL[serverURL.length-1] != '/')
|
||||
serverURL = serverURL + '/';
|
||||
|
||||
} catch (e) {
|
||||
throw e;
|
||||
DAV.baseURL = serverURL;
|
||||
DAV.MKCOL("user/" + this.userPath, self.cb);
|
||||
let ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create user directory";
|
||||
|
||||
} finally {
|
||||
if (success) {
|
||||
this._log.debug("Server lock reset successful");
|
||||
this._os.notifyObservers(null, "weave:server-lock-reset:success", "");
|
||||
} else {
|
||||
this._log.debug("Server lock reset failed");
|
||||
this._os.notifyObservers(null, "weave:server-lock-reset:error", "");
|
||||
}
|
||||
self.done(success);
|
||||
DAV.baseURL = serverURL + "user/" + this.userPath + "/";
|
||||
this._log.info("Using server URL: " + DAV.baseURL);
|
||||
},
|
||||
|
||||
_keyCheck: function WeaveSvc__keyCheck() {
|
||||
let self = yield;
|
||||
|
||||
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.privkey = keyResp.responseText;
|
||||
Crypto.RSAkeydecrypt.async(Crypto, self.cb, this._cryptoId);
|
||||
this._cryptoId.pubkey = yield;
|
||||
|
||||
} else {
|
||||
this._generateKeys.async(this, self.cb);
|
||||
yield;
|
||||
}
|
||||
},
|
||||
|
||||
_sync: function WeaveSync__sync() {
|
||||
let self = yield;
|
||||
let success = false;
|
||||
|
||||
try {
|
||||
if (!this._lock())
|
||||
return;
|
||||
|
||||
this._os.notifyObservers(null, "weave:service:sync:start", "");
|
||||
|
||||
if (this._prefs.getBoolPref("bookmarks")) {
|
||||
this._bmkEngine.sync(self.cb);
|
||||
yield;
|
||||
}
|
||||
if (this._prefs.getBoolPref("history")) {
|
||||
this._histEngine.sync(self.cb);
|
||||
yield;
|
||||
}
|
||||
if (this._prefs.getBoolPref("cookies")) {
|
||||
this._cookieEngine.sync(self.cb);
|
||||
yield;
|
||||
}
|
||||
success = true;
|
||||
this._unlock();
|
||||
|
||||
} catch (e) {
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
if (success)
|
||||
this._os.notifyObservers(null, "weave:service:sync:success", "");
|
||||
else
|
||||
this._os.notifyObservers(null, "weave:service:sync:error", "");
|
||||
self.done();
|
||||
}
|
||||
},
|
||||
|
||||
_resetServer: function WeaveSync__resetServer() {
|
||||
_generateKeys: function WeaveSync__generateKeys() {
|
||||
let self = yield;
|
||||
|
||||
if (!this._lock())
|
||||
throw "Could not acrquire lock";
|
||||
this._log.debug("Generating new RSA key");
|
||||
Crypto.RSAkeygen.async(Crypto, self.cb, this._cryptoId);
|
||||
let [privkey, pubkey] = yield;
|
||||
|
||||
this._bmkEngine.resetServer(self.cb);
|
||||
this._histEngine.resetServer(self.cb);
|
||||
this._cryptoId.privkey = privkey;
|
||||
this._cryptoId.pubkey = pubkey;
|
||||
|
||||
this._unlock();
|
||||
self.done();
|
||||
},
|
||||
DAV.MKCOL("private/", self.cb);
|
||||
let ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create private key directory";
|
||||
|
||||
_resetClient: function WeaveSync__resetClient() {
|
||||
let self = yield;
|
||||
DAV.MKCOL("public/", self.cb);
|
||||
ret = yield;
|
||||
if (!ret)
|
||||
throw "Could not create public key directory";
|
||||
|
||||
if (!this._lock())
|
||||
throw "Could not acrquire lock";
|
||||
DAV.PUT("private/privkey", privkey, self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not upload private key");
|
||||
|
||||
this._bmkEngine.resetClient(self.cb);
|
||||
this._histEngine.resetClient(self.cb);
|
||||
|
||||
this._unlock();
|
||||
self.done();
|
||||
},
|
||||
|
||||
_serverWipe: function WeaveSync__serverWipe() {
|
||||
let self = yield;
|
||||
|
||||
this._dav.listFiles.async(this._dav, self.cb);
|
||||
let names = yield;
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
this._dav.DELETE(names[i], self.cb);
|
||||
let resp = yield;
|
||||
this._log.debug(resp.status);
|
||||
}
|
||||
|
||||
self.done();
|
||||
DAV.PUT("public/pubkey", pubkey, self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not upload public key");
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupports]),
|
||||
|
@ -521,35 +398,161 @@ WeaveSyncService.prototype = {
|
|||
|
||||
// These are global (for all engines)
|
||||
|
||||
login: function WeaveSync_login(password, passphrase) {
|
||||
if (!this._lock())
|
||||
return;
|
||||
login: function WeaveSync_login(onComplete, password, passphrase) {
|
||||
this._localLock(this._notify("login", this._login,
|
||||
password, passphrase)).async(this, onComplete);
|
||||
},
|
||||
_login: function WeaveSync__login(password, passphrase) {
|
||||
let self = yield;
|
||||
|
||||
// cache password & passphrase
|
||||
// if null, _login() will try to get them from the pw manager
|
||||
// if null, we'll try to get them from the pw manager below
|
||||
this._mozId.setTempPassword(password);
|
||||
this._cryptoId.setTempPassword(passphrase);
|
||||
let self = this;
|
||||
this._login.async(this, function() {self._unlock()});
|
||||
|
||||
this._log.debug("Logging in");
|
||||
|
||||
if (!this.username)
|
||||
throw "No username set, login failed";
|
||||
if (!this.password)
|
||||
throw "No password given or found in password manager";
|
||||
|
||||
this._checkUserDir.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
DAV.login.async(DAV, self.cb, this.username, this.password);
|
||||
let success = yield;
|
||||
if (!success)
|
||||
throw "Login failed";
|
||||
|
||||
this._versionCheck.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
this._keyCheck.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
self.done(true);
|
||||
},
|
||||
|
||||
logout: function WeaveSync_logout() {
|
||||
this._log.info("Logging out");
|
||||
this._dav.logout();
|
||||
DAV.logout();
|
||||
this._mozId.setTempPassword(null); // clear cached password
|
||||
this._cryptoId.setTempPassword(null); // and passphrase
|
||||
this._os.notifyObservers(null, "weave:service-logout:success", "");
|
||||
this._os.notifyObservers(null, "weave:service:logout:success", "");
|
||||
},
|
||||
|
||||
resetLock: function WeaveSync_resetLock() {
|
||||
if (!this._lock())
|
||||
return;
|
||||
let self = this;
|
||||
this._resetLock.async(this, function() {self._unlock()});
|
||||
resetLock: function WeaveSvc_resetLock(onComplete) {
|
||||
this._notify("reset-server-lock", this._resetLock).async(this, onComplete);
|
||||
},
|
||||
_resetLock: function WeaveSvc__resetLock() {
|
||||
let self = yield;
|
||||
DAV.forceUnlock.async(DAV, self.cb);
|
||||
yield;
|
||||
},
|
||||
|
||||
serverWipe: function WeaveSvc_serverWipe(onComplete) {
|
||||
let cb = function WeaveSvc_serverWipeCb() {
|
||||
let self = yield;
|
||||
this._serverWipe.async(this, self.cb);
|
||||
yield;
|
||||
this.logout();
|
||||
self.done();
|
||||
};
|
||||
this._lock(this._notify("server-wipe", cb)).async(this, onComplete);
|
||||
},
|
||||
_serverWipe: function WeaveSvc__serverWipe() {
|
||||
let self = yield;
|
||||
|
||||
DAV.listFiles.async(DAV, self.cb);
|
||||
let names = yield;
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
DAV.DELETE(names[i], self.cb);
|
||||
let resp = yield;
|
||||
this._log.debug(resp.status);
|
||||
}
|
||||
},
|
||||
|
||||
// These are per-engine
|
||||
|
||||
sync: function WeaveSync_sync() { this._sync.async(this); },
|
||||
resetServer: function WeaveSync_resetServer() { this._resetServer.async(this); },
|
||||
resetClient: function WeaveSync_resetClient() { this._resetClient.async(this); }
|
||||
sync: function WeaveSync_sync(onComplete) {
|
||||
this._lock(this._notify("sync", this._sync)).async(this, onComplete);
|
||||
},
|
||||
_sync: function WeaveSync__sync() {
|
||||
let self = yield;
|
||||
|
||||
if (!DAV.loggedIn)
|
||||
throw "Can't sync: Not logged in";
|
||||
|
||||
this._versionCheck.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
this._keyCheck.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
if (Utils.prefs.getBoolPref("bookmarks")) {
|
||||
this._notify(this._bmkEngine.name + ":sync",
|
||||
this._syncEngine, this._bmkEngine).async(this, self.cb);
|
||||
yield;
|
||||
this._bmkEngine.syncMounts(self.cb); // FIXME
|
||||
yield;
|
||||
}
|
||||
if (Utils.prefs.getBoolPref("history")) {
|
||||
this._notify(this._histEngine.name + ":sync",
|
||||
this._syncEngine, this._histEngine).async(this, self.cb);
|
||||
yield;
|
||||
}
|
||||
if (this._prefs.getBoolPref("cookies")) {
|
||||
this._notify(this._cookieEngine.name + ":sync",
|
||||
this._syncEngine, this._cookieEngine).async(this, self.cb);
|
||||
yield;
|
||||
}
|
||||
},
|
||||
_syncEngine: function WeaveSvc__syncEngine(engine) {
|
||||
let self = yield;
|
||||
engine.sync(self.cb);
|
||||
yield;
|
||||
},
|
||||
|
||||
resetServer: function WeaveSync_resetServer(onComplete) {
|
||||
this._lock(this._notify("reset-server",
|
||||
this._resetServer)).async(this, onComplete);
|
||||
},
|
||||
_resetServer: function WeaveSync__resetServer() {
|
||||
let self = yield;
|
||||
|
||||
if (!DAV.loggedIn)
|
||||
throw "Can't reset server: Not logged in";
|
||||
|
||||
this._bmkEngine.resetServer(self.cb);
|
||||
yield;
|
||||
this._histEngine.resetServer(self.cb);
|
||||
yield;
|
||||
},
|
||||
|
||||
resetClient: function WeaveSync_resetClient(onComplete) {
|
||||
this._localLock(this._notify("reset-client",
|
||||
this._resetClient)).async(this, onComplete);
|
||||
},
|
||||
_resetClient: function WeaveSync__resetClient() {
|
||||
let self = yield;
|
||||
this._bmkEngine.resetClient(self.cb);
|
||||
yield;
|
||||
this._histEngine.resetClient(self.cb);
|
||||
yield;
|
||||
},
|
||||
|
||||
shareBookmarks: function WeaveSync_shareBookmarks(onComplete, username) {
|
||||
this._lock(this._notify("share-bookmarks",
|
||||
this._shareBookmarks,
|
||||
username)).async(this, onComplete);
|
||||
},
|
||||
_shareBookmarks: function WeaveSync__shareBookmarks(username) {
|
||||
let self = yield;
|
||||
this._bmkEngine.share(self.cb, username);
|
||||
let ret = yield;
|
||||
self.done(ret);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -46,6 +46,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 = Async.sugar;
|
||||
|
||||
/*
|
||||
* Data Stores
|
||||
|
@ -57,6 +60,7 @@ function Store() {
|
|||
}
|
||||
Store.prototype = {
|
||||
_logName: "Store",
|
||||
_yieldDuringApply: true,
|
||||
|
||||
__json: null,
|
||||
get _json() {
|
||||
|
@ -71,7 +75,18 @@ Store.prototype = {
|
|||
},
|
||||
|
||||
applyCommands: function Store_applyCommands(commandList) {
|
||||
let self = yield, timer, listener;
|
||||
|
||||
if (this._yieldDuringApply) {
|
||||
listener = new Utils.EventListener(self.cb);
|
||||
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
}
|
||||
|
||||
for (var i = 0; i < commandList.length; i++) {
|
||||
if (this._yieldDuringApply) {
|
||||
timer.initWithCallback(listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
}
|
||||
var command = commandList[i];
|
||||
this._log.debug("Processing command: " + this._json.encode(command));
|
||||
switch (command["action"]) {
|
||||
|
@ -89,6 +104,7 @@ Store.prototype = {
|
|||
break;
|
||||
}
|
||||
}
|
||||
self.done();
|
||||
},
|
||||
|
||||
// override these in derived objects
|
||||
|
@ -308,86 +324,23 @@ BookmarksStore.prototype = {
|
|||
return this.__ans;
|
||||
},
|
||||
|
||||
_getFolderNodes: function BSS__getFolderNodes(folder) {
|
||||
let query = this._hsvc.getNewQuery();
|
||||
query.setFolders([folder], 1);
|
||||
return this._hsvc.executeQuery(query, this._hsvc.getNewQueryOptions()).root;
|
||||
},
|
||||
|
||||
_wrapNode: function BSS__wrapNode(node) {
|
||||
var items = {};
|
||||
this._wrapNodeInternal(node, items, null, null);
|
||||
return items;
|
||||
},
|
||||
|
||||
_wrapNodeInternal: function BSS__wrapNodeInternal(node, items, parentGUID, index) {
|
||||
let GUID = this._bms.getItemGUID(node.itemId);
|
||||
let item = {parentGUID: parentGUID,
|
||||
index: index};
|
||||
|
||||
if (node.type == node.RESULT_TYPE_FOLDER) {
|
||||
if (this._ls.isLivemark(node.itemId)) {
|
||||
item.type = "livemark";
|
||||
let siteURI = this._ls.getSiteURI(node.itemId);
|
||||
let feedURI = this._ls.getFeedURI(node.itemId);
|
||||
item.siteURI = siteURI? siteURI.spec : "";
|
||||
item.feedURI = feedURI? feedURI.spec : "";
|
||||
} else {
|
||||
item.type = "folder";
|
||||
node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
|
||||
node.containerOpen = true;
|
||||
for (var i = 0; i < node.childCount; i++) {
|
||||
this._wrapNodeInternal(node.getChild(i), items, GUID, i);
|
||||
}
|
||||
}
|
||||
item.title = node.title;
|
||||
} else if (node.type == node.RESULT_TYPE_URI ||
|
||||
node.type == node.RESULT_TYPE_QUERY) {
|
||||
if (this._ms.hasMicrosummary(node.itemId)) {
|
||||
item.type = "microsummary";
|
||||
let micsum = this._ms.getMicrosummary(node.itemId);
|
||||
item.generatorURI = micsum.generator.uri.spec; // breaks local generators
|
||||
} else if (node.type == node.RESULT_TYPE_QUERY) {
|
||||
item.type = "query";
|
||||
item.title = node.title;
|
||||
} else {
|
||||
item.type = "bookmark";
|
||||
item.title = node.title;
|
||||
}
|
||||
item.URI = node.uri;
|
||||
item.tags = this._ts.getTagsForURI(Utils.makeURI(node.uri), {});
|
||||
item.keyword = this._bms.getKeywordForBookmark(node.itemId);
|
||||
} else if (node.type == node.RESULT_TYPE_SEPARATOR) {
|
||||
item.type = "separator";
|
||||
} else {
|
||||
this._log.warn("Warning: unknown item type, cannot serialize: " + node.type);
|
||||
return;
|
||||
}
|
||||
|
||||
items[GUID] = item;
|
||||
},
|
||||
|
||||
_getWrappedBookmarks: function BSS__getWrappedBookmarks(folder) {
|
||||
return this._wrapNode(this._getFolderNodes(folder));
|
||||
},
|
||||
|
||||
_resetGUIDsInt: function BSS__resetGUIDsInt(node) {
|
||||
if (this._ans.itemHasAnnotation(node.itemId, "placesInternal/GUID"))
|
||||
this._ans.removeItemAnnotation(node.itemId, "placesInternal/GUID");
|
||||
|
||||
if (node.type == node.RESULT_TYPE_FOLDER &&
|
||||
!this._ls.isLivemark(node.itemId)) {
|
||||
node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
|
||||
node.containerOpen = true;
|
||||
for (var i = 0; i < node.childCount; i++) {
|
||||
this._resetGUIDsInt(node.getChild(i));
|
||||
}
|
||||
_getItemIdForGUID: function BStore__getItemIdForGUID(GUID) {
|
||||
switch (GUID) {
|
||||
case "menu":
|
||||
return this._bms.bookmarksMenuFolder;
|
||||
case "toolbar":
|
||||
return this._bms.toolbarFolder;
|
||||
case "unfiled":
|
||||
return this._bms.unfiledBookmarksFolder;
|
||||
default:
|
||||
return this._bms.getItemIdForGUID(GUID);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_createCommand: function BStore__createCommand(command) {
|
||||
let newId;
|
||||
let parentId = this._bms.getItemIdForGUID(command.data.parentGUID);
|
||||
let parentId = this._getItemIdForGUID(command.data.parentGUID);
|
||||
|
||||
if (parentId < 0) {
|
||||
this._log.warn("Creating node with unknown parent -> reparenting to root");
|
||||
|
@ -398,7 +351,7 @@ BookmarksStore.prototype = {
|
|||
case "query":
|
||||
case "bookmark":
|
||||
case "microsummary": {
|
||||
this._log.info(" -> creating bookmark \"" + command.data.title + "\"");
|
||||
this._log.debug(" -> creating bookmark \"" + command.data.title + "\"");
|
||||
let URI = Utils.makeURI(command.data.URI);
|
||||
newId = this._bms.insertBookmark(parentId,
|
||||
URI,
|
||||
|
@ -409,7 +362,7 @@ BookmarksStore.prototype = {
|
|||
this._bms.setKeywordForBookmark(newId, command.data.keyword);
|
||||
|
||||
if (command.data.type == "microsummary") {
|
||||
this._log.info(" \-> is a microsummary");
|
||||
this._log.debug(" \-> is a microsummary");
|
||||
let genURI = Utils.makeURI(command.data.generatorURI);
|
||||
try {
|
||||
let micsum = this._ms.createMicrosummary(URI, genURI);
|
||||
|
@ -419,21 +372,30 @@ BookmarksStore.prototype = {
|
|||
}
|
||||
} break;
|
||||
case "folder":
|
||||
this._log.info(" -> creating folder \"" + command.data.title + "\"");
|
||||
this._log.debug(" -> creating folder \"" + command.data.title + "\"");
|
||||
newId = this._bms.createFolder(parentId,
|
||||
command.data.title,
|
||||
command.data.index);
|
||||
break;
|
||||
case "livemark":
|
||||
this._log.info(" -> creating livemark \"" + command.data.title + "\"");
|
||||
this._log.debug(" -> creating livemark \"" + command.data.title + "\"");
|
||||
newId = this._ls.createLivemark(parentId,
|
||||
command.data.title,
|
||||
Utils.makeURI(command.data.siteURI),
|
||||
Utils.makeURI(command.data.feedURI),
|
||||
command.data.index);
|
||||
break;
|
||||
case "mounted-share":
|
||||
this._log.debug(" -> creating share mountpoint \"" + command.data.title + "\"");
|
||||
newId = this._bms.createFolder(parentId,
|
||||
command.data.title,
|
||||
command.data.index);
|
||||
|
||||
this._ans.setItemAnnotation(newId, "weave/mounted-share-id",
|
||||
command.data.mountId, 0, this._ans.EXPIRE_NEVER);
|
||||
break;
|
||||
case "separator":
|
||||
this._log.info(" -> creating separator");
|
||||
this._log.debug(" -> creating separator");
|
||||
newId = this._bms.insertSeparator(parentId, command.data.index);
|
||||
break;
|
||||
default:
|
||||
|
@ -445,6 +407,14 @@ BookmarksStore.prototype = {
|
|||
},
|
||||
|
||||
_removeCommand: function BStore__removeCommand(command) {
|
||||
if (command.GUID == "menu" ||
|
||||
command.GUID == "toolbar" ||
|
||||
command.GUID == "unfiled") {
|
||||
this._log.warn("Attempted to remove root node (" + command.GUID +
|
||||
"). Skipping command.");
|
||||
return;
|
||||
}
|
||||
|
||||
var itemId = this._bms.getItemIdForGUID(command.GUID);
|
||||
if (itemId < 0) {
|
||||
this._log.warn("Attempted to remove item " + command.GUID +
|
||||
|
@ -455,15 +425,15 @@ BookmarksStore.prototype = {
|
|||
|
||||
switch (type) {
|
||||
case this._bms.TYPE_BOOKMARK:
|
||||
this._log.info(" -> removing bookmark " + command.GUID);
|
||||
this._log.debug(" -> removing bookmark " + command.GUID);
|
||||
this._bms.removeItem(itemId);
|
||||
break;
|
||||
case this._bms.TYPE_FOLDER:
|
||||
this._log.info(" -> removing folder " + command.GUID);
|
||||
this._log.debug(" -> removing folder " + command.GUID);
|
||||
this._bms.removeFolder(itemId);
|
||||
break;
|
||||
case this._bms.TYPE_SEPARATOR:
|
||||
this._log.info(" -> removing separator " + command.GUID);
|
||||
this._log.debug(" -> removing separator " + command.GUID);
|
||||
this._bms.removeItem(itemId);
|
||||
break;
|
||||
default:
|
||||
|
@ -473,6 +443,14 @@ BookmarksStore.prototype = {
|
|||
},
|
||||
|
||||
_editCommand: function BStore__editCommand(command) {
|
||||
if (command.GUID == "menu" ||
|
||||
command.GUID == "toolbar" ||
|
||||
command.GUID == "unfiled") {
|
||||
this._log.warn("Attempted to edit root node (" + command.GUID +
|
||||
"). Skipping command.");
|
||||
return;
|
||||
}
|
||||
|
||||
var itemId = this._bms.getItemIdForGUID(command.GUID);
|
||||
if (itemId < 0) {
|
||||
this._log.warn("Item for GUID " + command.GUID + " not found. Skipping.");
|
||||
|
@ -481,8 +459,12 @@ BookmarksStore.prototype = {
|
|||
|
||||
for (let key in command.data) {
|
||||
switch (key) {
|
||||
case "type":
|
||||
// all commands have this to help in reconciliation, but it makes
|
||||
// no sense to edit it
|
||||
break;
|
||||
case "GUID":
|
||||
var existing = this._bms.getItemIdForGUID(command.data.GUID);
|
||||
var existing = this._getItemIdForGUID(command.data.GUID);
|
||||
if (existing < 0)
|
||||
this._bms.setItemGUID(itemId, command.data.GUID);
|
||||
else
|
||||
|
@ -504,11 +486,11 @@ BookmarksStore.prototype = {
|
|||
if (command.data.index && command.data.index >= 0)
|
||||
index = command.data.index;
|
||||
this._bms.moveItem(
|
||||
itemId, this._bms.getItemIdForGUID(command.data.parentGUID), index);
|
||||
itemId, this._getItemIdForGUID(command.data.parentGUID), index);
|
||||
} break;
|
||||
case "tags": {
|
||||
let tagsURI = this._bms.getBookmarkURI(itemId);
|
||||
this._ts.untagURI(URI, null);
|
||||
this._ts.untagURI(tagsURI, null);
|
||||
this._ts.tagURI(tagsURI, command.data.tags);
|
||||
} break;
|
||||
case "keyword":
|
||||
|
@ -533,22 +515,135 @@ BookmarksStore.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_getNode: function BSS__getNode(folder) {
|
||||
let query = this._hsvc.getNewQuery();
|
||||
query.setFolders([folder], 1);
|
||||
return this._hsvc.executeQuery(query, this._hsvc.getNewQueryOptions()).root;
|
||||
},
|
||||
|
||||
__wrap: function BSS___wrap(node, items, parentGUID, index, guidOverride) {
|
||||
let GUID, item;
|
||||
|
||||
// we override the guid for the root items, "menu", "toolbar", etc.
|
||||
if (guidOverride) {
|
||||
GUID = guidOverride;
|
||||
item = {};
|
||||
} else {
|
||||
GUID = this._bms.getItemGUID(node.itemId);
|
||||
item = {parentGUID: parentGUID, index: index};
|
||||
}
|
||||
|
||||
if (node.type == node.RESULT_TYPE_FOLDER) {
|
||||
if (this._ls.isLivemark(node.itemId)) {
|
||||
item.type = "livemark";
|
||||
let siteURI = this._ls.getSiteURI(node.itemId);
|
||||
let feedURI = this._ls.getFeedURI(node.itemId);
|
||||
item.siteURI = siteURI? siteURI.spec : "";
|
||||
item.feedURI = feedURI? feedURI.spec : "";
|
||||
|
||||
} else if (this._ans.itemHasAnnotation(node.itemId,
|
||||
"weave/mounted-share-id")) {
|
||||
item.type = "mounted-share";
|
||||
item.title = node.title;
|
||||
item.mountId = this._ans.getItemAnnotation(node.itemId,
|
||||
"weave/mounted-share-id");
|
||||
|
||||
} else {
|
||||
item.type = "folder";
|
||||
node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
|
||||
node.containerOpen = true;
|
||||
for (var i = 0; i < node.childCount; i++) {
|
||||
this.__wrap(node.getChild(i), items, GUID, i);
|
||||
}
|
||||
}
|
||||
if (!guidOverride)
|
||||
item.title = node.title; // no titles for root nodes
|
||||
|
||||
} else if (node.type == node.RESULT_TYPE_URI ||
|
||||
node.type == node.RESULT_TYPE_QUERY) {
|
||||
if (this._ms.hasMicrosummary(node.itemId)) {
|
||||
item.type = "microsummary";
|
||||
let micsum = this._ms.getMicrosummary(node.itemId);
|
||||
item.generatorURI = micsum.generator.uri.spec; // breaks local generators
|
||||
} else if (node.type == node.RESULT_TYPE_QUERY) {
|
||||
item.type = "query";
|
||||
item.title = node.title;
|
||||
} else {
|
||||
item.type = "bookmark";
|
||||
item.title = node.title;
|
||||
}
|
||||
item.URI = node.uri;
|
||||
item.tags = this._ts.getTagsForURI(Utils.makeURI(node.uri), {});
|
||||
item.keyword = this._bms.getKeywordForBookmark(node.itemId);
|
||||
|
||||
} else if (node.type == node.RESULT_TYPE_SEPARATOR) {
|
||||
item.type = "separator";
|
||||
|
||||
} else {
|
||||
this._log.warn("Warning: unknown item type, cannot serialize: " + node.type);
|
||||
return;
|
||||
}
|
||||
|
||||
items[GUID] = item;
|
||||
},
|
||||
|
||||
// helper
|
||||
_wrap: function BStore__wrap(node, items, rootName) {
|
||||
return this.__wrap(node, items, null, null, rootName);
|
||||
},
|
||||
|
||||
_wrapMount: function BStore__wrapMount(node, id) {
|
||||
if (node.type != node.RESULT_TYPE_FOLDER)
|
||||
throw "Trying to wrap a non-folder mounted share";
|
||||
|
||||
let GUID = this._bms.getItemGUID(node.itemId);
|
||||
let ret = {rootGUID: GUID, userid: id, snapshot: {}};
|
||||
|
||||
node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
|
||||
node.containerOpen = true;
|
||||
for (var i = 0; i < node.childCount; i++) {
|
||||
this.__wrap(node.getChild(i), ret.snapshot, GUID, i);
|
||||
}
|
||||
|
||||
// remove any share mountpoints
|
||||
for (let guid in ret.snapshot) {
|
||||
if (ret.snapshot[guid].type == "mounted-share")
|
||||
delete ret.snapshot[guid];
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
_resetGUIDs: function BSS__resetGUIDs(node) {
|
||||
if (this._ans.itemHasAnnotation(node.itemId, "placesInternal/GUID"))
|
||||
this._ans.removeItemAnnotation(node.itemId, "placesInternal/GUID");
|
||||
|
||||
if (node.type == node.RESULT_TYPE_FOLDER &&
|
||||
!this._ls.isLivemark(node.itemId)) {
|
||||
node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
|
||||
node.containerOpen = true;
|
||||
for (var i = 0; i < node.childCount; i++) {
|
||||
this._resetGUIDs(node.getChild(i));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
findMounts: function BStore_findMounts() {
|
||||
let ret = [];
|
||||
let a = this._ans.getItemsWithAnnotation("weave/mounted-share-id", {});
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
let id = this._ans.getItemAnnotation(a[i], "weave/mounted-share-id");
|
||||
ret.push(this._wrapMount(this._getNode(a[i]), id));
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
wrap: function BStore_wrap() {
|
||||
let filed = this._getWrappedBookmarks(this._bms.bookmarksMenuFolder);
|
||||
let toolbar = this._getWrappedBookmarks(this._bms.toolbarFolder);
|
||||
let unfiled = this._getWrappedBookmarks(this._bms.unfiledBookmarksFolder);
|
||||
|
||||
for (let guid in unfiled) {
|
||||
if (!(guid in filed))
|
||||
filed[guid] = unfiled[guid];
|
||||
}
|
||||
|
||||
for (let guid in toolbar) {
|
||||
if (!(guid in filed))
|
||||
filed[guid] = toolbar[guid];
|
||||
}
|
||||
|
||||
return filed; // (combined)
|
||||
var items = {};
|
||||
this._wrap(this._getNode(this._bms.bookmarksMenuFolder), items, "menu");
|
||||
this._wrap(this._getNode(this._bms.toolbarFolder), items, "toolbar");
|
||||
this._wrap(this._getNode(this._bms.unfiledBookmarksFolder), items, "unfiled");
|
||||
return items;
|
||||
},
|
||||
|
||||
wipe: function BStore_wipe() {
|
||||
|
@ -558,9 +653,9 @@ BookmarksStore.prototype = {
|
|||
},
|
||||
|
||||
resetGUIDs: function BStore_resetGUIDs() {
|
||||
this._resetGUIDsInt(this._getFolderNodes(this._bms.bookmarksMenuFolder));
|
||||
this._resetGUIDsInt(this._getFolderNodes(this._bms.toolbarFolder));
|
||||
this._resetGUIDsInt(this._getFolderNodes(this._bms.unfiledBookmarksFolder));
|
||||
this._resetGUIDs(this._getNode(this._bms.bookmarksMenuFolder));
|
||||
this._resetGUIDs(this._getNode(this._bms.toolbarFolder));
|
||||
this._resetGUIDs(this._getNode(this._bms.unfiledBookmarksFolder));
|
||||
}
|
||||
};
|
||||
BookmarksStore.prototype.__proto__ = new Store();
|
||||
|
@ -573,40 +668,37 @@ HistoryStore.prototype = {
|
|||
|
||||
__hsvc: null,
|
||||
get _hsvc() {
|
||||
if (!this.__hsvc)
|
||||
if (!this.__hsvc) {
|
||||
this.__hsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
|
||||
getService(Ci.nsINavHistoryService);
|
||||
this.__hsvc.QueryInterface(Ci.nsIGlobalHistory2);
|
||||
this.__hsvc.QueryInterface(Ci.nsIBrowserHistory);
|
||||
}
|
||||
return this.__hsvc;
|
||||
},
|
||||
|
||||
__browserHist: null,
|
||||
get _browserHist() {
|
||||
if (!this.__browserHist)
|
||||
this.__browserHist = Cc["@mozilla.org/browser/nav-history-service;1"].
|
||||
getService(Ci.nsIBrowserHistory);
|
||||
return this.__browserHist;
|
||||
},
|
||||
|
||||
_createCommand: function HistStore__createCommand(command) {
|
||||
this._log.info(" -> creating history entry: " + command.GUID);
|
||||
this._log.debug(" -> creating history entry: " + command.GUID);
|
||||
try {
|
||||
this._browserHist.addPageWithDetails(Utils.makeURI(command.GUID),
|
||||
command.data.title,
|
||||
command.data.time);
|
||||
this._hsvc.setPageDetails(Utils.makeURI(command.GUID), command.data.title,
|
||||
command.data.accessCount, false, false);
|
||||
let uri = Utils.makeURI(command.data.URI);
|
||||
this._hsvc.addVisit(uri, command.data.time, null,
|
||||
this._hsvc.TRANSITION_TYPED, false, null);
|
||||
this._hsvc.setPageTitle(uri, command.data.title);
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e));
|
||||
}
|
||||
},
|
||||
|
||||
_removeCommand: function HistStore__removeCommand(command) {
|
||||
this._log.info(" -> NOT removing history entry: " + command.GUID);
|
||||
//this._browserHist.removePage(command.GUID);
|
||||
this._log.trace(" -> NOT removing history entry: " + command.GUID);
|
||||
// we can't remove because we only sync the last 1000 items, not
|
||||
// the whole store. So we don't know if remove commands were
|
||||
// generated due to the user removing an entry or because it
|
||||
// dropped past the 1000 item mark.
|
||||
},
|
||||
|
||||
_editCommand: function HistStore__editCommand(command) {
|
||||
this._log.info(" -> FIXME: NOT editing history entry: " + command.GUID);
|
||||
this._log.trace(" -> FIXME: NOT editing history entry: " + command.GUID);
|
||||
// FIXME: implement!
|
||||
},
|
||||
|
||||
|
@ -615,8 +707,9 @@ HistoryStore.prototype = {
|
|||
options = this._hsvc.getNewQueryOptions();
|
||||
|
||||
query.minVisits = 1;
|
||||
options.maxResults = 500;
|
||||
options.sortingMode = query.SORT_BY_LASTMODIFIED_DESCENDING;
|
||||
options.maxResults = 1000;
|
||||
options.resultType = options.RESULTS_AS_VISIT; // FULL_VISIT does not work
|
||||
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
|
||||
options.queryType = options.QUERY_TYPE_HISTORY;
|
||||
|
||||
let root = this._hsvc.executeQuery(query, options).root;
|
||||
|
@ -630,19 +723,19 @@ HistoryStore.prototype = {
|
|||
let items = {};
|
||||
for (let i = 0; i < root.childCount; i++) {
|
||||
let item = root.getChild(i);
|
||||
items[item.uri] = {parentGUID: '',
|
||||
let guid = item.time + ":" + item.uri
|
||||
items[guid] = {parentGUID: '',
|
||||
title: item.title,
|
||||
URI: item.uri,
|
||||
time: item.time,
|
||||
accessCount: item.accessCount,
|
||||
dateAdded: item.dateAdded,
|
||||
time: item.time
|
||||
};
|
||||
// FIXME: sync transition type - requires FULL_VISITs
|
||||
}
|
||||
return items;
|
||||
},
|
||||
|
||||
wipe: function HistStore_wipe() {
|
||||
this._browserHist.removeAllPages();
|
||||
this._hsvc.removeAllPages();
|
||||
}
|
||||
};
|
||||
HistoryStore.prototype.__proto__ = new Store();
|
||||
|
|
|
@ -179,7 +179,7 @@ SyncCore.prototype = {
|
|||
for (let i = 0; i < list.length; i++) {
|
||||
if (!list[i])
|
||||
continue;
|
||||
if (list[i].data.parentGUID == oldGUID)
|
||||
if (list[i].data && list[i].data.parentGUID == oldGUID)
|
||||
list[i].data.parentGUID = newGUID;
|
||||
for (let j = 0; j < list[i].parents.length; j++) {
|
||||
if (list[i].parents[j] == oldGUID)
|
||||
|
@ -227,83 +227,76 @@ SyncCore.prototype = {
|
|||
this._log.debug("Reconciling " + listA.length +
|
||||
" against " + listB.length + "commands");
|
||||
|
||||
try {
|
||||
let guidChanges = [];
|
||||
for (let i = 0; i < listA.length; i++) {
|
||||
let a = listA[i];
|
||||
let guidChanges = [];
|
||||
for (let i = 0; i < listA.length; i++) {
|
||||
let a = listA[i];
|
||||
timer.initWithCallback(listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
|
||||
//this._log.debug("comparing " + i + ", listB length: " + listB.length);
|
||||
|
||||
let skip = false;
|
||||
listB = listB.filter(function(b) {
|
||||
// fast path for when we already found a matching command
|
||||
if (skip)
|
||||
return true;
|
||||
|
||||
if (Utils.deepEquals(a, b)) {
|
||||
delete listA[i]; // a
|
||||
skip = true;
|
||||
return false; // b
|
||||
|
||||
} else if (this._commandLike(a, b)) {
|
||||
this._fixParents(listA, a.GUID, b.GUID);
|
||||
guidChanges.push({action: "edit",
|
||||
GUID: a.GUID,
|
||||
data: {GUID: b.GUID}});
|
||||
delete listA[i]; // a
|
||||
skip = true;
|
||||
return false; // b, but we add it back from guidChanges
|
||||
}
|
||||
|
||||
// watch out for create commands with GUIDs that already exist
|
||||
if (b.action == "create" && this._itemExists(b.GUID)) {
|
||||
this._log.error("Remote command has GUID that already exists " +
|
||||
"locally. Dropping command.");
|
||||
return false; // delete b
|
||||
}
|
||||
return true; // keep b
|
||||
}, this);
|
||||
}
|
||||
|
||||
listA = listA.filter(function(elt) { return elt });
|
||||
listB = guidChanges.concat(listB);
|
||||
|
||||
for (let i = 0; i < listA.length; i++) {
|
||||
for (let j = 0; j < listB.length; j++) {
|
||||
|
||||
timer.initWithCallback(listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
|
||||
//this._log.debug("comparing " + i + ", listB length: " + listB.length);
|
||||
|
||||
let skip = false;
|
||||
listB = listB.filter(function(b) {
|
||||
// fast path for when we already found a matching command
|
||||
if (skip)
|
||||
return true;
|
||||
|
||||
if (Utils.deepEquals(a, b)) {
|
||||
delete listA[i]; // a
|
||||
skip = true;
|
||||
return false; // b
|
||||
|
||||
} else if (this._commandLike(a, b)) {
|
||||
this._fixParents(listA, a.GUID, b.GUID);
|
||||
guidChanges.push({action: "edit",
|
||||
GUID: a.GUID,
|
||||
data: {GUID: b.GUID}});
|
||||
delete listA[i]; // a
|
||||
skip = true;
|
||||
return false; // b, but we add it back from guidChanges
|
||||
}
|
||||
|
||||
// watch out for create commands with GUIDs that already exist
|
||||
if (b.action == "create" && this._itemExists(b.GUID)) {
|
||||
this._log.error("Remote command has GUID that already exists " +
|
||||
"locally. Dropping command.");
|
||||
return false; // delete b
|
||||
}
|
||||
return true; // keep b
|
||||
}, this);
|
||||
}
|
||||
|
||||
listA = listA.filter(function(elt) { return elt });
|
||||
listB = guidChanges.concat(listB);
|
||||
|
||||
for (let i = 0; i < listA.length; i++) {
|
||||
for (let j = 0; j < listB.length; j++) {
|
||||
|
||||
timer.initWithCallback(listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
|
||||
if (this._conflicts(listA[i], listB[j]) ||
|
||||
this._conflicts(listB[j], listA[i])) {
|
||||
if (!conflicts[0].some(
|
||||
function(elt) { return elt.GUID == listA[i].GUID }))
|
||||
conflicts[0].push(listA[i]);
|
||||
if (!conflicts[1].some(
|
||||
function(elt) { return elt.GUID == listB[j].GUID }))
|
||||
conflicts[1].push(listB[j]);
|
||||
}
|
||||
if (this._conflicts(listA[i], listB[j]) ||
|
||||
this._conflicts(listB[j], listA[i])) {
|
||||
if (!conflicts[0].some(
|
||||
function(elt) { return elt.GUID == listA[i].GUID }))
|
||||
conflicts[0].push(listA[i]);
|
||||
if (!conflicts[1].some(
|
||||
function(elt) { return elt.GUID == listB[j].GUID }))
|
||||
conflicts[1].push(listB[j]);
|
||||
}
|
||||
}
|
||||
|
||||
this._getPropagations(listA, conflicts[0], propagations[1]);
|
||||
|
||||
timer.initWithCallback(listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
|
||||
this._getPropagations(listB, conflicts[1], propagations[0]);
|
||||
ret = {propagations: propagations, conflicts: conflicts};
|
||||
|
||||
} catch (e) {
|
||||
this._log.error("Exception caught: " + (e.message? e.message : e) +
|
||||
" - " + (e.location? e.location : "_reconcile"));
|
||||
|
||||
} finally {
|
||||
timer = null;
|
||||
self.done(ret);
|
||||
}
|
||||
|
||||
this._getPropagations(listA, conflicts[0], propagations[1]);
|
||||
|
||||
timer.initWithCallback(listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
|
||||
this._getPropagations(listB, conflicts[1], propagations[0]);
|
||||
ret = {propagations: propagations, conflicts: conflicts};
|
||||
|
||||
timer = null;
|
||||
self.done(ret);
|
||||
},
|
||||
|
||||
// Public methods
|
||||
|
@ -335,21 +328,42 @@ BookmarksSyncCore.prototype = {
|
|||
return this._bms.getItemIdForGUID(GUID) >= 0;
|
||||
},
|
||||
|
||||
_commandLike: function BSC_commandLike(a, b) {
|
||||
_getEdits: function BSC__getEdits(a, b) {
|
||||
// NOTE: we do not increment ret.numProps, as that would cause
|
||||
// edit commands to always get generated
|
||||
let ret = SyncCore.prototype._getEdits.call(this, a, b);
|
||||
ret.props.type = a.type;
|
||||
return ret;
|
||||
},
|
||||
|
||||
// compares properties
|
||||
// returns true if the property is not set in either object
|
||||
// returns true if the property is set and equal in both objects
|
||||
// returns false otherwise
|
||||
_comp: function BSC__comp(a, b, prop) {
|
||||
return (!a.data[prop] && !b.data[prop]) ||
|
||||
(a.data[prop] && b.data[prop] && (a.data[prop] == b.data[prop]));
|
||||
},
|
||||
|
||||
_commandLike: function BSC__commandLike(a, b) {
|
||||
// Check that neither command is null, that their actions, types,
|
||||
// and parents are the same, and that they don't have the same
|
||||
// GUID.
|
||||
// Items with the same GUID do not qualify for 'likeness' because
|
||||
// we already consider them to be the same object, and therefore
|
||||
// we need to process any edits.
|
||||
// The parent GUID check works because reconcile() fixes up the
|
||||
// parent GUIDs as it runs, and the command list is sorted by
|
||||
// depth
|
||||
// * Items with the same GUID do not qualify for 'likeness' because
|
||||
// we already consider them to be the same object, and therefore
|
||||
// we need to process any edits.
|
||||
// * Remove or edit commands don't qualify for likeness either,
|
||||
// since remove or edit commands with different GUIDs are
|
||||
// guaranteed to refer to two different items
|
||||
// * The parent GUID check works because reconcile() fixes up the
|
||||
// parent GUIDs as it runs, and the command list is sorted by
|
||||
// depth
|
||||
if (!a || !b ||
|
||||
a.action != b.action ||
|
||||
a.data.type != b.data.type ||
|
||||
a.data.parentGUID != b.data.parentGUID ||
|
||||
a.GUID == b.GUID)
|
||||
a.action != b.action ||
|
||||
a.action != "create" ||
|
||||
a.data.type != b.data.type ||
|
||||
a.data.parentGUID != b.data.parentGUID ||
|
||||
a.GUID == b.GUID)
|
||||
return false;
|
||||
|
||||
// Bookmarks are allowed to be in a different index as long as
|
||||
|
@ -357,33 +371,33 @@ BookmarksSyncCore.prototype = {
|
|||
// the same index to qualify for 'likeness'.
|
||||
switch (a.data.type) {
|
||||
case "bookmark":
|
||||
if (a.data.URI == b.data.URI &&
|
||||
a.data.title == b.data.title)
|
||||
if (this._comp(a, b, 'URI') &&
|
||||
this._comp(a, b, 'title'))
|
||||
return true;
|
||||
return false;
|
||||
case "query":
|
||||
if (a.data.URI == b.data.URI &&
|
||||
a.data.title == b.data.title)
|
||||
if (this._comp(a, b, 'URI') &&
|
||||
this._comp(a, b, 'title'))
|
||||
return true;
|
||||
return false;
|
||||
case "microsummary":
|
||||
if (a.data.URI == b.data.URI &&
|
||||
a.data.generatorURI == b.data.generatorURI)
|
||||
if (this._comp(a, b, 'URI') &&
|
||||
this._comp(a, b, 'generatorURI'))
|
||||
return true;
|
||||
return false;
|
||||
case "folder":
|
||||
if (a.index == b.index &&
|
||||
a.data.title == b.data.title)
|
||||
if (this._comp(a, b, 'index') &&
|
||||
this._comp(a, b, 'title'))
|
||||
return true;
|
||||
return false;
|
||||
case "livemark":
|
||||
if (a.data.title == b.data.title &&
|
||||
a.data.siteURI == b.data.siteURI &&
|
||||
a.data.feedURI == b.data.feedURI)
|
||||
if (this._comp(a, b, 'title') &&
|
||||
this._comp(a, b, 'siteURI') &&
|
||||
this._comp(a, b, 'feedURI'))
|
||||
return true;
|
||||
return false;
|
||||
case "separator":
|
||||
if (a.index == b.index)
|
||||
if (this._comp(a, b, 'index'))
|
||||
return true;
|
||||
return false;
|
||||
default:
|
||||
|
@ -417,8 +431,6 @@ HistorySyncCore.prototype = {
|
|||
HistorySyncCore.prototype.__proto__ = new SyncCore();
|
||||
|
||||
|
||||
|
||||
|
||||
function CookiesSyncCore() {
|
||||
this._init();
|
||||
}
|
||||
|
|
|
@ -51,6 +51,17 @@ Cu.import("resource://weave/log4moz.js");
|
|||
|
||||
let Utils = {
|
||||
|
||||
// lazy load objects from a constructor on first access. It will
|
||||
// work with the global object ('this' in the global context).
|
||||
lazy: function Weave_lazy(dest, prop, ctr) {
|
||||
let getter = function() {
|
||||
delete dest[prop];
|
||||
dest[prop] = new ctr();
|
||||
return dest[prop];
|
||||
};
|
||||
dest.__defineGetter__(prop, getter);
|
||||
},
|
||||
|
||||
deepEquals: function Weave_deepEquals(a, b) {
|
||||
if (!a && !b)
|
||||
return true;
|
||||
|
@ -143,8 +154,11 @@ let Utils = {
|
|||
}
|
||||
}
|
||||
|
||||
let log = Log4Moz.Service.getLogger("Service.Util");
|
||||
log.error(msg + " Error code: " + code);
|
||||
if (msg) {
|
||||
let log = Log4Moz.Service.getLogger("Service.Util");
|
||||
log.error(msg + " Error code: " + code);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
|
@ -153,6 +167,28 @@ let Utils = {
|
|||
throw 'checkStatus failed';
|
||||
},
|
||||
|
||||
sha1: function Weave_sha1(string) {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Ci.nsICryptoHash);
|
||||
hasher.init(hasher.SHA1);
|
||||
|
||||
let data = converter.convertToByteArray(string, {});
|
||||
hasher.update(data, data.length);
|
||||
let rawHash = hasher.finish(false);
|
||||
|
||||
// return the two-digit hexadecimal code for a byte
|
||||
function toHexString(charCode) {
|
||||
return ("0" + charCode.toString(16)).slice(-2);
|
||||
}
|
||||
|
||||
let hash = [toHexString(rawHash.charCodeAt(i)) for (i in rawHash)].join("");
|
||||
return hash;
|
||||
},
|
||||
|
||||
makeURI: function Weave_makeURI(URIString) {
|
||||
if (URIString === null || URIString == "")
|
||||
return null;
|
||||
|
@ -278,6 +314,17 @@ let Utils = {
|
|||
return function innerBind() { return method.apply(object, arguments); }
|
||||
},
|
||||
|
||||
_prefs: null,
|
||||
get prefs() {
|
||||
if (!this.__prefs) {
|
||||
this.__prefs = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService);
|
||||
this.__prefs = this.__prefs.getBranch(PREFS_BRANCH);
|
||||
this.__prefs.QueryInterface(Ci.nsIPrefBranch2);
|
||||
}
|
||||
return this.__prefs;
|
||||
},
|
||||
|
||||
/*
|
||||
* Event listener object
|
||||
* Used to handle XMLHttpRequest and nsITimer callbacks
|
||||
|
@ -286,7 +333,9 @@ let Utils = {
|
|||
EventListener: function Weave_EventListener(handler, eventName) {
|
||||
this._handler = handler;
|
||||
this._eventName = eventName;
|
||||
this._log = Log4Moz.Service.getLogger("Service.EventHandler");
|
||||
this._log = Log4Moz.Service.getLogger("Async.EventHandler");
|
||||
this._log.level =
|
||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.async")];
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Bookmarks Sync.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dan Mills <thunder@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const EXPORTED_SYMBOLS = ['Weave'];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://weave/service.js");
|
||||
|
||||
let Weave = {};
|
||||
//Weave.Crypto = new WeaveCrypto();
|
||||
Weave.Service = new WeaveSyncService();
|
|
@ -0,0 +1,166 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Bookmarks Sync.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Dan Mills <thunder@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const EXPORTED_SYMBOLS = ['Wrap'];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://weave/log4moz.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
/*
|
||||
* Wrapper utility functions
|
||||
*
|
||||
* Not in util.js because that would cause a circular dependency
|
||||
* between util.js and async.js (we include async.js so that our
|
||||
* returned generator functions have the .async sugar defined)
|
||||
*/
|
||||
|
||||
let Wrap = {
|
||||
|
||||
// NOTE: requires _osPrefix string property in your object
|
||||
// NOTE2: copy this function over to your objects, use like this:
|
||||
//
|
||||
// MyObj.prototype = {
|
||||
// _notify: Wrap.notify,
|
||||
// ...
|
||||
// method: function MyMethod() {
|
||||
// let self = yield;
|
||||
// ...
|
||||
// // doFoo is assumed to be an asynchronous method
|
||||
// this._notify("foo", this._doFoo, arg1, arg2).async(this, self.cb);
|
||||
// let ret = yield;
|
||||
// ...
|
||||
// }
|
||||
// };
|
||||
notify: function Weave_notify(name, method /* , arg1, arg2, ..., argN */) {
|
||||
let savedName = name;
|
||||
let savedMethod = method;
|
||||
let savedArgs = Array.prototype.slice.call(arguments, 2);
|
||||
|
||||
return function WeaveNotifyWrapper(/* argN+1, argN+2, ... */) {
|
||||
let self = yield;
|
||||
let ret;
|
||||
let args = Array.prototype.slice.call(arguments);
|
||||
|
||||
try {
|
||||
this._os.notifyObservers(null, this._osPrefix + savedName + ":start", "");
|
||||
|
||||
args = savedArgs.concat(args);
|
||||
args.unshift(this, savedMethod, self.cb);
|
||||
Async.run.apply(Async, args);
|
||||
ret = yield;
|
||||
|
||||
this._os.notifyObservers(null, this._osPrefix + savedName + ":success", "");
|
||||
|
||||
} catch (e) {
|
||||
this._os.notifyObservers(null, this._osPrefix + savedName + ":error", "");
|
||||
throw e;
|
||||
}
|
||||
|
||||
self.done(ret);
|
||||
};
|
||||
},
|
||||
|
||||
// NOTE: see notify, this works the same way. they can be
|
||||
// chained together as well.
|
||||
lock: function WeaveSync_lock(method /* , arg1, arg2, ..., argN */) {
|
||||
let savedMethod = method;
|
||||
let savedArgs = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
return function WeaveLockWrapper( /* argN+1, argN+2, ... */) {
|
||||
let self = yield;
|
||||
let ret;
|
||||
let args = Array.prototype.slice.call(arguments);
|
||||
|
||||
DAV.lock.async(DAV, self.cb);
|
||||
let locked = yield;
|
||||
if (!locked)
|
||||
throw "Could not acquire lock";
|
||||
|
||||
try {
|
||||
args = savedArgs.concat(args);
|
||||
args.unshift(this, savedMethod, self.cb);
|
||||
Async.run.apply(Async, args);
|
||||
ret = yield;
|
||||
|
||||
} catch (e) {
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
DAV.unlock.async(DAV, self.cb);
|
||||
yield;
|
||||
}
|
||||
|
||||
self.done(ret);
|
||||
};
|
||||
},
|
||||
|
||||
// NOTE: see notify, this works the same way. they can be
|
||||
// chained together as well.
|
||||
localLock: function WeaveSync_localLock(method /* , arg1, arg2, ..., argN */) {
|
||||
let savedMethod = method;
|
||||
let savedArgs = Array.prototype.slice.call(arguments, 1);
|
||||
|
||||
return function WeaveLocalLockWrapper(/* argN+1, argN+2, ... */) {
|
||||
let self = yield;
|
||||
let ret;
|
||||
let args = Array.prototype.slice.call(arguments);
|
||||
|
||||
if (DAV.locked)
|
||||
throw "Could not acquire lock";
|
||||
DAV.allowLock = false;
|
||||
|
||||
try {
|
||||
args = savedArgs.concat(args);
|
||||
args.unshift(this, savedMethod, self.cb);
|
||||
Async.run.apply(Async, args);
|
||||
ret = yield;
|
||||
}
|
||||
catch (e) { throw e; }
|
||||
finally { DAV.allowLock = true; }
|
||||
|
||||
self.done(ret);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ pref("extensions.weave.encryption", "aes-256-cbc");
|
|||
pref("extensions.weave.lastversion", "firstrun");
|
||||
pref("extensions.weave.lastsync", "0");
|
||||
|
||||
pref("extensions.weave.ui.syncnow", true);
|
||||
pref("extensions.weave.ui.sharebookmarks", false);
|
||||
pref("extensions.weave.rememberpassword", true);
|
||||
pref("extensions.weave.autoconnect", true);
|
||||
pref("extensions.weave.enabled", true);
|
||||
|
@ -14,9 +16,14 @@ pref("extensions.weave.history", true);
|
|||
pref("extensions.weave.cookies", false );
|
||||
pref("extensions.weave.schedule", 1);
|
||||
|
||||
|
||||
pref("extensions.weave.log.rootLogger", "Config");
|
||||
pref("extensions.weave.log.appender.console", "Warn");
|
||||
pref("extensions.weave.log.appender.dump", "Error");
|
||||
pref("extensions.weave.log.appender.briefLog", "Info");
|
||||
pref("extensions.weave.log.appender.debugLog", "Config");
|
||||
pref("extensions.weave.log.appender.debugLog", "Trace");
|
||||
|
||||
pref("extensions.weave.log.rootLogger", "Trace");
|
||||
pref("extensions.weave.log.logger.async", "Debug");
|
||||
pref("extensions.weave.log.logger.service.crypto", "Debug");
|
||||
pref("extensions.weave.log.logger.service.dav", "Debug");
|
||||
pref("extensions.weave.log.logger.service.engine", "Debug");
|
||||
pref("extensions.weave.log.logger.service.main", "Trace");
|
||||
|
|
Загрузка…
Ссылка в новой задаче