зеркало из https://github.com/mozilla/gecko-dev.git
merge upstream changes
This commit is contained in:
Коммит
ba5e8762d2
|
@ -63,7 +63,6 @@ function DAVCollection(baseURL, defaultPrefix) {
|
||||||
this.baseURL = baseURL;
|
this.baseURL = baseURL;
|
||||||
this.defaultPrefix = defaultPrefix;
|
this.defaultPrefix = defaultPrefix;
|
||||||
this._identity = 'DAV:default';
|
this._identity = 'DAV:default';
|
||||||
this._authProvider = new DummyAuthProvider();
|
|
||||||
this._log = Log4Moz.Service.getLogger("Service.DAV");
|
this._log = Log4Moz.Service.getLogger("Service.DAV");
|
||||||
this._log.level =
|
this._log.level =
|
||||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.service.dav")];
|
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.service.dav")];
|
||||||
|
@ -124,16 +123,15 @@ DAVCollection.prototype = {
|
||||||
|
|
||||||
path = this._defaultPrefix + path;
|
path = this._defaultPrefix + path;
|
||||||
|
|
||||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
|
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
|
||||||
request = request.QueryInterface(Ci.nsIDOMEventTarget);
|
|
||||||
|
|
||||||
let cb = self.cb;
|
let xhrCb = self.cb;
|
||||||
request.addEventListener("load", new Utils.EventListener(cb, "load"), false);
|
|
||||||
request.addEventListener("error", new Utils.EventListener(cb, "error"), false);
|
request.onload = new Utils.EventListener(xhrCb, "load");
|
||||||
request = request.QueryInterface(Ci.nsIXMLHttpRequest);
|
request.onerror = new Utils.EventListener(xhrCb, "error");
|
||||||
|
request.mozBackgroundRequest = true;
|
||||||
request.open(op, this._baseURL + path, true);
|
request.open(op, this._baseURL + path, true);
|
||||||
|
|
||||||
|
|
||||||
// Force cache validation
|
// Force cache validation
|
||||||
let channel = request.channel;
|
let channel = request.channel;
|
||||||
channel = channel.QueryInterface(Ci.nsIRequest);
|
channel = channel.QueryInterface(Ci.nsIRequest);
|
||||||
|
@ -150,15 +148,10 @@ DAVCollection.prototype = {
|
||||||
request.setRequestHeader(key, headers[key]);
|
request.setRequestHeader(key, headers[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._authProvider._authFailed = false;
|
|
||||||
request.channel.notificationCallbacks = this._authProvider;
|
|
||||||
|
|
||||||
request.send(data);
|
request.send(data);
|
||||||
let event = yield;
|
let event = yield;
|
||||||
ret = event.target;
|
ret = event.target;
|
||||||
|
|
||||||
if (this._authProvider._authFailed)
|
|
||||||
this._log.warn("_makeRequest: authentication failed");
|
|
||||||
if (ret.status < 200 || ret.status >= 300)
|
if (ret.status < 200 || ret.status >= 300)
|
||||||
this._log.warn("_makeRequest: got status " + ret.status);
|
this._log.warn("_makeRequest: got status " + ret.status);
|
||||||
|
|
||||||
|
@ -316,8 +309,7 @@ DAVCollection.prototype = {
|
||||||
this.GET("", self.cb);
|
this.GET("", self.cb);
|
||||||
let resp = yield;
|
let resp = yield;
|
||||||
|
|
||||||
if (this._authProvider._authFailed ||
|
if (resp.status < 200 || resp.status >= 300) {
|
||||||
resp.status < 200 || resp.status >= 300) {
|
|
||||||
self.done(false);
|
self.done(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -339,8 +331,7 @@ DAVCollection.prototype = {
|
||||||
"</D:propfind>", self.cb);
|
"</D:propfind>", self.cb);
|
||||||
let resp = yield;
|
let resp = yield;
|
||||||
|
|
||||||
if (this._authProvider._authFailed ||
|
if (resp.status < 200 || resp.status >= 300) {
|
||||||
resp.status < 200 || resp.status >= 300) {
|
|
||||||
self.done(false);
|
self.done(false);
|
||||||
yield;
|
yield;
|
||||||
}
|
}
|
||||||
|
@ -376,8 +367,7 @@ DAVCollection.prototype = {
|
||||||
"</D:lockinfo>", self.cb);
|
"</D:lockinfo>", self.cb);
|
||||||
let resp = yield;
|
let resp = yield;
|
||||||
|
|
||||||
if (this._authProvider._authFailed ||
|
if (resp.status < 200 || resp.status >= 300)
|
||||||
resp.status < 200 || resp.status >= 300)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let tokens = Utils.xpath(resp.responseXML, '//D:locktoken/D:href');
|
let tokens = Utils.xpath(resp.responseXML, '//D:locktoken/D:href');
|
||||||
|
@ -413,8 +403,7 @@ DAVCollection.prototype = {
|
||||||
this.UNLOCK("lock", self.cb);
|
this.UNLOCK("lock", self.cb);
|
||||||
let resp = yield;
|
let resp = yield;
|
||||||
|
|
||||||
if (this._authProvider._authFailed ||
|
if (resp.status < 200 || resp.status >= 300) {
|
||||||
resp.status < 200 || resp.status >= 300) {
|
|
||||||
self.done(false);
|
self.done(false);
|
||||||
yield;
|
yield;
|
||||||
}
|
}
|
||||||
|
@ -450,154 +439,3 @@ DAVCollection.prototype = {
|
||||||
self.done(unlocked);
|
self.done(unlocked);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Auth provider object
|
|
||||||
* Taken from nsMicrosummaryService.js and massaged slightly
|
|
||||||
*/
|
|
||||||
|
|
||||||
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,
|
|
||||||
Ci.nsIAuthPrompt,
|
|
||||||
Ci.nsIPrompt,
|
|
||||||
Ci.nsIProgressEventSink,
|
|
||||||
Ci.nsIInterfaceRequestor,
|
|
||||||
Ci.nsISupports],
|
|
||||||
|
|
||||||
// Auth requests appear to succeed when we cancel them (since the server
|
|
||||||
// redirects us to a "you're not authorized" page), so we have to set a flag
|
|
||||||
// to let the load handler know to treat the load as a failure.
|
|
||||||
get _authFailed() { return this.__authFailed; },
|
|
||||||
set _authFailed(newValue) { return this.__authFailed = newValue; },
|
|
||||||
|
|
||||||
// nsISupports
|
|
||||||
|
|
||||||
QueryInterface: function DAP_QueryInterface(iid) {
|
|
||||||
if (!this.interfaces.some( function(v) { return iid.equals(v); } ))
|
|
||||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
|
||||||
|
|
||||||
// nsIAuthPrompt and nsIPrompt need separate implementations because
|
|
||||||
// their method signatures conflict. The other interfaces we implement
|
|
||||||
// within DummyAuthProvider itself.
|
|
||||||
switch(iid) {
|
|
||||||
case Ci.nsIAuthPrompt:
|
|
||||||
return this.authPrompt;
|
|
||||||
case Ci.nsIPrompt:
|
|
||||||
return this.prompt;
|
|
||||||
default:
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIInterfaceRequestor
|
|
||||||
|
|
||||||
getInterface: function DAP_getInterface(iid) {
|
|
||||||
return this.QueryInterface(iid);
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIBadCertListener
|
|
||||||
|
|
||||||
// Suppress UI and abort secure loads from servers with bad SSL certificates.
|
|
||||||
|
|
||||||
confirmUnknownIssuer: function DAP_confirmUnknownIssuer(socketInfo, cert, certAddType) {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
confirmMismatchDomain: function DAP_confirmMismatchDomain(socketInfo, targetURL, cert) {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
confirmCertExpired: function DAP_confirmCertExpired(socketInfo, cert) {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
notifyCrlNextupdate: function DAP_notifyCrlNextupdate(socketInfo, targetURL, cert) {
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIAuthPromptProvider
|
|
||||||
|
|
||||||
getAuthPrompt: function(aPromptReason, aIID) {
|
|
||||||
this._authFailed = true;
|
|
||||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
|
||||||
},
|
|
||||||
|
|
||||||
// HTTP always requests nsIAuthPromptProvider first, so it never needs
|
|
||||||
// nsIAuthPrompt, but not all channels use nsIAuthPromptProvider, so we
|
|
||||||
// implement nsIAuthPrompt too.
|
|
||||||
|
|
||||||
// nsIAuthPrompt
|
|
||||||
|
|
||||||
get authPrompt() {
|
|
||||||
var resource = this;
|
|
||||||
return {
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
|
|
||||||
prompt: function(dialogTitle, text, passwordRealm, savePassword, defaultText, result) {
|
|
||||||
resource._authFailed = true;
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
promptUsernameAndPassword: function(dialogTitle, text, passwordRealm, savePassword, user, pwd) {
|
|
||||||
resource._authFailed = true;
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
promptPassword: function(dialogTitle, text, passwordRealm, savePassword, pwd) {
|
|
||||||
resource._authFailed = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIPrompt
|
|
||||||
|
|
||||||
get prompt() {
|
|
||||||
var resource = this;
|
|
||||||
return {
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
|
|
||||||
alert: function(dialogTitle, text) {
|
|
||||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
},
|
|
||||||
alertCheck: function(dialogTitle, text, checkMessage, checkValue) {
|
|
||||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
},
|
|
||||||
confirm: function(dialogTitle, text) {
|
|
||||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
},
|
|
||||||
confirmCheck: function(dialogTitle, text, checkMessage, checkValue) {
|
|
||||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
},
|
|
||||||
confirmEx: function(dialogTitle, text, buttonFlags, button0Title, button1Title, button2Title, checkMsg, checkValue) {
|
|
||||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
},
|
|
||||||
prompt: function(dialogTitle, text, value, checkMsg, checkValue) {
|
|
||||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
},
|
|
||||||
promptPassword: function(dialogTitle, text, password, checkMsg, checkValue) {
|
|
||||||
resource._authFailed = true;
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
promptUsernameAndPassword: function(dialogTitle, text, username, password, checkMsg, checkValue) {
|
|
||||||
resource._authFailed = true;
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
select: function(dialogTitle, text, count, selectList, outSelection) {
|
|
||||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// nsIProgressEventSink
|
|
||||||
|
|
||||||
onProgress: function DAP_onProgress(aRequest, aContext,
|
|
||||||
aProgress, aProgressMax) {
|
|
||||||
},
|
|
||||||
|
|
||||||
onStatus: function DAP_onStatus(aRequest, aContext,
|
|
||||||
aStatus, aStatusArg) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -570,13 +570,16 @@ Engine.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
_share: function Engine__share(guid, username) {
|
_share: function Engine__share(guid, username) {
|
||||||
|
let self = yield;
|
||||||
/* This should be overridden by the engine subclass for each datatype.
|
/* This should be overridden by the engine subclass for each datatype.
|
||||||
Implementation should share the data node identified by guid,
|
Implementation should share the data node identified by guid,
|
||||||
and all its children, if any, with the user identified by username. */
|
and all its children, if any, with the user identified by username. */
|
||||||
return;
|
self.done();
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO need a "stop sharing" function.
|
/* TODO need a "stop sharing" function.
|
||||||
|
Actually, stopping an outgoing share and stopping an incoming share
|
||||||
|
are two different things. */
|
||||||
|
|
||||||
sync: function Engine_sync(onComplete) {
|
sync: function Engine_sync(onComplete) {
|
||||||
return this._sync.async(this, onComplete);
|
return this._sync.async(this, onComplete);
|
||||||
|
@ -586,7 +589,7 @@ Engine.prototype = {
|
||||||
return this._share.async(this, onComplete, guid, username);
|
return this._share.async(this, onComplete, guid, username);
|
||||||
},
|
},
|
||||||
|
|
||||||
resetServer: function Engine_resetServer(onComplete) {
|
resetServer: function Engimne_resetServer(onComplete) {
|
||||||
this._notify("reset-server", this._resetServer).async(this, onComplete);
|
this._notify("reset-server", this._resetServer).async(this, onComplete);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,50 @@
|
||||||
|
/* ***** 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>
|
||||||
|
* Jono DiCarlo <jdicarlo@mozilla.org>
|
||||||
|
*
|
||||||
|
* 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 = ['BookmarksEngine'];
|
const EXPORTED_SYMBOLS = ['BookmarksEngine'];
|
||||||
|
|
||||||
const Cc = Components.classes;
|
const Cc = Components.classes;
|
||||||
const Ci = Components.interfaces;
|
const Ci = Components.interfaces;
|
||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
|
|
||||||
|
// Annotation to use for shared bookmark folders, incoming and outgoing:
|
||||||
|
const INCOMING_SHARED_ANNO = "weave/shared-incoming";
|
||||||
|
const OUTGOING_SHARED_ANNO = "weave/shared-outgoing";
|
||||||
|
|
||||||
Cu.import("resource://weave/log4moz.js");
|
Cu.import("resource://weave/log4moz.js");
|
||||||
Cu.import("resource://weave/dav.js");
|
Cu.import("resource://weave/dav.js");
|
||||||
Cu.import("resource://weave/util.js");
|
Cu.import("resource://weave/util.js");
|
||||||
|
@ -14,6 +55,12 @@ Cu.import("resource://weave/syncCores.js");
|
||||||
Cu.import("resource://weave/stores.js");
|
Cu.import("resource://weave/stores.js");
|
||||||
Cu.import("resource://weave/trackers.js");
|
Cu.import("resource://weave/trackers.js");
|
||||||
|
|
||||||
|
/* LONGTERM TODO: when we start working on the ability to share other types
|
||||||
|
of data besides bookmarks, the xmppClient instance should be moved to hang
|
||||||
|
off of Weave.Service instead of hanging off the BookmarksEngine. But for
|
||||||
|
now this is the easiest place to deal with it. */
|
||||||
|
Cu.import("resource://weave/xmpp/xmppClient.js");
|
||||||
|
|
||||||
Function.prototype.async = Async.sugar;
|
Function.prototype.async = Async.sugar;
|
||||||
|
|
||||||
function BookmarksEngine(pbeId) {
|
function BookmarksEngine(pbeId) {
|
||||||
|
@ -45,16 +92,177 @@ BookmarksEngine.prototype = {
|
||||||
return this.__tracker;
|
return this.__tracker;
|
||||||
},
|
},
|
||||||
|
|
||||||
syncMounts: function BmkEngine_syncMounts(onComplete) {
|
_init: function BmkEngine__init( pbeId ) {
|
||||||
this._syncMounts.async(this, onComplete);
|
this.__proto__.__proto__._init.call( this, pbeId );
|
||||||
|
if ( Utils.prefs.getBoolPref( "xmpp.enabled" ) ) {
|
||||||
|
dump( "Starting XMPP client for bookmark engine..." );
|
||||||
|
this._startXmppClient.async(this);
|
||||||
|
//this._startXmppClient();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_syncMounts: function BmkEngine__syncMounts() {
|
|
||||||
|
_startXmppClient: function BmkEngine__startXmppClient() {
|
||||||
|
// To be called asynchronously.
|
||||||
let self = yield;
|
let self = yield;
|
||||||
let mounts = this._store.findMounts();
|
|
||||||
|
// Get serverUrl and realm of the jabber server from preferences:
|
||||||
|
let serverUrl = Utils.prefs.getCharPref( "xmpp.server.url" );
|
||||||
|
let realm = Utils.prefs.getCharPref( "xmpp.server.realm" );
|
||||||
|
|
||||||
|
// TODO once we have ejabberd talking to LDAP, the username/password
|
||||||
|
// for xmpp will be the same as the ones for Weave itself, so we can
|
||||||
|
// read username/password like this:
|
||||||
|
// let clientName = ID.get('WeaveID').username;
|
||||||
|
// let clientPassword = ID.get('WeaveID').password;
|
||||||
|
// until then get these from preferences as well:
|
||||||
|
let clientName = Utils.prefs.getCharPref( "xmpp.client.name" );
|
||||||
|
let clientPassword = Utils.prefs.getCharPref( "xmpp.client.password" );
|
||||||
|
|
||||||
|
let transport = new HTTPPollingTransport( serverUrl, false, 15000 );
|
||||||
|
let auth = new PlainAuthenticator();
|
||||||
|
// TODO use MD5Authenticator instead once we get it working -- plain is
|
||||||
|
// a security hole.
|
||||||
|
this._xmppClient = new XmppClient( clientName,
|
||||||
|
realm,
|
||||||
|
clientPassword,
|
||||||
|
transport,
|
||||||
|
auth );
|
||||||
|
let bmkEngine = this;
|
||||||
|
let messageHandler = {
|
||||||
|
handle: function ( messageText, from ) {
|
||||||
|
/* The callback function for incoming xmpp messages.
|
||||||
|
We expect message text to be either:
|
||||||
|
"share <dir>"
|
||||||
|
(sender offers to share directory dir with us)
|
||||||
|
or "stop <dir>"
|
||||||
|
(sender has stopped sharing directory dir with us.)
|
||||||
|
or "accept <dir>"
|
||||||
|
(sharee has accepted our offer to share our dir.)
|
||||||
|
or "decline <dir>"
|
||||||
|
(sharee has declined our offer to share our dir.)
|
||||||
|
*/
|
||||||
|
let words = messageText.split(" ");
|
||||||
|
let commandWord = words[0];
|
||||||
|
let directoryName = words.slice(1).join(" ");
|
||||||
|
if ( commandWord == "share" ) {
|
||||||
|
bmkEngine._incomingShareOffer( directoryName, from );
|
||||||
|
} else if ( commandWord == "stop" ) {
|
||||||
|
bmkEngine._incomingShareWithdrawn( directoryName, from );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._xmppClient.registerMessageHandler( messageHandler );
|
||||||
|
this._xmppClient.connect( realm, self.cb );
|
||||||
|
yield;
|
||||||
|
if ( this._xmppClient._connectionStatus == this._xmppClient.FAILED ) {
|
||||||
|
this._log.warn( "Weave can't log in to xmpp server: xmpp disabled." );
|
||||||
|
} else if ( this._xmppClient._connectionStatus == this._xmppClient.CONNECTED ) {
|
||||||
|
this._log.info( "Weave logged into xmpp OK." );
|
||||||
|
}
|
||||||
|
yield;
|
||||||
|
self.done();
|
||||||
|
},
|
||||||
|
|
||||||
|
_incomingShareOffer: function BmkEngine__incomingShareOffer( dir, user ) {
|
||||||
|
/* Called when we receive an offer from another user to share a
|
||||||
|
directory.
|
||||||
|
|
||||||
|
TODO what should happen is that we add a notification to the queue
|
||||||
|
telling that the incoming share has been offered; when the offer
|
||||||
|
is accepted we will call createIncomingShare and then
|
||||||
|
updateIncomingShare.
|
||||||
|
|
||||||
|
But since we don't have notification in place yet, I'm going to skip
|
||||||
|
right ahead to creating the incoming share.
|
||||||
|
*/
|
||||||
|
dump( "I was offered the directory " + dir + " from user " + dir );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_incomingShareWithdrawn: function BmkEngine__incomingShareStop( dir, user ) {
|
||||||
|
/* Called when we receive a message telling us that a user who has
|
||||||
|
already shared a directory with us has chosen to stop sharing
|
||||||
|
the directory.
|
||||||
|
|
||||||
|
TODO Find the incomingShare in our bookmark tree that corresponds
|
||||||
|
to the shared directory, and delete it; add a notification to
|
||||||
|
the queue telling us what has happened.
|
||||||
|
*/
|
||||||
|
},
|
||||||
|
|
||||||
|
_sync: function BmkEngine__sync() {
|
||||||
|
/* After syncing, also call syncMounts to get the
|
||||||
|
incoming shared bookmark folder contents. */
|
||||||
|
let self = yield;
|
||||||
|
this.__proto__.__proto__._sync.async(this, self.cb );
|
||||||
|
yield;
|
||||||
|
this.updateAllIncomingShares(self.cb);
|
||||||
|
yield;
|
||||||
|
self.done();
|
||||||
|
},
|
||||||
|
|
||||||
|
_share: function BmkEngine__share( selectedFolder, username ) {
|
||||||
|
// Return true if success, false if failure.
|
||||||
|
let ret = false;
|
||||||
|
let ans = Cc["@mozilla.org/browser/annotation-service;1"].
|
||||||
|
getService(Ci.nsIAnnotationService);
|
||||||
|
let self = yield;
|
||||||
|
|
||||||
|
/* TODO What should the behavior be if i'm already sharing it with user
|
||||||
|
A and I ask to share it with user B? (This should be prevented by
|
||||||
|
the UI. */
|
||||||
|
|
||||||
|
// Create the outgoing share folder on the server
|
||||||
|
// TODO do I need to call these asynchronously?
|
||||||
|
//this._createOutgoingShare.async( this, selectedFolder, username );
|
||||||
|
//this._updateOutgoingShare.async( this, selectedFolder, username );
|
||||||
|
|
||||||
|
/* Set the annotation on the folder so we know
|
||||||
|
it's an outgoing share: */
|
||||||
|
dump( "I'm in _share.\n" );
|
||||||
|
let folderItemId = selectedFolder.node.itemId;
|
||||||
|
let folderName = selectedFolder.getAttribute( "label" );
|
||||||
|
ans.setItemAnnotation(folderItemId, OUTGOING_SHARED_ANNO, username, 0,
|
||||||
|
ans.EXPIRE_NEVER);
|
||||||
|
// TODO: does this clobber existing annotations?
|
||||||
|
dump( "I set the annotation...\n" );
|
||||||
|
// Send an xmpp message to the share-ee
|
||||||
|
if ( this._xmppClient ) {
|
||||||
|
if ( this._xmppClient._connectionStatus == this._xmppClient.CONNECTED ) {
|
||||||
|
dump( "Gonna send notification...\n" );
|
||||||
|
let msgText = "share " + folderName;
|
||||||
|
this._xmppClient.sendMessage( username, msgText );
|
||||||
|
} else {
|
||||||
|
this._log.info( "XMPP connection not available for share notification." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LONGTERM TODO: in the future when we allow sharing one folder
|
||||||
|
with many people, the value of the annotation can be a whole list
|
||||||
|
of usernames instead of just one. */
|
||||||
|
|
||||||
|
dump( "Bookmark engine shared " +folderName + " with " + username );
|
||||||
|
ret = true;
|
||||||
|
self.done( ret );
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAllIncomingShares: function BmkEngine_updateAllIncoming(onComplete) {
|
||||||
|
this._updateAllIncomingShares.async(this, onComplete);
|
||||||
|
},
|
||||||
|
_updateAllIncomingShares: function BmkEngine__updateAllIncoming() {
|
||||||
|
/* For every bookmark folder in my tree that has the annotation
|
||||||
|
marking it as an incoming shared folder, pull down its latest
|
||||||
|
contents from its owner's account on the server. (This is
|
||||||
|
a one-way data transfer because I can't modify bookmarks that
|
||||||
|
are owned by someone else but shared to me; any changes I make
|
||||||
|
to the folder contents are simply wiped out by the latest
|
||||||
|
server contents.) */
|
||||||
|
let self = yield;
|
||||||
|
let mounts = this._store.findIncomingShares();
|
||||||
|
|
||||||
for (i = 0; i < mounts.length; i++) {
|
for (i = 0; i < mounts.length; i++) {
|
||||||
try {
|
try {
|
||||||
this._syncOneMount.async(this, self.cb, mounts[i]);
|
this._updateIncomingShare.async(this, self.cb, mounts[i]);
|
||||||
yield;
|
yield;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._log.warn("Could not sync shared folder from " + mounts[i].userid);
|
this._log.warn("Could not sync shared folder from " + mounts[i].userid);
|
||||||
|
@ -63,13 +271,11 @@ BookmarksEngine.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO modify this as neccessary since I just moved it from the engine
|
_createOutgoingShare: function BmkEngine__createOutgoing(folder, username) {
|
||||||
// superclass into BookmarkEngine.
|
|
||||||
_share: function BookmarkEngine__share(guid, username) {
|
|
||||||
let self = yield;
|
let self = yield;
|
||||||
let prefix = DAV.defaultPrefix;
|
let prefix = DAV.defaultPrefix;
|
||||||
|
|
||||||
this._log.debug("Sharing bookmarks with " + username);
|
this._log.debug("Sharing bookmarks from " + guid + " with " + username);
|
||||||
|
|
||||||
this._getSymKey.async(this, self.cb);
|
this._getSymKey.async(this, self.cb);
|
||||||
yield;
|
yield;
|
||||||
|
@ -78,6 +284,7 @@ BookmarksEngine.prototype = {
|
||||||
DAV.GET(this.keysFile, self.cb);
|
DAV.GET(this.keysFile, self.cb);
|
||||||
let ret = yield;
|
let ret = yield;
|
||||||
Utils.ensureStatus(ret.status, "Could not get keys file.");
|
Utils.ensureStatus(ret.status, "Could not get keys file.");
|
||||||
|
// note: this._json is just an encoder/decoder, no state.
|
||||||
let keys = this._json.decode(ret.responseText);
|
let keys = this._json.decode(ret.responseText);
|
||||||
|
|
||||||
// get the other user's pubkey
|
// get the other user's pubkey
|
||||||
|
@ -107,25 +314,31 @@ BookmarksEngine.prototype = {
|
||||||
ret = yield;
|
ret = yield;
|
||||||
Utils.ensureStatus(ret.status, "Could not upload keyring file.");
|
Utils.ensureStatus(ret.status, "Could not upload keyring file.");
|
||||||
|
|
||||||
this._createShare(guid, username, username);
|
|
||||||
|
|
||||||
this._log.debug("All done sharing!");
|
this._log.debug("All done sharing!");
|
||||||
|
|
||||||
|
// Call Atul's js api for setting htaccess:
|
||||||
|
let api = new Sharing.Api( DAV );
|
||||||
|
api.shareWithUsers( directory, [username], self.cb );
|
||||||
|
let result = yield;
|
||||||
|
|
||||||
self.done(true);
|
self.done(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
_createShare: function BookmarkEngine__createShare(guid, id, title) {
|
_updateOutgoingShare: function BmkEngine__updateOutgoing(guid, username) {
|
||||||
/* the bookmark item identified by guid, and the whole subtree under it,
|
/* TODO this needs to have the logic to break the shared bookmark
|
||||||
must be copied out from the main file into a separate file which is
|
subtree out of the store and put it in a separate file...*/
|
||||||
put into the new directory and encrypted with the key in the keychain.
|
},
|
||||||
id is the userid of the user we're sharing with.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* TODO it appears that this just creates the folder and puts the
|
_stopOutgoingShare: function BmkEngine__stopOutgoingShare( guid, username ) {
|
||||||
annotation on it; the mechanics of sharing must be done when syncing?
|
/* TODO implement this... */
|
||||||
|
},
|
||||||
|
|
||||||
Or has that not been done yet at all?
|
_createIncomingShare: function BookmarkEngine__createShare(guid, id, title) {
|
||||||
Do we have to create a new Mount?
|
|
||||||
|
/* TODO This used to be called just _createShare, but its semantics
|
||||||
|
have changed slightly -- its purpose now is to create a new empty
|
||||||
|
incoming shared bookmark folder. To do this is mostly the same code,
|
||||||
|
but it will need a few tweaks.
|
||||||
*/
|
*/
|
||||||
let bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
|
let bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
|
||||||
getService(Ci.nsINavBookmarksService);
|
getService(Ci.nsINavBookmarksService);
|
||||||
|
@ -160,19 +373,23 @@ BookmarksEngine.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_stopShare: function BookmarkeEngine__stopShare( guid, username) {
|
|
||||||
// TODO implement this; also give a way to call it from outside
|
|
||||||
// the service.
|
|
||||||
},
|
|
||||||
|
|
||||||
_syncOneMount: function BmkEngine__syncOneMount(mountData) {
|
_updateIncomingShare: function BmkEngine__updateIncomingShare(mountData) {
|
||||||
|
/* Pull down bookmarks from the server for a single incoming
|
||||||
|
shared folder. */
|
||||||
|
|
||||||
|
/* TODO modify this: the old implementation assumes we want to copy
|
||||||
|
everything that the other user has, by pulling down snapshot and
|
||||||
|
diffs and applying the diffs to the snapshot. Instead, now we just
|
||||||
|
want to get a single subfolder and its children, which will be in
|
||||||
|
a separate file. */
|
||||||
|
|
||||||
let self = yield;
|
let self = yield;
|
||||||
let user = mountData.userid;
|
let user = mountData.userid;
|
||||||
let prefix = DAV.defaultPrefix;
|
let prefix = DAV.defaultPrefix;
|
||||||
let serverURL = Utils.prefs.getCharPref("serverURL");
|
let serverURL = Utils.prefs.getCharPref("serverURL");
|
||||||
let snap = new SnapshotStore();
|
let snap = new SnapshotStore();
|
||||||
|
|
||||||
// TODO this is obviously what we want.
|
|
||||||
this._log.debug("Syncing shared folder from user " + user);
|
this._log.debug("Syncing shared folder from user " + user);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -738,11 +955,15 @@ BookmarksStore.prototype = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
findMounts: function BStore_findMounts() {
|
findIncomingShares: function BStore_findIncomingShares() {
|
||||||
|
/* Returns list of mount data structures, each of which
|
||||||
|
represents one incoming shared-bookmark folder. */
|
||||||
let ret = [];
|
let ret = [];
|
||||||
let a = this._ans.getItemsWithAnnotation("weave/mounted-share-id", {});
|
let a = this._ans.getItemsWithAnnotation(INCOMING_SHARED_ANNO, {});
|
||||||
for (let i = 0; i < a.length; i++) {
|
for (let i = 0; i < a.length; i++) {
|
||||||
let id = this._ans.getItemAnnotation(a[i], "weave/mounted-share-id");
|
/* The value of the incoming-shared annotation is the id of the
|
||||||
|
person who has shared it with us. Get that value: */
|
||||||
|
let id = this._ans.getItemAnnotation(a[i], INCOMING_SHARED_ANNO);
|
||||||
ret.push(this._wrapMount(this._getNode(a[i]), id));
|
ret.push(this._wrapMount(this._getNode(a[i]), id));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -427,15 +427,6 @@ WeaveSvc.prototype = {
|
||||||
_login: function WeaveSync__login(password, passphrase) {
|
_login: function WeaveSync__login(password, passphrase) {
|
||||||
let self = yield;
|
let self = yield;
|
||||||
|
|
||||||
// XmlHttpRequests fail when the window that triggers them goes away
|
|
||||||
// because of bug 317600, and the first XmlHttpRequest we do happens
|
|
||||||
// just before the login dialog closes itself (for logins prompted by
|
|
||||||
// that dialog), so it triggers the bug. To work around it, we pause
|
|
||||||
// here and then continue after a 0ms timeout.
|
|
||||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
||||||
timer.initWithCallback({ notify: self.cb }, 0, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
||||||
yield;
|
|
||||||
|
|
||||||
// cache password & passphrase
|
// cache password & passphrase
|
||||||
// if null, we'll try to get them from the pw manager below
|
// if null, we'll try to get them from the pw manager below
|
||||||
ID.get('WeaveID').setTempPassword(password);
|
ID.get('WeaveID').setTempPassword(password);
|
||||||
|
@ -546,10 +537,6 @@ WeaveSvc.prototype = {
|
||||||
this._notify(engines[i].name + "-engine:sync",
|
this._notify(engines[i].name + "-engine:sync",
|
||||||
this._syncEngine, engines[i]).async(this, self.cb);
|
this._syncEngine, engines[i]).async(this, self.cb);
|
||||||
yield;
|
yield;
|
||||||
if (engines[i].name == "bookmarks") { // FIXME
|
|
||||||
Engines.get("bookmarks").syncMounts(self.cb);
|
|
||||||
yield;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -595,11 +582,6 @@ WeaveSvc.prototype = {
|
||||||
// overloaded, we'll contribute to the problem by trying to sync
|
// overloaded, we'll contribute to the problem by trying to sync
|
||||||
// repeatedly at the maximum rate.
|
// repeatedly at the maximum rate.
|
||||||
this._syncThresholds[engine.name] = INITIAL_THRESHOLD;
|
this._syncThresholds[engine.name] = INITIAL_THRESHOLD;
|
||||||
|
|
||||||
if (engine.name == "bookmarks") { // FIXME
|
|
||||||
Engines.get("bookmarks").syncMounts(self.cb);
|
|
||||||
yield;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this._log.debug(engine.name + " score " + score +
|
this._log.debug(engine.name + " score " + score +
|
||||||
|
@ -677,23 +659,27 @@ WeaveSvc.prototype = {
|
||||||
Implementation, as well as the interpretation of what 'guid' means,
|
Implementation, as well as the interpretation of what 'guid' means,
|
||||||
is left up to the engine for the specific dataType. */
|
is left up to the engine for the specific dataType. */
|
||||||
|
|
||||||
// TODO who is listening for the share-bookmarks message?
|
|
||||||
let messageName = "share-" + dataType;
|
let messageName = "share-" + dataType;
|
||||||
// so for instance, if dataType is "bookmarks" then a message
|
/* so for instance, if dataType is "bookmarks" then a message
|
||||||
// "share-bookmarks" will be sent out to any observers who are listening
|
"share-bookmarks" will be sent out to any observers who are listening
|
||||||
// for it.
|
for it. As far as I know, there aren't currently any listeners for
|
||||||
|
"share-bookmarks" but we'll send it out just in case. */
|
||||||
|
dump( "This fails with an Exception: cannot aquire internal lock.\n" );
|
||||||
this._lock(this._notify(messageName,
|
this._lock(this._notify(messageName,
|
||||||
this._shareData,
|
this._shareData,
|
||||||
dataType,
|
dataType,
|
||||||
guid,
|
guid,
|
||||||
username)).async(this, onComplete);
|
username)).async(this, onComplete);
|
||||||
},
|
},
|
||||||
_shareBookmarks: function WeaveSync__shareBookmarks(dataType,
|
|
||||||
guid,
|
_shareData: function WeaveSync__shareData(dataType,
|
||||||
username) {
|
guid,
|
||||||
|
username) {
|
||||||
let self = yield;
|
let self = yield;
|
||||||
if (Engines.get(dataType).enabled)
|
if (!Engines.get(dataType).enabled) {
|
||||||
|
this._log.warn( "Can't share disabled data type: " + dataType );
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
Engines.get(dataType).share(self.cb, guid, username);
|
Engines.get(dataType).share(self.cb, guid, username);
|
||||||
let ret = yield;
|
let ret = yield;
|
||||||
self.done(ret);
|
self.done(ret);
|
||||||
|
|
|
@ -53,6 +53,7 @@ XmppClient.prototype = {
|
||||||
this._iqResponders = [];
|
this._iqResponders = [];
|
||||||
this._nextIqId = 0;
|
this._nextIqId = 0;
|
||||||
this._pendingIqs = {};
|
this._pendingIqs = {};
|
||||||
|
this._callbackOnConnect = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
__parser: null,
|
__parser: null,
|
||||||
|
@ -81,10 +82,17 @@ XmppClient.prototype = {
|
||||||
namespace */
|
namespace */
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_finishConnectionAttempt: function() {
|
||||||
|
if ( this._callbackOnConnect ) {
|
||||||
|
this._callbackOnConnect.call();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setError: function( errorText ) {
|
setError: function( errorText ) {
|
||||||
LOG( "Error: " + errorText );
|
LOG( "Error: " + errorText );
|
||||||
this._error = errorText;
|
this._error = errorText;
|
||||||
this._connectionStatus = this.FAILED;
|
this._connectionStatus = this.FAILED;
|
||||||
|
this._finishConnectionAttempt();
|
||||||
},
|
},
|
||||||
|
|
||||||
onIncomingData: function( messageText ) {
|
onIncomingData: function( messageText ) {
|
||||||
|
@ -126,17 +134,19 @@ XmppClient.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message is parseable, now look for message-level errors.
|
// Message is parseable, now look for message-level errors.
|
||||||
|
|
||||||
var rootElem = responseDOM.documentElement;
|
var rootElem = responseDOM.documentElement;
|
||||||
|
|
||||||
var errors = rootElem.getElementsByTagName( "stream:error" );
|
var errors = rootElem.getElementsByTagName( "stream:error" );
|
||||||
if ( errors.length > 0 ) {
|
if ( errors.length > 0 ) {
|
||||||
this.setError( errors[0].firstChild.nodeName );
|
this.setError( errors[0].firstChild.nodeName );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
errors = rootElem.getElementsByTagName( "error" );
|
||||||
|
if ( errors.length > 0 ) {
|
||||||
|
this.setError( errors[0].firstChild.nodeName );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Stream is valid.
|
// Stream is valid.
|
||||||
|
|
||||||
// Detect and handle mid-authentication steps.
|
// Detect and handle mid-authentication steps.
|
||||||
if ( this._connectionStatus == this.CALLED_SERVER ) {
|
if ( this._connectionStatus == this.CALLED_SERVER ) {
|
||||||
// skip TLS, go straight to SALS. (encryption should be negotiated
|
// skip TLS, go straight to SALS. (encryption should be negotiated
|
||||||
|
@ -148,7 +158,7 @@ XmppClient.prototype = {
|
||||||
this.setError( this._authenticationLayer.getError() );
|
this.setError( this._authenticationLayer.getError() );
|
||||||
} else if ( response == this._authenticationLayer.COMPLETION_CODE ){
|
} else if ( response == this._authenticationLayer.COMPLETION_CODE ){
|
||||||
this._connectionStatus = this.CONNECTED;
|
this._connectionStatus = this.CONNECTED;
|
||||||
LOG( "We be connected!!" );
|
this._finishConnectionAttempt();
|
||||||
} else {
|
} else {
|
||||||
this._transportLayer.send( response );
|
this._transportLayer.send( response );
|
||||||
}
|
}
|
||||||
|
@ -296,12 +306,16 @@ XmppClient.prototype = {
|
||||||
this.setError( errorText );
|
this.setError( errorText );
|
||||||
},
|
},
|
||||||
|
|
||||||
connect: function( host ) {
|
connect: function( host, callback ) {
|
||||||
// Do the handshake to connect with the server and authenticate.
|
/* Do the handshake to connect with the server and authenticate.
|
||||||
|
callback is optional: if provided, it will be called (with no arguments)
|
||||||
|
when the connection has either succeeded or failed. */
|
||||||
|
if ( callback ) {
|
||||||
|
this._callbackOnConnect = callback;
|
||||||
|
}
|
||||||
this._transportLayer.connect();
|
this._transportLayer.connect();
|
||||||
this._transportLayer.setCallbackObject( this );
|
this._transportLayer.setCallbackObject( this );
|
||||||
this._transportLayer.send( this._makeHeaderXml( host ) );
|
this._transportLayer.send( this._makeHeaderXml( host ) );
|
||||||
|
|
||||||
this._connectionStatus = this.CALLED_SERVER;
|
this._connectionStatus = this.CALLED_SERVER;
|
||||||
// Now we wait... the rest of the protocol will be driven by
|
// Now we wait... the rest of the protocol will be driven by
|
||||||
// onIncomingData.
|
// onIncomingData.
|
||||||
|
|
|
@ -31,3 +31,10 @@ pref("extensions.weave.log.logger.service.crypto", "Debug");
|
||||||
pref("extensions.weave.log.logger.service.dav", "Debug");
|
pref("extensions.weave.log.logger.service.dav", "Debug");
|
||||||
pref("extensions.weave.log.logger.service.engine", "Debug");
|
pref("extensions.weave.log.logger.service.engine", "Debug");
|
||||||
pref("extensions.weave.log.logger.service.main", "Trace");
|
pref("extensions.weave.log.logger.service.main", "Trace");
|
||||||
|
|
||||||
|
pref("extensions.weave.xmpp.enabled", true);
|
||||||
|
pref("extensions.weave.xmpp.server.url",
|
||||||
|
"http://sm-labs01.mozilla.org:5280/http-poll");
|
||||||
|
pref("extensions.weave.xmpp.server.realm", "sm-labs01.mozilla.org");
|
||||||
|
pref("extensions.weave.xmpp.client.name", "");
|
||||||
|
pref("extensions.weave.xmpp.client.password", "");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче