зеркало из https://github.com/mozilla/gecko-dev.git
Automated merge with ssh://hg.mozilla.org/labs/weave/
This commit is contained in:
Коммит
001d6fc9e7
|
@ -48,7 +48,8 @@ Cu.import("resource://weave/util.js");
|
|||
* Asynchronous generator helpers
|
||||
*/
|
||||
|
||||
let currentId = 0;
|
||||
let gCurrentId = 0;
|
||||
let gOutstandingGenerators = 0;
|
||||
|
||||
function AsyncException(initFrame, message) {
|
||||
this.message = message;
|
||||
|
@ -71,13 +72,14 @@ AsyncException.prototype = {
|
|||
};
|
||||
|
||||
function Generator(thisArg, method, onComplete, args) {
|
||||
gOutstandingGenerators++;
|
||||
this._outstandingCbs = 0;
|
||||
this._log = Log4Moz.Service.getLogger("Async.Generator");
|
||||
this._log.level =
|
||||
Log4Moz.Level[Utils.prefs.getCharPref("log.logger.async")];
|
||||
this._thisArg = thisArg;
|
||||
this._method = method;
|
||||
this._id = currentId++;
|
||||
this._id = gCurrentId++;
|
||||
this.onComplete = onComplete;
|
||||
this._args = args;
|
||||
this._initFrame = Components.stack.caller;
|
||||
|
@ -251,6 +253,7 @@ Generator.prototype = {
|
|||
this._log.trace("Initial stack trace:\n" + this.trace);
|
||||
}
|
||||
}
|
||||
gOutstandingGenerators--;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -280,6 +283,7 @@ function trace(frame, str) {
|
|||
|
||||
|
||||
Async = {
|
||||
get outstandingGenerators() { return gOutstandingGenerators; },
|
||||
|
||||
// Use:
|
||||
// let gen = Async.run(this, this.fooGen, ...);
|
||||
|
|
|
@ -65,27 +65,12 @@ CryptoSvc.prototype = {
|
|||
return this.__os;
|
||||
},
|
||||
|
||||
__xxtea: {},
|
||||
__xxteaLoaded: false,
|
||||
get _xxtea() {
|
||||
if (!this.__xxteaLoaded) {
|
||||
Cu.import("resource://weave/xxtea.js", this.__xxtea);
|
||||
this.__xxteaLoaded = true;
|
||||
}
|
||||
return this.__xxtea;
|
||||
},
|
||||
|
||||
get defaultAlgorithm() {
|
||||
let branch = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefBranch);
|
||||
return branch.getCharPref("extensions.weave.encryption");
|
||||
return Utils.prefs.getCharPref("encryption");
|
||||
},
|
||||
set defaultAlgorithm(value) {
|
||||
let branch = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefBranch);
|
||||
let cur = branch.getCharPref("extensions.weave.encryption");
|
||||
if (value != cur)
|
||||
branch.setCharPref("extensions.weave.encryption", value);
|
||||
if (value != Utils.prefs.getCharPref("encryption"))
|
||||
Utils.prefs.setCharPref("encryption", value);
|
||||
},
|
||||
|
||||
_init: function Crypto__init() {
|
||||
|
@ -374,20 +359,17 @@ CryptoSvc.prototype = {
|
|||
|
||||
let cur = branch.getCharPref("extensions.weave.encryption");
|
||||
if (cur == data)
|
||||
return;
|
||||
return;
|
||||
|
||||
switch (data) {
|
||||
case "none":
|
||||
this._log.info("Encryption disabled");
|
||||
break;
|
||||
case "XXTEA":
|
||||
case "XXXTEA": // Weave 0.1 had this typo
|
||||
this._log.info("Using encryption algorithm: " + data);
|
||||
break;
|
||||
default:
|
||||
this._log.warn("Unknown encryption algorithm, resetting");
|
||||
branch.setCharPref("extensions.weave.encryption", "XXTEA");
|
||||
return; // otherwise we'll send the alg changed event twice
|
||||
case "none":
|
||||
this._log.info("Encryption disabled");
|
||||
break;
|
||||
|
||||
default:
|
||||
this._log.warn("Unknown encryption algorithm, resetting");
|
||||
branch.clearUserPref("extensions.weave.encryption");
|
||||
return; // otherwise we'll send the alg changed event twice
|
||||
}
|
||||
// FIXME: listen to this bad boy somewhere
|
||||
this._os.notifyObservers(null, "weave:encryption:algorithm-changed", "");
|
||||
|
@ -414,23 +396,6 @@ CryptoSvc.prototype = {
|
|||
ret = data;
|
||||
break;
|
||||
|
||||
case "XXXTEA": // Weave 0.1.12.10 and below had this typo
|
||||
case "XXTEA": {
|
||||
let gen = this._xxtea.encrypt(data, identity.password);
|
||||
ret = gen.next();
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
try {
|
||||
while (typeof(ret) == "object") {
|
||||
timer.initWithCallback(self.listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
ret = gen.next();
|
||||
}
|
||||
gen.close();
|
||||
} finally {
|
||||
timer = null;
|
||||
}
|
||||
} break;
|
||||
|
||||
case "aes-128-cbc":
|
||||
case "aes-192-cbc":
|
||||
case "aes-256-cbc":
|
||||
|
@ -464,23 +429,6 @@ CryptoSvc.prototype = {
|
|||
ret = data;
|
||||
break;
|
||||
|
||||
case "XXXTEA": // Weave 0.1.12.10 and below had this typo
|
||||
case "XXTEA": {
|
||||
let gen = this._xxtea.decrypt(data, identity.password);
|
||||
ret = gen.next();
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
try {
|
||||
while (typeof(ret) == "object") {
|
||||
timer.initWithCallback(self.listener, 0, timer.TYPE_ONE_SHOT);
|
||||
yield; // Yield to main loop
|
||||
ret = gen.next();
|
||||
}
|
||||
gen.close();
|
||||
} finally {
|
||||
timer = null;
|
||||
}
|
||||
} break;
|
||||
|
||||
case "aes-128-cbc":
|
||||
case "aes-192-cbc":
|
||||
case "aes-256-cbc":
|
||||
|
|
|
@ -44,6 +44,10 @@ 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";
|
||||
const SERVER_PATH_ANNO = "weave/shared-server-path";
|
||||
// Standard names for shared files on the server
|
||||
const KEYRING_FILE_NAME = "keyring";
|
||||
const SHARED_BOOKMARK_FILE_NAME = "shared_bookmarks";
|
||||
|
||||
Cu.import("resource://weave/log4moz.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
|
@ -97,7 +101,6 @@ BookmarksEngine.prototype = {
|
|||
if ( Utils.prefs.getBoolPref( "xmpp.enabled" ) ) {
|
||||
dump( "Starting XMPP client for bookmark engine..." );
|
||||
this._startXmppClient.async(this);
|
||||
//this._startXmppClient();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -108,7 +111,7 @@ BookmarksEngine.prototype = {
|
|||
// 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:
|
||||
|
@ -117,9 +120,8 @@ BookmarksEngine.prototype = {
|
|||
// 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();
|
||||
let auth = new PlainAuthenticator();
|
||||
// TODO use MD5Authenticator instead once we get it working -- plain is
|
||||
// a security hole.
|
||||
this._xmppClient = new XmppClient( clientName,
|
||||
|
@ -131,26 +133,31 @@ BookmarksEngine.prototype = {
|
|||
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.)
|
||||
We expect message text to be in the form of:
|
||||
<command> <serverpath> <foldername>.
|
||||
Where command is one of:
|
||||
"share" (sender offers to share directory dir with us)
|
||||
or "stop" (sender has stopped sharing directory dir with us.)
|
||||
or "accept" (sharee has accepted our offer to share our dir.)
|
||||
or "decline" (sharee has declined our offer to share our dir.)
|
||||
|
||||
Folder name is the human-readable name of the bookmark folder
|
||||
being shared (it can contain spaces). serverpath is the path
|
||||
on the server to the directory where the data is stored:
|
||||
only the machine seese this, and it can't have spaces.
|
||||
*/
|
||||
let words = messageText.split(" ");
|
||||
let commandWord = words[0];
|
||||
let directoryName = words.slice(1).join(" ");
|
||||
let serverPath = words[1];
|
||||
let directoryName = words.slice(2).join(" ");
|
||||
if ( commandWord == "share" ) {
|
||||
bmkEngine._incomingShareOffer( directoryName, from );
|
||||
bmkEngine._incomingShareOffer(from, serverPath, folderName);
|
||||
} else if ( commandWord == "stop" ) {
|
||||
bmkEngine._incomingShareWithdrawn( directoryName, from );
|
||||
bmkEngine._incomingShareWithdrawn(from, serverPath, folderName);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._xmppClient.registerMessageHandler( messageHandler );
|
||||
this._xmppClient.connect( realm, self.cb );
|
||||
yield;
|
||||
|
@ -163,9 +170,11 @@ BookmarksEngine.prototype = {
|
|||
self.done();
|
||||
},
|
||||
|
||||
_incomingShareOffer: function BmkEngine__incomingShareOffer( dir, user ) {
|
||||
/* Called when we receive an offer from another user to share a
|
||||
directory.
|
||||
_incomingShareOffer: function BmkEngine__incomingShareOffer(user,
|
||||
serverPath,
|
||||
folderName) {
|
||||
/* Called when we receive an offer from another user to share a
|
||||
folder.
|
||||
|
||||
TODO what should happen is that we add a notification to the queue
|
||||
telling that the incoming share has been offered; when the offer
|
||||
|
@ -176,14 +185,17 @@ BookmarksEngine.prototype = {
|
|||
right ahead to creating the incoming share.
|
||||
*/
|
||||
dump( "I was offered the directory " + dir + " from user " + dir );
|
||||
|
||||
_createIncomingShare( user, serverPath, folderName );
|
||||
},
|
||||
|
||||
_incomingShareWithdrawn: function BmkEngine__incomingShareStop( dir, user ) {
|
||||
_incomingShareWithdrawn: function BmkEngine__incomingShareStop(user,
|
||||
serverPath,
|
||||
folderName) {
|
||||
|
||||
/* 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.
|
||||
|
@ -196,6 +208,8 @@ BookmarksEngine.prototype = {
|
|||
let self = yield;
|
||||
this.__proto__.__proto__._sync.async(this, self.cb );
|
||||
yield;
|
||||
this.updateAllOutgoingShares(self.cb);
|
||||
yield;
|
||||
this.updateAllIncomingShares(self.cb);
|
||||
yield;
|
||||
self.done();
|
||||
|
@ -213,13 +227,15 @@ BookmarksEngine.prototype = {
|
|||
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 );
|
||||
dump( "About to call _createOutgoingShare asynchronously.\n" );
|
||||
this._createOutgoingShare.async( this, self.cb, selectedFolder, username );
|
||||
let serverPath = yield;
|
||||
dump( "Done calling _createOutgoingShare asynchronously.\n" );
|
||||
this._updateOutgoingShare.async( this, self.cb, selectedFolder );
|
||||
yield;
|
||||
|
||||
/* 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,
|
||||
|
@ -229,21 +245,21 @@ BookmarksEngine.prototype = {
|
|||
// 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;
|
||||
let msgText = "share " + serverPath + " " + folderName;
|
||||
this._log.debug( "Sending XMPP message: " + msgText );
|
||||
this._xmppClient.sendMessage( username, msgText );
|
||||
} else {
|
||||
this._log.info( "XMPP connection not available for share notification." );
|
||||
this._log.warn( "No XMPP connection 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 );
|
||||
dump( "Bookmark engine shared " +folderName + " with " + username + "\n" );
|
||||
ret = true;
|
||||
self.done( ret );
|
||||
self.done( true );
|
||||
},
|
||||
|
||||
updateAllIncomingShares: function BmkEngine_updateAllIncoming(onComplete) {
|
||||
|
@ -271,66 +287,132 @@ BookmarksEngine.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
updateAllOutgoingShares: function BmkEngine_updateAllOutgoing(onComplete) {
|
||||
// TODO implement me.
|
||||
// Pseudocode:
|
||||
// let shares = findFoldersWithAnnotation( OUTGOING_SHARE_ANNO );
|
||||
// for ( let share in shares ) {
|
||||
// if ( share.hasChanged() ) {
|
||||
// this._updateOutgoingShare( share );
|
||||
// }
|
||||
// }
|
||||
// Not sure how to implement share.hasChanged(). See if there's a
|
||||
// corresponding entry in the latest diff?
|
||||
},
|
||||
|
||||
_createOutgoingShare: function BmkEngine__createOutgoing(folder, username) {
|
||||
/* To be called asynchronously. Folder is a node indicating the bookmark
|
||||
folder that is being shared; username is a string indicating the user
|
||||
that it is to be shared with. This function creates the directory and
|
||||
keyring on the server in which the shared data will be put, but it
|
||||
doesn't actually put the bookmark data there (that's done in
|
||||
_updateOutgoingShare().) */
|
||||
let self = yield;
|
||||
let prefix = DAV.defaultPrefix;
|
||||
let myUserName = ID.get('WeaveID').username;
|
||||
this._log.debug("Sharing bookmarks from " + folder.getAttribute( "label" )
|
||||
+ " with " + username);
|
||||
|
||||
this._log.debug("Sharing bookmarks from " + guid + " with " + username);
|
||||
/* Generate a new GUID to use as the new directory name on the server
|
||||
in which we'll store the shared data. */
|
||||
let uuidgen = Cc["@mozilla.org/uuid-generator;1"].
|
||||
getService(Ci.nsIUUIDGenerator);
|
||||
let folderGuid = uuidgen.generateUUID().toString().replace(/[{}]/g, '');
|
||||
|
||||
this._getSymKey.async(this, self.cb);
|
||||
/* Create the directory on the server if it does not exist already. */
|
||||
let serverPath = "/user/" + myUserName + "/share/" + folderGuid;
|
||||
DAV.MKCOL(serverPath, self.cb);
|
||||
let ret = yield;
|
||||
if (!ret) {
|
||||
this._log.error("Can't create remote folder for outgoing share.");
|
||||
self.done(false);
|
||||
}
|
||||
// TODO more error handling
|
||||
|
||||
/* Store the path to the server directory in an annotation on the shared
|
||||
bookmark folder, so we can easily get back to it later. */
|
||||
let ans = Cc["@mozilla.org/browser/annotation-service;1"].
|
||||
getService(Ci.nsIAnnotationService);
|
||||
ans.setItemAnnotation(folder.node.itemId,
|
||||
SERVER_PATH_ANNO,
|
||||
serverPath,
|
||||
0,
|
||||
ans.EXPIRE_NEVER);
|
||||
|
||||
// Create a new symmetric key, to be used only for encrypting this share.
|
||||
Crypto.PBEkeygen.async(Crypto, self.cb);
|
||||
let newSymKey = yield;
|
||||
|
||||
/* Get public keys for me and the user I'm sharing with.
|
||||
Each user's public key is stored in /user/username/public/pubkey. */
|
||||
let myPubKeyFile = new Resource("/user/" + myUserName + "/public/pubkey");
|
||||
myPubKeyFile.get(self.cb);
|
||||
let myPubKey = yield;
|
||||
let userPubKeyFile = new Resource("/user/" + username + "/public/pubkey");
|
||||
userPubKeyFile.get(self.cb);
|
||||
let userPubKey = yield;
|
||||
|
||||
/* Create the keyring, containing the sym key encrypted with each
|
||||
of our public keys: */
|
||||
Crypto.RSAencrypt.async(Crypto, self.cb, symKey, {pubkey: myPubKey} );
|
||||
let encryptedForMe = yield;
|
||||
Crypto.RSAencrypt.async(Crypto, self.cb, symKey, {pubkey: userPubKey} );
|
||||
let encryptedForYou = yield;
|
||||
let keyring = { myUserName: encryptedForMe,
|
||||
username: encryptedForYou };
|
||||
let keyringFile = new Resource( serverPath + "/" + KEYRING_FILE_NAME );
|
||||
keyringFile.put( self.cb, this._json.encode( keyring ) );
|
||||
yield;
|
||||
|
||||
// copied from getSymKey
|
||||
DAV.GET(this.keysFile, self.cb);
|
||||
let ret = yield;
|
||||
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);
|
||||
|
||||
// get the other user's pubkey
|
||||
let serverURL = Utils.prefs.getCharPref("serverURL");
|
||||
|
||||
try {
|
||||
DAV.defaultPrefix = "user/" + username + "/";
|
||||
DAV.GET("public/pubkey", self.cb);
|
||||
ret = yield;
|
||||
}
|
||||
catch (e) { throw e; }
|
||||
finally { DAV.defaultPrefix = prefix; }
|
||||
|
||||
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[username] = enckey;
|
||||
DAV.PUT(this.keysFile, this._json.encode(keys), self.cb);
|
||||
ret = yield;
|
||||
Utils.ensureStatus(ret.status, "Could not upload keyring file.");
|
||||
|
||||
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 sharingApi = new Sharing.Api( DAV );
|
||||
sharingApi.shareWithUsers( serverPath, [username], self.cb );
|
||||
let result = yield;
|
||||
|
||||
self.done(true);
|
||||
// return the server path:
|
||||
self.done( serverPath );
|
||||
},
|
||||
|
||||
_updateOutgoingShare: function BmkEngine__updateOutgoing(guid, username) {
|
||||
/* TODO this needs to have the logic to break the shared bookmark
|
||||
subtree out of the store and put it in a separate file...*/
|
||||
_updateOutgoingShare: function BmkEngine__updateOutgoing(folderNode) {
|
||||
/* Puts all the bookmark data from the specified bookmark folder,
|
||||
encrypted, onto the shared directory on the server (replacing
|
||||
anything that was already there).
|
||||
|
||||
TODO: error handling*/
|
||||
let self = yield;
|
||||
let myUserName = ID.get('WeaveID').username;
|
||||
// The folder has an annotation specifying the server path to the
|
||||
// directory:
|
||||
let ans = Cc["@mozilla.org/browser/annotation-service;1"].
|
||||
getService(Ci.nsIAnnotationService);
|
||||
let serverPath = ans.getItemAnnotation(folderNode, SERVER_PATH_ANNO);
|
||||
// From that directory, get the keyring file, and from it, the symmetric
|
||||
// key that we'll use to encrypt.
|
||||
let keyringFile = new Resource(serverPath + "/" + KEYRING_FILE_NAME);
|
||||
keyringFile.get(self.cb);
|
||||
let keyring = yield;
|
||||
let symKey = keyring[ myUserName ];
|
||||
// Get the
|
||||
let json = this._store._wrapMount( folderNode, myUserName );
|
||||
/* TODO what does wrapMount do with this username? Should I be passing
|
||||
in my own or that of the person I share with? */
|
||||
|
||||
// Encrypt it with the symkey and put it into the shared-bookmark file.
|
||||
let bookmarkFile = new Resource(serverPath + "/" + SHARED_BOOKMARK_FILE_NAME);
|
||||
Crypto.PBEencrypt.async( Crypto, self.cb, json, {password:symKey} );
|
||||
let cyphertext = yield;
|
||||
bookmarkFile.put( self.cb, cyphertext );
|
||||
yield;
|
||||
self.done();
|
||||
},
|
||||
|
||||
_stopOutgoingShare: function BmkEngine__stopOutgoingShare( guid, username ) {
|
||||
_stopOutgoingShare: function BmkEngine__stopOutgoingShare(folderNode) {
|
||||
/* TODO implement this... */
|
||||
|
||||
// pseudocode:
|
||||
// let serverPath = getAnnotation( folderNode, SERVER_PATH_ANNO );
|
||||
// removeAnnotationFromFolder( folderNode, SERVER_PATH_ANNO );
|
||||
// removeAnnotationFromFolder( folderNode, OUTGOING_SHARED_ANNO );
|
||||
// server.deleteAllFromDirectory( serverPath );
|
||||
},
|
||||
|
||||
_createIncomingShare: function BookmarkEngine__createShare(guid, id, title) {
|
||||
|
@ -378,6 +460,9 @@ BookmarksEngine.prototype = {
|
|||
/* Pull down bookmarks from the server for a single incoming
|
||||
shared folder. */
|
||||
|
||||
// TODO can use this._remote.keys.getKey( sharer_identity ) to get
|
||||
// the symmetric key for decryption of the resource. (That will throw
|
||||
// exception if the resource isn't shared with me.)
|
||||
/* 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
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
const EXPORTED_SYMBOLS = ['PasswordEngine'];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://weave/util.js");
|
||||
|
@ -30,8 +28,8 @@ function _hashLoginInfo(aLogin) {
|
|||
return Utils.sha1(loginKey);
|
||||
}
|
||||
|
||||
function PasswordEngine(pbeId) {
|
||||
this._init(pbeId);
|
||||
function PasswordEngine() {
|
||||
this._init();
|
||||
}
|
||||
PasswordEngine.prototype = {
|
||||
get name() { return "passwords"; },
|
||||
|
@ -63,8 +61,7 @@ PasswordSyncCore.prototype = {
|
|||
__loginManager : null,
|
||||
get _loginManager() {
|
||||
if (!this.__loginManager)
|
||||
this.__loginManager = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
this.__loginManager = Utils.getLoginManager();
|
||||
return this.__loginManager;
|
||||
},
|
||||
|
||||
|
@ -100,21 +97,17 @@ PasswordStore.prototype = {
|
|||
__loginManager : null,
|
||||
get _loginManager() {
|
||||
if (!this.__loginManager)
|
||||
this.__loginManager = Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
this.__loginManager = Utils.getLoginManager();
|
||||
return this.__loginManager;
|
||||
},
|
||||
|
||||
__nsLoginInfo : null,
|
||||
get _nsLoginInfo() {
|
||||
if (!this.__nsLoginInfo)
|
||||
this.__nsLoginInfo = new Components.Constructor(
|
||||
"@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo, "init");
|
||||
this.__nsLoginInfo = Utils.makeNewLoginInfo();
|
||||
return this.__nsLoginInfo;
|
||||
},
|
||||
|
||||
|
||||
_createCommand: function PasswordStore__createCommand(command) {
|
||||
this._log.info("PasswordStore got createCommand: " + command );
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ IDManager.prototype = {
|
|||
if (this._aliases[name])
|
||||
return this._ids[this._aliases[name]];
|
||||
else
|
||||
return this._ids[name]
|
||||
return this._ids[name];
|
||||
},
|
||||
set: function IDMgr_set(name, id) {
|
||||
this._ids[name] = id;
|
||||
|
@ -112,49 +112,14 @@ Identity.prototype = {
|
|||
_password: null,
|
||||
get password() {
|
||||
if (!this._password)
|
||||
return findPassword(this.realm, this.username);
|
||||
return Utils.findPassword(this.realm, this.username);
|
||||
return this._password;
|
||||
},
|
||||
set password(value) {
|
||||
setPassword(this.realm, this.username, value);
|
||||
Utils.setPassword(this.realm, this.username, value);
|
||||
},
|
||||
|
||||
setTempPassword: function Id_setTempPassword(value) {
|
||||
this._password = value;
|
||||
}
|
||||
};
|
||||
|
||||
// fixme: move these to util.js?
|
||||
function findPassword(realm, username) {
|
||||
// fixme: make a request and get the realm ?
|
||||
let password;
|
||||
let lm = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||
let logins = lm.findLogins({}, 'chrome://sync', null, realm);
|
||||
|
||||
for (let i = 0; i < logins.length; i++) {
|
||||
if (logins[i].username == username) {
|
||||
password = logins[i].password;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
function setPassword(realm, username, password) {
|
||||
// cleanup any existing passwords
|
||||
let lm = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||
let logins = lm.findLogins({}, 'chrome://sync', null, realm);
|
||||
for(let i = 0; i < logins.length; i++) {
|
||||
lm.removeLogin(logins[i]);
|
||||
}
|
||||
|
||||
if (!password)
|
||||
return;
|
||||
|
||||
// save the new one
|
||||
let nsLoginInfo = new Components.Constructor(
|
||||
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
|
||||
let login = new nsLoginInfo('chrome://sync', null, realm,
|
||||
username, password, "", "");
|
||||
lm.addLogin(login);
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ Utils.lazy(Weave, 'Service', WeaveSvc);
|
|||
* Main entry point into Weave's sync framework
|
||||
*/
|
||||
|
||||
function WeaveSvc() {
|
||||
function WeaveSvc(engines) {
|
||||
this._startupFinished = false;
|
||||
this._initLogs();
|
||||
this._log.info("Weave Sync Service Initializing");
|
||||
|
@ -116,13 +116,19 @@ function WeaveSvc() {
|
|||
ID.setAlias('WeaveID', 'DAV:default');
|
||||
ID.setAlias('WeaveCryptoID', 'Engine:PBE:default');
|
||||
|
||||
// Register built-in engines
|
||||
Engines.register(new BookmarksEngine());
|
||||
Engines.register(new HistoryEngine());
|
||||
Engines.register(new CookieEngine());
|
||||
Engines.register(new PasswordEngine());
|
||||
Engines.register(new FormEngine());
|
||||
Engines.register(new TabEngine());
|
||||
if (typeof engines == "undefined")
|
||||
engines = [
|
||||
new BookmarksEngine(),
|
||||
new HistoryEngine(),
|
||||
new CookieEngine(),
|
||||
new PasswordEngine(),
|
||||
new FormEngine(),
|
||||
new TabEngine()
|
||||
];
|
||||
|
||||
// Register engines
|
||||
for (let i = 0; i < engines.length; i++)
|
||||
Engines.register(engines[i]);
|
||||
|
||||
// Other misc startup
|
||||
Utils.prefs.addObserver("", this, false);
|
||||
|
@ -666,7 +672,6 @@ WeaveSvc.prototype = {
|
|||
"share-bookmarks" will be sent out to any observers who are listening
|
||||
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._shareData,
|
||||
dataType,
|
||||
|
@ -677,6 +682,7 @@ WeaveSvc.prototype = {
|
|||
_shareData: function WeaveSync__shareData(dataType,
|
||||
guid,
|
||||
username) {
|
||||
dump( "in _shareData...\n" );
|
||||
let self = yield;
|
||||
if (!Engines.get(dataType).enabled) {
|
||||
this._log.warn( "Can't share disabled data type: " + dataType );
|
||||
|
|
|
@ -76,16 +76,10 @@ Store.prototype = {
|
|||
|
||||
applyCommands: function Store_applyCommands(commandList) {
|
||||
let self = yield;
|
||||
let 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);
|
||||
Utils.makeTimerForCall(self.cb);
|
||||
yield; // Yield to main loop
|
||||
}
|
||||
var command = commandList[i];
|
||||
|
@ -130,14 +124,6 @@ SnapshotStore.prototype = {
|
|||
this._filename = value + ".json";
|
||||
},
|
||||
|
||||
__dirSvc: null,
|
||||
get _dirSvc() {
|
||||
if (!this.__dirSvc)
|
||||
this.__dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
return this.__dirSvc;
|
||||
},
|
||||
|
||||
// Last synced tree, version, and GUID (to detect if the store has
|
||||
// been completely replaced and invalidate the snapshot)
|
||||
|
||||
|
@ -182,7 +168,7 @@ SnapshotStore.prototype = {
|
|||
oldGUID = command.GUID;
|
||||
|
||||
this._data[newGUID] = this._data[oldGUID];
|
||||
delete this._data[oldGUID]
|
||||
delete this._data[oldGUID];
|
||||
|
||||
for (let GUID in this._data) {
|
||||
if (this._data[GUID].parentGUID == oldGUID)
|
||||
|
@ -199,14 +185,10 @@ SnapshotStore.prototype = {
|
|||
save: function SStore_save() {
|
||||
this._log.info("Saving snapshot to disk");
|
||||
|
||||
let file = this._dirSvc.get("ProfD", Ci.nsIFile);
|
||||
file.QueryInterface(Ci.nsILocalFile);
|
||||
|
||||
file.append("weave");
|
||||
file.append("snapshots");
|
||||
file.append(this.filename);
|
||||
if (!file.exists())
|
||||
file.create(file.NORMAL_FILE_TYPE, PERMS_FILE);
|
||||
let file = Utils.getProfileFile(
|
||||
{path: "weave/snapshots/" + this.filename,
|
||||
autoCreate: true}
|
||||
);
|
||||
|
||||
let out = {version: this.version,
|
||||
GUID: this.GUID,
|
||||
|
@ -219,11 +201,7 @@ SnapshotStore.prototype = {
|
|||
},
|
||||
|
||||
load: function SStore_load() {
|
||||
let file = this._dirSvc.get("ProfD", Ci.nsIFile);
|
||||
file.append("weave");
|
||||
file.append("snapshots");
|
||||
file.append(this.filename);
|
||||
|
||||
let file = Utils.getProfileFile("weave/snapshots/" + this.filename);
|
||||
if (!file.exists())
|
||||
return;
|
||||
|
||||
|
|
|
@ -50,6 +50,83 @@ Cu.import("resource://weave/log4moz.js");
|
|||
*/
|
||||
|
||||
let Utils = {
|
||||
// Returns a nsILocalFile representing a file relative to the
|
||||
// current user's profile directory. If the argument is a string,
|
||||
// it should be a string with unix-style slashes for directory names
|
||||
// (these slashes are automatically converted to platform-specific
|
||||
// path separators).
|
||||
//
|
||||
// Alternatively, if the argument is an object, it should contain
|
||||
// the following attributes:
|
||||
//
|
||||
// path: the path to the file, relative to the current user's
|
||||
// profile dir.
|
||||
//
|
||||
// autoCreate: whether or not the file should be created if it
|
||||
// doesn't already exist.
|
||||
getProfileFile: function getProfileFile(arg) {
|
||||
if (typeof arg == "string")
|
||||
arg = {path: arg};
|
||||
|
||||
let pathParts = arg.path.split("/");
|
||||
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
let file = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
file.QueryInterface(Ci.nsILocalFile);
|
||||
for (let i = 0; i < pathParts.length; i++)
|
||||
file.append(pathParts[i]);
|
||||
if (arg.autoCreate && !file.exists())
|
||||
file.create(file.NORMAL_FILE_TYPE, PERMS_FILE);
|
||||
return file;
|
||||
},
|
||||
|
||||
getLoginManager: function getLoginManager() {
|
||||
return Cc["@mozilla.org/login-manager;1"].
|
||||
getService(Ci.nsILoginManager);
|
||||
},
|
||||
|
||||
makeNewLoginInfo: function getNewLoginInfo() {
|
||||
return new Components.Constructor(
|
||||
"@mozilla.org/login-manager/loginInfo;1",
|
||||
Ci.nsILoginInfo,
|
||||
"init"
|
||||
);
|
||||
},
|
||||
|
||||
findPassword: function findPassword(realm, username) {
|
||||
// fixme: make a request and get the realm ?
|
||||
let password;
|
||||
let lm = Cc["@mozilla.org/login-manager;1"]
|
||||
.getService(Ci.nsILoginManager);
|
||||
let logins = lm.findLogins({}, 'chrome://sync', null, realm);
|
||||
|
||||
for (let i = 0; i < logins.length; i++) {
|
||||
if (logins[i].username == username) {
|
||||
password = logins[i].password;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return password;
|
||||
},
|
||||
|
||||
setPassword: function setPassword(realm, username, password) {
|
||||
// cleanup any existing passwords
|
||||
let lm = Cc["@mozilla.org/login-manager;1"]
|
||||
.getService(Ci.nsILoginManager);
|
||||
let logins = lm.findLogins({}, 'chrome://sync', null, realm);
|
||||
for (let i = 0; i < logins.length; i++)
|
||||
lm.removeLogin(logins[i]);
|
||||
|
||||
if (!password)
|
||||
return;
|
||||
|
||||
// save the new one
|
||||
let nsLoginInfo = new Components.Constructor(
|
||||
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
|
||||
let login = new nsLoginInfo('chrome://sync', null, realm,
|
||||
username, password, "", "");
|
||||
lm.addLogin(login);
|
||||
},
|
||||
|
||||
// lazy load objects from a constructor on first access. It will
|
||||
// work with the global object ('this' in the global context).
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
const EXPORTED_SYMBOLS = [ "PlainAuthenticator", "Md5DigestAuthenticator" ];
|
||||
|
||||
function LOG(aMsg) {
|
||||
dump("Weave::AuthenticationLayer: " + aMsg + "\n");
|
||||
}
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://weave/log4moz.js");
|
||||
|
||||
if (typeof(atob) == 'undefined') {
|
||||
// This code was written by Tyler Akins and has been placed in the
|
||||
|
@ -367,7 +369,6 @@ PlainAuthenticator.prototype = {
|
|||
this._jid = jidNodes[0].firstChild.nodeValue;
|
||||
// TODO: Does the client need to do anything special with its new
|
||||
// "client@host.com/resourceID" full JID?
|
||||
LOG( "JID set to " + this._jid );
|
||||
|
||||
// If we still need to do session, then we're not done yet:
|
||||
if ( this._needSession ) {
|
||||
|
|
|
@ -2,10 +2,9 @@ const EXPORTED_SYMBOLS = ['HTTPPollingTransport'];
|
|||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
|
||||
function LOG(aMsg) {
|
||||
dump("Weave::Transport-HTTP-Poll: " + aMsg + "\n");
|
||||
}
|
||||
Cu.import("resource://weave/log4moz.js");
|
||||
|
||||
/*
|
||||
The interface that should be implemented by any Transport object:
|
||||
|
@ -163,7 +162,8 @@ function HTTPPollingTransport( serverUrl, useKeys, interval ) {
|
|||
}
|
||||
HTTPPollingTransport.prototype = {
|
||||
_init: function( serverUrl, useKeys, interval ) {
|
||||
LOG("Initializing transport: serverUrl=" + serverUrl + ", useKeys=" + useKeys + ", interval=" + interval);
|
||||
this._log = Log4Moz.Service.getLogger("Service.XmppTransportLayer");
|
||||
this._log.info("Initializing transport: serverUrl=" + serverUrl + ", useKeys=" + useKeys + ", interval=" + interval);
|
||||
this._serverUrl = serverUrl
|
||||
this._n = 0;
|
||||
this._key = this._makeSeed();
|
||||
|
@ -223,7 +223,6 @@ HTTPPollingTransport.prototype = {
|
|||
|
||||
_setIdFromCookie: function( self, cookie ) {
|
||||
// parse connection ID out of the cookie:
|
||||
// dump( "Cookie is " + cookie + "\n" );
|
||||
var cookieSegments = cookie.split( ";" );
|
||||
cookieSegments = cookieSegments[0].split( "=" );
|
||||
var newConnectionId = cookieSegments[1];
|
||||
|
@ -242,13 +241,13 @@ HTTPPollingTransport.prototype = {
|
|||
break;
|
||||
default :
|
||||
self._connectionId = cookieSegments[1];
|
||||
// dump( "Connection ID set to " + self._connectionId + "\n" );
|
||||
this._log.debug("Connection ID set to " + self._connectionId);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_onError: function( errorText ) {
|
||||
dump( "Transport error: " + errorText + "\n" );
|
||||
this._log.error( errorText );
|
||||
if ( this._callbackObject != null ) {
|
||||
this._callbackObject.onTransportError( errorText );
|
||||
}
|
||||
|
@ -277,7 +276,7 @@ HTTPPollingTransport.prototype = {
|
|||
if ( request.status == 200) {
|
||||
// 200 means success.
|
||||
|
||||
LOG("Server says: " + request.responseText);
|
||||
self._log.debug("Server says: " + request.responseText);
|
||||
// Look for a set-cookie header:
|
||||
var latestCookie = request.getResponseHeader( "Set-Cookie" );
|
||||
if ( latestCookie.length > 0 ) {
|
||||
|
@ -289,7 +288,7 @@ HTTPPollingTransport.prototype = {
|
|||
callbackObj.onIncomingData( request.responseText );
|
||||
}
|
||||
} else {
|
||||
LOG( "Error! Got HTTP status code " + request.status );
|
||||
self._log.error( "Got HTTP status code " + request.status );
|
||||
if ( request.status == 0 ) {
|
||||
/* Sometimes the server gives us HTTP status code 0 in response
|
||||
to an attempt to POST. I'm not sure why this happens, but
|
||||
|
@ -320,11 +319,11 @@ HTTPPollingTransport.prototype = {
|
|||
request.setRequestHeader( "Content-length", contents.length );
|
||||
request.setRequestHeader( "Connection", "close" );
|
||||
request.onreadystatechange = _processReqChange;
|
||||
LOG("Sending: " + contents);
|
||||
this._log.debug("Sending: " + contents);
|
||||
request.send( contents );
|
||||
} catch(ex) {
|
||||
this._onError("Unable to send message to server: " + this._serverUrl);
|
||||
LOG("Connection failure: " + ex);
|
||||
this._log.error("Connection failure: " + ex);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -371,10 +370,10 @@ HTTPPollingTransport.prototype = {
|
|||
|
||||
testKeys: function () {
|
||||
this._key = "foo";
|
||||
LOG(this._key);
|
||||
this._log.debug(this._key);
|
||||
for ( var x = 1; x < 7; x++ ) {
|
||||
this._advanceKey();
|
||||
LOG(this._key);
|
||||
this._log.debug(this._key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,10 +14,7 @@ var Cc = Components.classes;
|
|||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
|
||||
function LOG(aMsg) {
|
||||
dump("Weave::XMPPClient: " + aMsg + "\n");
|
||||
}
|
||||
|
||||
Cu.import("resource://weave/log4moz.js");
|
||||
Cu.import("resource://weave/xmpp/transportLayer.js");
|
||||
Cu.import("resource://weave/xmpp/authenticationLayer.js");
|
||||
|
||||
|
@ -38,6 +35,7 @@ XmppClient.prototype = {
|
|||
IQ_ERROR: -1,
|
||||
|
||||
_init: function( clientName, realm, clientPassword, transport, authenticator ) {
|
||||
this._log = Log4Moz.Service.getLogger("Service.XmppClient");
|
||||
this._myName = clientName;
|
||||
this._realm = realm;
|
||||
this._fullName = clientName + "@" + realm;
|
||||
|
@ -47,7 +45,7 @@ XmppClient.prototype = {
|
|||
this._streamOpen = false;
|
||||
this._transportLayer = transport;
|
||||
this._authenticationLayer = authenticator;
|
||||
LOG("initialized auth with clientName=" + clientName + ", realm=" + realm + ", pw=" + clientPassword);
|
||||
this._log.debug("initialized auth with clientName=" + clientName + ", realm=" + realm + ", pw=" + clientPassword);
|
||||
this._authenticationLayer.initialize( clientName, realm, clientPassword );
|
||||
this._messageHandlers = [];
|
||||
this._iqResponders = [];
|
||||
|
@ -71,9 +69,8 @@ XmppClient.prototype = {
|
|||
},
|
||||
|
||||
parseError: function( streamErrorNode ) {
|
||||
LOG( "Uh-oh, there was an error!" );
|
||||
var error = streamErrorNode.childNodes[0];
|
||||
LOG( "Name: " + error.nodeName + " Value: " + error.nodeValue );
|
||||
this._log.error( "Name: " + error.nodeName + " Value: " + error.nodeValue );
|
||||
this._error = error.nodeName;
|
||||
this.disconnect();
|
||||
/* Note there can be an optional <text>bla bla </text> node inside
|
||||
|
@ -89,14 +86,14 @@ XmppClient.prototype = {
|
|||
},
|
||||
|
||||
setError: function( errorText ) {
|
||||
LOG( "Error: " + errorText );
|
||||
this._log.error( errorText );
|
||||
this._error = errorText;
|
||||
this._connectionStatus = this.FAILED;
|
||||
this._finishConnectionAttempt();
|
||||
},
|
||||
|
||||
onIncomingData: function( messageText ) {
|
||||
LOG("onIncomingData(): rcvd: " + messageText);
|
||||
this._log.debug("onIncomingData(): rcvd: " + messageText);
|
||||
var responseDOM = this._parser.parseFromString( messageText, "text/xml" );
|
||||
|
||||
// Handle server disconnection
|
||||
|
@ -171,7 +168,7 @@ XmppClient.prototype = {
|
|||
if (presences.length > 0 ) {
|
||||
var from = presences[0].getAttribute( "from" );
|
||||
if ( from != undefined ) {
|
||||
LOG( "I see that " + from + " is online." );
|
||||
this._log.debug( "I see that " + from + " is online." );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,14 +197,14 @@ XmppClient.prototype = {
|
|||
},
|
||||
|
||||
processIncomingMessage: function( messageElem ) {
|
||||
LOG( "in processIncomingMessage: messageElem is a " + messageElem );
|
||||
this._log.debug("processIncomingMsg: messageElem is a " + messageElem);
|
||||
var from = messageElem.getAttribute( "from" );
|
||||
var contentElem = messageElem.firstChild;
|
||||
// Go down till we find the element with nodeType = 3 (TEXT_NODE)
|
||||
while ( contentElem.nodeType != 3 ) {
|
||||
contentElem = contentElem.firstChild;
|
||||
}
|
||||
LOG( "Incoming message to you from " + from + ":" + contentElem.nodeValue );
|
||||
this._log.debug("Incoming msg from " + from + ":" + contentElem.nodeValue);
|
||||
for ( var x in this._messageHandlers ) {
|
||||
// TODO do messages have standard place for metadata?
|
||||
// will want to have handlers that trigger only on certain metadata.
|
||||
|
@ -333,8 +330,7 @@ XmppClient.prototype = {
|
|||
var msgXml = "<message xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='" +
|
||||
fullName + "' to='" + recipient + "' xml:lang='en'><body>" +
|
||||
messageText + "</body></message>";
|
||||
LOG( "Message xml: " );
|
||||
LOG( msgXml );
|
||||
this._log.debug( "Outgoing Message xml: " + msgXml );
|
||||
return msgXml;
|
||||
},
|
||||
|
||||
|
|
|
@ -1,163 +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 Corrected Block TEA.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Chris Veness
|
||||
* Portions created by the Initial Developer are Copyright (C) 2005
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Veness <chrisv@movable-type.co.uk>
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
// Original 'Corrected Block TEA' algorithm David Wheeler & Roger Needham
|
||||
// See http://en.wikipedia.org/wiki/XXTEA
|
||||
//
|
||||
// Javascript version by Chris Veness
|
||||
// http://www.movable-type.co.uk/scripts/tea.html
|
||||
|
||||
const EXPORTED_SYMBOLS = ['encrypt', 'decrypt'];
|
||||
|
||||
function Paused() {
|
||||
}
|
||||
Paused.prototype = {
|
||||
toString: function Paused_toString() {
|
||||
return "[Generator Paused]";
|
||||
}
|
||||
}
|
||||
|
||||
// use (16 chars of) 'password' to encrypt 'plaintext'
|
||||
//
|
||||
// note1:this is a generator so the caller can pause and give control
|
||||
// to the UI thread
|
||||
//
|
||||
// note2: if plaintext or password are passed as string objects, rather
|
||||
// than strings, this function will throw an 'Object doesn't support
|
||||
// this property or method' error
|
||||
|
||||
function encrypt(plaintext, password) {
|
||||
var v = new Array(2), k = new Array(4), s = "", i;
|
||||
|
||||
// use escape() so only have single-byte chars to encode
|
||||
plaintext = escape(plaintext);
|
||||
|
||||
// build key directly from 1st 16 chars of password
|
||||
for (i = 0; i < 4; i++)
|
||||
k[i] = Str4ToLong(password.slice(i * 4, (i + 1) * 4));
|
||||
|
||||
for (i = 0; i < plaintext.length; i += 8) {
|
||||
// encode plaintext into s in 64-bit (8 char) blocks
|
||||
// ... note this is 'electronic codebook' mode
|
||||
v[0] = Str4ToLong(plaintext.slice(i, i + 4));
|
||||
v[1] = Str4ToLong(plaintext.slice(i + 4, i + 8));
|
||||
code(v, k);
|
||||
s += LongToStr4(v[0]) + LongToStr4(v[1]);
|
||||
|
||||
if (i % 512 == 0)
|
||||
yield new Paused();
|
||||
}
|
||||
|
||||
yield escCtrlCh(s);
|
||||
}
|
||||
|
||||
// use (16 chars of) 'password' to decrypt 'ciphertext' with xTEA
|
||||
|
||||
function decrypt(ciphertext, password) {
|
||||
var v = new Array(2), k = new Array(4), s = "", i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
k[i] = Str4ToLong(password.slice(i * 4, (i + 1) * 4));
|
||||
|
||||
ciphertext = unescCtrlCh(ciphertext);
|
||||
for (i = 0; i < ciphertext.length; i += 8) {
|
||||
// decode ciphertext into s in 64-bit (8 char) blocks
|
||||
v[0] = Str4ToLong(ciphertext.slice(i, i + 4));
|
||||
v[1] = Str4ToLong(ciphertext.slice(i + 4, i + 8));
|
||||
decode(v, k);
|
||||
s += LongToStr4(v[0]) + LongToStr4(v[1]);
|
||||
|
||||
if (i % 512 == 0)
|
||||
yield new Paused();
|
||||
}
|
||||
|
||||
// strip trailing null chars resulting from filling 4-char blocks:
|
||||
s = s.replace(/\0+$/, '');
|
||||
|
||||
yield unescape(s);
|
||||
}
|
||||
|
||||
|
||||
function code(v, k) {
|
||||
// Extended TEA: this is the 1997 revised version of Needham & Wheeler's algorithm
|
||||
// params: v[2] 64-bit value block; k[4] 128-bit key
|
||||
var y = v[0], z = v[1];
|
||||
var delta = 0x9E3779B9, limit = delta*32, sum = 0;
|
||||
|
||||
while (sum != limit) {
|
||||
y += (z<<4 ^ z>>>5)+z ^ sum+k[sum & 3];
|
||||
sum += delta;
|
||||
z += (y<<4 ^ y>>>5)+y ^ sum+k[sum>>>11 & 3];
|
||||
// note: unsigned right-shift '>>>' is used in place of original '>>', due to lack
|
||||
// of 'unsigned' type declaration in JavaScript (thanks to Karsten Kraus for this)
|
||||
}
|
||||
v[0] = y; v[1] = z;
|
||||
}
|
||||
|
||||
function decode(v, k) {
|
||||
var y = v[0], z = v[1];
|
||||
var delta = 0x9E3779B9, sum = delta*32;
|
||||
|
||||
while (sum != 0) {
|
||||
z -= (y<<4 ^ y>>>5)+y ^ sum+k[sum>>>11 & 3];
|
||||
sum -= delta;
|
||||
y -= (z<<4 ^ z>>>5)+z ^ sum+k[sum & 3];
|
||||
}
|
||||
v[0] = y; v[1] = z;
|
||||
}
|
||||
|
||||
|
||||
// supporting functions
|
||||
|
||||
function Str4ToLong(s) { // convert 4 chars of s to a numeric long
|
||||
var v = 0;
|
||||
for (var i=0; i<4; i++) v |= s.charCodeAt(i) << i*8;
|
||||
return isNaN(v) ? 0 : v;
|
||||
}
|
||||
|
||||
function LongToStr4(v) { // convert a numeric long to 4 char string
|
||||
var s = String.fromCharCode(v & 0xFF, v>>8 & 0xFF, v>>16 & 0xFF, v>>24 & 0xFF);
|
||||
return s;
|
||||
}
|
||||
|
||||
function escCtrlCh(str) { // escape control chars which might cause problems with encrypted texts
|
||||
return str.replace(/[\0\t\n\v\f\r\xa0'"!]/g, function(c) { return '!' + c.charCodeAt(0) + '!'; });
|
||||
}
|
||||
|
||||
function unescCtrlCh(str) { // unescape potentially problematic nulls and control characters
|
||||
return str.replace(/!\d\d?\d?!/g, function(c) { return String.fromCharCode(c.slice(1,-1)); });
|
||||
}
|
|
@ -34,7 +34,7 @@ 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");
|
||||
"https://sm-labs01.mozilla.org:81/xmpp");
|
||||
pref("extensions.weave.xmpp.server.realm", "sm-labs01.mozilla.org");
|
||||
pref("extensions.weave.xmpp.client.name", "");
|
||||
pref("extensions.weave.xmpp.client.password", "");
|
||||
|
|
|
@ -114,3 +114,88 @@ function makeAsyncTestRunner(generator) {
|
|||
|
||||
return run_test;
|
||||
}
|
||||
|
||||
function FakePrefService(contents) {
|
||||
Cu.import("resource://weave/util.js");
|
||||
this.fakeContents = contents;
|
||||
Utils.__prefs = this;
|
||||
}
|
||||
|
||||
FakePrefService.prototype = {
|
||||
_getPref: function fake__getPref(pref) {
|
||||
Log4Moz.Service.rootLogger.trace("Getting pref: " + pref);
|
||||
return this.fakeContents[pref];
|
||||
},
|
||||
getCharPref: function fake_getCharPref(pref) {
|
||||
return this._getPref(pref);
|
||||
},
|
||||
getBoolPref: function fake_getBoolPref(pref) {
|
||||
return this._getPref(pref);
|
||||
},
|
||||
getIntPref: function fake_getIntPref(pref) {
|
||||
return this._getPref(pref);
|
||||
},
|
||||
addObserver: function fake_addObserver() {}
|
||||
};
|
||||
|
||||
function makeFakeAsyncFunc(retval) {
|
||||
Cu.import("resource://weave/async.js");
|
||||
Function.prototype.async = Async.sugar;
|
||||
|
||||
function fakeAsyncFunc() {
|
||||
let self = yield;
|
||||
|
||||
Utils.makeTimerForCall(self.cb);
|
||||
yield;
|
||||
|
||||
self.done(retval);
|
||||
}
|
||||
|
||||
return fakeAsyncFunc;
|
||||
}
|
||||
|
||||
function FakeDAVService(contents) {
|
||||
Cu.import("resource://weave/dav.js");
|
||||
|
||||
this.fakeContents = contents;
|
||||
DAV.__proto__ = this;
|
||||
this.checkLogin = makeFakeAsyncFunc(true);
|
||||
}
|
||||
|
||||
FakeDAVService.prototype = {
|
||||
PUT: function fake_PUT(path, data, onComplete) {
|
||||
Log4Moz.Service.rootLogger.info("Putting " + path);
|
||||
this.fakeContents[path] = data;
|
||||
makeFakeAsyncFunc({status: 200}).async(this, onComplete);
|
||||
},
|
||||
|
||||
GET: function fake_GET(path, onComplete) {
|
||||
Log4Moz.Service.rootLogger.info("Retrieving " + path);
|
||||
var result = {status: 404};
|
||||
if (path in this.fakeContents)
|
||||
result = {status: 200, responseText: this.fakeContents[path]};
|
||||
|
||||
return makeFakeAsyncFunc(result).async(this, onComplete);
|
||||
},
|
||||
|
||||
MKCOL: function fake_MKCOL(path, onComplete) {
|
||||
Log4Moz.Service.rootLogger.info("Creating dir " + path);
|
||||
makeFakeAsyncFunc(true).async(this, onComplete);
|
||||
}
|
||||
};
|
||||
|
||||
function FakePasswordService(contents) {
|
||||
Cu.import("resource://weave/util.js");
|
||||
|
||||
this.fakeContents = contents;
|
||||
let self = this;
|
||||
|
||||
Utils.findPassword = function fake_findPassword(realm, username) {
|
||||
Log4Moz.Service.rootLogger.trace("Password requested for " +
|
||||
realm + ":" + username);
|
||||
if (realm in self.fakeContents && username in self.fakeContents[realm])
|
||||
return self.fakeContents[realm][username];
|
||||
else
|
||||
return null;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -40,7 +40,11 @@ function run_test() {
|
|||
|
||||
do_check_eq(timesYielded, 2);
|
||||
|
||||
do_check_eq(Async.outstandingGenerators, 1);
|
||||
|
||||
do_check_true(fts.processCallback());
|
||||
|
||||
do_check_false(fts.processCallback());
|
||||
|
||||
do_check_eq(Async.outstandingGenerators, 0);
|
||||
}
|
||||
|
|
|
@ -47,4 +47,5 @@ function run_test() {
|
|||
runTestGenerator.async({});
|
||||
for (var i = 0; fts.processCallback(); i++) {}
|
||||
do_check_eq(i, 4);
|
||||
do_check_eq(Async.outstandingGenerators, 0);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,23 @@
|
|||
Cu.import("resource://weave/util.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
Cu.import("resource://weave/identity.js");
|
||||
|
||||
let __fakePasswords = {
|
||||
'Mozilla Services Password': {foo: "bar"},
|
||||
'Mozilla Services Encryption Passphrase': {foo: "passphrase"}
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
var fpasses = new FakePasswordService(__fakePasswords);
|
||||
var fprefs = new FakePrefService({"encryption" : "none"});
|
||||
var fds = new FakeDAVService({});
|
||||
var fts = new FakeTimerService();
|
||||
var logStats = initTestLogging();
|
||||
|
||||
ID.set('Engine:PBE:default',
|
||||
new Identity('Mozilla Services Encryption Passphrase', 'foo'));
|
||||
|
||||
// The JS module we're testing, with all members exposed.
|
||||
var passwords = loadInSandbox("resource://weave/engines/passwords.js");
|
||||
|
||||
|
@ -13,10 +32,22 @@ function run_test() {
|
|||
passwordField: "test_password"
|
||||
};
|
||||
|
||||
// Fake nsILoginManager object.
|
||||
var fakeLoginManager = {
|
||||
getAllLogins: function() { return [fakeUser]; }
|
||||
Utils.getLoginManager = function fake_getLoginManager() {
|
||||
// Return a fake nsILoginManager object.
|
||||
return {getAllLogins: function() { return [fakeUser]; }};
|
||||
};
|
||||
|
||||
Utils.getProfileFile = function fake_getProfileFile(arg) {
|
||||
return {exists: function() {return false;}};
|
||||
};
|
||||
|
||||
Utils.open = function fake_open(file, mode) {
|
||||
let fakeStream = {
|
||||
writeString: function(data) {Log4Moz.Service.rootLogger.debug(data);},
|
||||
close: function() {}
|
||||
};
|
||||
return [fakeStream];
|
||||
};
|
||||
|
||||
// Ensure that _hashLoginInfo() works.
|
||||
var fakeUserHash = passwords._hashLoginInfo(fakeUser);
|
||||
|
@ -25,7 +56,14 @@ function run_test() {
|
|||
|
||||
// Ensure that PasswordSyncCore._itemExists() works.
|
||||
var psc = new passwords.PasswordSyncCore();
|
||||
psc.__loginManager = fakeLoginManager;
|
||||
do_check_false(psc._itemExists("invalid guid"));
|
||||
do_check_true(psc._itemExists(fakeUserHash));
|
||||
|
||||
// Make sure the engine can sync.
|
||||
var engine = new passwords.PasswordEngine();
|
||||
engine.sync();
|
||||
|
||||
while (fts.processCallback()) {}
|
||||
do_check_eq(logStats.errorsLogged, 0);
|
||||
do_check_eq(Async.outstandingGenerators, 0);
|
||||
}
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
Cu.import("resource://weave/log4moz.js");
|
||||
Cu.import("resource://weave/wrap.js");
|
||||
Cu.import("resource://weave/async.js");
|
||||
Cu.import("resource://weave/util.js");
|
||||
Cu.import("resource://weave/dav.js");
|
||||
Cu.import("resource://weave/crypto.js");
|
||||
Cu.import("resource://weave/identity.js");
|
||||
|
||||
Function.prototype.async = Async.sugar;
|
||||
let __fakePrefs = {
|
||||
"log.logger.async" : "Debug",
|
||||
"username" : "foo",
|
||||
"serverURL" : "https://example.com/",
|
||||
"encryption" : "aes-256-cbc",
|
||||
"enabled" : true,
|
||||
"schedule" : 0
|
||||
};
|
||||
|
||||
function makeFakeAsyncFunc(retval) {
|
||||
function fakeAsyncFunc() {
|
||||
let self = yield;
|
||||
let __fakeDAVContents = {
|
||||
"meta/version" : "2",
|
||||
"private/privkey" : "fake private key"
|
||||
};
|
||||
|
||||
Utils.makeTimerForCall(self.cb);
|
||||
yield;
|
||||
|
||||
self.done(retval);
|
||||
}
|
||||
|
||||
return fakeAsyncFunc;
|
||||
}
|
||||
let __fakePasswords = {
|
||||
'Mozilla Services Password': {foo: "bar"},
|
||||
'Mozilla Services Encryption Passphrase': {foo: "passphrase"}
|
||||
};
|
||||
|
||||
Crypto.__proto__ = {
|
||||
RSAkeydecrypt: function fake_RSAkeydecrypt(identity) {
|
||||
|
@ -33,53 +33,24 @@ Crypto.__proto__ = {
|
|||
}
|
||||
};
|
||||
|
||||
DAV.__proto__ = {
|
||||
checkLogin: makeFakeAsyncFunc(true),
|
||||
|
||||
__contents: {"meta/version" : "2",
|
||||
"private/privkey" : "fake private key"},
|
||||
|
||||
GET: function fake_GET(path, onComplete) {
|
||||
Log4Moz.Service.rootLogger.info("Retrieving " + path);
|
||||
var result = {status: 404};
|
||||
if (path in this.__contents)
|
||||
result = {status: 200, responseText: this.__contents[path]};
|
||||
|
||||
return makeFakeAsyncFunc(result).async(this, onComplete);
|
||||
}
|
||||
};
|
||||
|
||||
function FakeID(realm, username, password) {
|
||||
this.realm = realm;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.setTempPassword = function FID_setTempPassword(value) {
|
||||
if (typeof value != "undefined")
|
||||
this.password = value;
|
||||
};
|
||||
}
|
||||
|
||||
ID.__proto__ = {
|
||||
__contents: {WeaveID: new FakeID("", "foo", "bar"),
|
||||
WeaveCryptoID: new FakeID("", "", "passphrase")},
|
||||
|
||||
get: function fake_ID_get(name) {
|
||||
return this.__contents[name];
|
||||
}
|
||||
};
|
||||
|
||||
let Service = loadInSandbox("resource://weave/service.js");
|
||||
|
||||
function TestService() {
|
||||
this._startupFinished = false;
|
||||
this._log = Log4Moz.Service.getLogger("Service.Main");
|
||||
this.__superclassConstructor = Service.WeaveSvc;
|
||||
this.__superclassConstructor([]);
|
||||
}
|
||||
|
||||
TestService.prototype = {
|
||||
_initLogs: function TS__initLogs() {
|
||||
this._log = Log4Moz.Service.getLogger("Service.Main");
|
||||
}
|
||||
};
|
||||
TestService.prototype.__proto__ = Service.WeaveSvc.prototype;
|
||||
|
||||
function test_login_works() {
|
||||
var fds = new FakeDAVService(__fakeDAVContents);
|
||||
var fprefs = new FakePrefService(__fakePrefs);
|
||||
var fpasses = new FakePasswordService(__fakePasswords);
|
||||
var fts = new FakeTimerService();
|
||||
var logStats = initTestLogging();
|
||||
var testService = new TestService();
|
||||
|
@ -97,4 +68,5 @@ function test_login_works() {
|
|||
do_check_true(finished);
|
||||
do_check_true(successful);
|
||||
do_check_eq(logStats.errorsLogged, 0);
|
||||
do_check_eq(Async.outstandingGenerators, 0);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
*** test pending
|
||||
Running test: test_login_works
|
||||
Service.Main INFO Weave Sync Service Initializing
|
||||
Service.Main DEBUG Logging in
|
||||
Service.Main INFO Using server URL: https://example.com/user/foo
|
||||
root INFO Retrieving meta/version
|
||||
root INFO Retrieving private/privkey
|
||||
Service.Main INFO Weave scheduler disabled
|
||||
1 of 1 tests passed.
|
||||
*** test finished
|
||||
*** exiting
|
||||
*** PASS ***
|
Загрузка…
Ссылка в новой задаче