зеркало из https://github.com/mozilla/pjs.git
add status notification to sharing dialog; fix async generators in the case where an async method is missing a yield (better error, continue execution in parent); add guts of demo sharing code to engine & store (for bookmarks, with some spillage)
This commit is contained in:
Родитель
38ed71b0a2
Коммит
26713b0184
|
@ -2,6 +2,5 @@
|
|||
<!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 description.ok "Weave sharing complete!">
|
||||
<!ENTITY description.error "Error. Please check the Weave ID and try again.">
|
||||
<!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...
|
|
@ -219,11 +219,12 @@ Generator.prototype = {
|
|||
|
||||
_done: function AsyncGen__done(retval) {
|
||||
if (!this._generator) {
|
||||
this._log.error("Generator '" + this.name + "' called 'done' after it's finalized");
|
||||
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);
|
||||
return;
|
||||
} else {
|
||||
this._generator.close();
|
||||
}
|
||||
this._generator.close();
|
||||
this._generator = null;
|
||||
this._timer = null;
|
||||
|
||||
|
|
|
@ -685,56 +685,83 @@ Engine.prototype = {
|
|||
|
||||
this._log.debug("Sharing bookmarks with " + username);
|
||||
|
||||
this._getSymKey.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
// copied from getSymKey
|
||||
this._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 {
|
||||
this._getSymKey.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
// copied from getSymKey
|
||||
this._log.debug("Getting keyring from server");
|
||||
this._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
|
||||
this._log.debug("Constructing other user's URL");
|
||||
let hash = Utils.sha1(username);
|
||||
let serverURL = Utils.prefs.getCharPref("serverURL");
|
||||
this._dav.baseURL = serverURL + "user/" + hash + "/"; //FIXME: very ugly!
|
||||
|
||||
this._log.debug("Getting other user's public key");
|
||||
this._dav.GET("public/pubkey", self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status,
|
||||
"Could not get public key for user" + username);
|
||||
let id = new Identity();
|
||||
id.pubkey = ret.responseText;
|
||||
}
|
||||
catch (e) { throw e; }
|
||||
finally { this._dav.baseURL = base; }
|
||||
|
||||
this._dav.baseURL = base;
|
||||
Utils.ensureStatus(ret.status, "Could not get public key for " + username);
|
||||
|
||||
// now encrypt the symkey with their pubkey and upload the new keyring
|
||||
this._log.debug("Encrypting symmetric key with other user's public key");
|
||||
Crypto.RSAencrypt.async(Crypto, self.cb, this._engineId.password, id);
|
||||
let enckey = yield;
|
||||
if (!enckey)
|
||||
throw "Could not encrypt symmetric encryption key";
|
||||
let id = new Identity();
|
||||
id.pubkey = ret.responseText;
|
||||
|
||||
this._log.debug("Uploading new keyring");
|
||||
keys.ring[hash] = enckey;
|
||||
this._dav.PUT(this.keysFile, this._json.encode(keys), self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not upload keyring file.");
|
||||
// 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";
|
||||
|
||||
this._log.debug("All done sharing!");
|
||||
keys.ring[hash] = enckey;
|
||||
this._dav.PUT(this.keysFile, this._json.encode(keys), self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not upload keyring file.");
|
||||
|
||||
} catch (e) {
|
||||
throw e;
|
||||
this._createShare(username, username);
|
||||
|
||||
} finally {
|
||||
this._dav.baseURL = base;
|
||||
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);
|
||||
}
|
||||
|
||||
self.done();
|
||||
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) {
|
||||
|
@ -774,6 +801,93 @@ 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 = this._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);
|
||||
this._dav.baseURL = serverURL + "user/" + hash + "/"; //FIXME: very ugly!
|
||||
|
||||
this._getSymKey.async(this, self.cb);
|
||||
yield;
|
||||
|
||||
this._log.trace("Getting status file for " + user);
|
||||
this._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);
|
||||
this._dav.GET(this.snapshotFile, self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not download snapshot.");
|
||||
Crypto.PBEdecrypt.async(Crypto, self.cb, resp.responseText,
|
||||
this._engineId, status.snapEncryption);
|
||||
let data = yield;
|
||||
snap.data = this._json.decode(data);
|
||||
|
||||
this._log.trace("Downloading server deltas for " + user);
|
||||
this._dav.GET(this.deltasFile, self.cb);
|
||||
resp = yield;
|
||||
Utils.ensureStatus(resp.status, "Could not download deltas.");
|
||||
Crypto.PBEdecrypt.async(Crypto, self.cb, resp.responseText,
|
||||
this._engineId, status.deltasEncryption);
|
||||
data = yield;
|
||||
deltas = this._json.decode(data);
|
||||
}
|
||||
catch (e) { throw e; }
|
||||
finally { this._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();
|
||||
|
|
|
@ -512,6 +512,8 @@ WeaveSvc.prototype = {
|
|||
this._notify(this._bmkEngine.name + ":sync",
|
||||
engineCb, this._bmkEngine).async(this, innerSelf.cb);
|
||||
yield;
|
||||
this._bmkEngine.syncMounts(innerSelf.cb);
|
||||
yield;
|
||||
}
|
||||
if (Utils.prefs.getBoolPref("history")) {
|
||||
this._notify(this._histEngine.name + ":sync",
|
||||
|
@ -569,11 +571,13 @@ WeaveSvc.prototype = {
|
|||
let cb = function Weave_shareBookmarks() {
|
||||
let innerSelf = yield;
|
||||
this._bmkEngine.share(innerSelf.cb, username);
|
||||
yield;
|
||||
let ret = yield;
|
||||
innerSelf.done(ret);
|
||||
};
|
||||
|
||||
this._lock(this._notify("share-bookmarks", cb)).async(this, self.cb);
|
||||
yield;
|
||||
let ret = yield;
|
||||
self.done(ret);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -385,6 +385,15 @@ BookmarksStore.prototype = {
|
|||
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.debug(" -> creating separator");
|
||||
newId = this._bms.insertSeparator(parentId, command.data.index);
|
||||
|
@ -508,7 +517,7 @@ BookmarksStore.prototype = {
|
|||
return this._hsvc.executeQuery(query, this._hsvc.getNewQueryOptions()).root;
|
||||
},
|
||||
|
||||
__wrap: function BSS__wrap(node, items, parentGUID, index, guidOverride) {
|
||||
__wrap: function BSS___wrap(node, items, parentGUID, index, guidOverride) {
|
||||
let GUID, item;
|
||||
|
||||
// we override the guid for the root items, "menu", "toolbar", etc.
|
||||
|
@ -527,6 +536,14 @@ BookmarksStore.prototype = {
|
|||
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);
|
||||
|
@ -567,10 +584,32 @@ BookmarksStore.prototype = {
|
|||
},
|
||||
|
||||
// helper
|
||||
_wrap: function BStore_wrap(node, items, rootName) {
|
||||
_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");
|
||||
|
@ -585,6 +624,16 @@ BookmarksStore.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
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() {
|
||||
var items = {};
|
||||
this._wrap(this._getNode(this._bms.bookmarksMenuFolder), items, "menu");
|
||||
|
|
Загрузка…
Ссылка в новой задаче