Merge branch 'serviceworker' into staging

pull/1/head
yflory 7 years ago
commit 4896fe81fe

@ -89,7 +89,7 @@ define([
}; };
/* Used to accept friend requests within apps other than /contacts/ */ /* Used to accept friend requests within apps other than /contacts/ */
Msg.addDirectMessageHandler = function (cfg) { Msg.addDirectMessageHandler = function (cfg, href) {
var network = cfg.network; var network = cfg.network;
var proxy = cfg.proxy; var proxy = cfg.proxy;
if (!network) { return void console.error('Network not ready'); } if (!network) { return void console.error('Network not ready'); }
@ -97,13 +97,12 @@ define([
var msg; var msg;
if (sender === network.historyKeeper) { return; } if (sender === network.historyKeeper) { return; }
try { try {
var parsed = Hash.parsePadUrl(window.location.href); var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets(parsed.type, parsed.hash);
if (!parsed.hashData) { return; } if (!parsed.hashData) { return; }
var chan = Hash.hrefToHexChannelId(window.location.href); var chan = secret.channel;
// Decrypt // Decrypt
var keyStr = parsed.hashData.key; var key = secret.keys ? secret.keys.cryptKey : Hash.decodeBase64(secret.key);
var cryptor = Crypto.createEditCryptor(keyStr);
var key = cryptor.cryptKey;
var decryptMsg; var decryptMsg;
try { try {
decryptMsg = Crypto.decrypt(message, key); decryptMsg = Crypto.decrypt(message, key);
@ -197,15 +196,14 @@ define([
var network = cfg.network; var network = cfg.network;
var netfluxId = data.netfluxId; var netfluxId = data.netfluxId;
var parsed = Hash.parsePadUrl(data.href); var parsed = Hash.parsePadUrl(data.href);
var secret = Hash.getSecrets(parsed.type, parsed.hash);
if (!parsed.hashData) { return; } if (!parsed.hashData) { return; }
// Message // Message
var chan = Hash.hrefToHexChannelId(data.href); var chan = secret.channel;
var myData = createData(cfg.proxy); var myData = createData(cfg.proxy);
var msg = ["FRIEND_REQ", chan, myData]; var msg = ["FRIEND_REQ", chan, myData];
// Encryption // Encryption
var keyStr = parsed.hashData.key; var key = secret.keys ? secret.keys.cryptKey : Hash.decodeBase64(secret.key);
var cryptor = Crypto.createEditCryptor(keyStr);
var key = cryptor.cryptKey;
var msgStr = Crypto.encrypt(JSON.stringify(msg), key); var msgStr = Crypto.encrypt(JSON.stringify(msg), key);
// Send encrypted message // Send encrypted message
if (pendingRequests.indexOf(netfluxId) === -1) { if (pendingRequests.indexOf(netfluxId) === -1) {

@ -7,25 +7,27 @@ define([
'/common/common-constants.js', '/common/common-constants.js',
'/common/common-feedback.js', '/common/common-feedback.js',
'/common/outer/local-store.js', '/common/outer/local-store.js',
'/common/outer/store-rpc.js', '/common/outer/worker-channel.js',
'/customize/application_config.js', '/customize/application_config.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
], function (Config, Messages, Util, Hash, ], function (Config, Messages, Util, Hash,
Messaging, Constants, Feedback, LocalStore, AStore, Messaging, Constants, Feedback, LocalStore, Channel,
AppConfig, Nthen) { AppConfig, Nthen) {
/* This file exposes functionality which is specific to Cryptpad, but not to /* This file exposes functionality which is specific to Cryptpad, but not to
any particular pad type. This includes functions for committing metadata any particular pad type. This includes functions for committing metadata
about pads to your local storage for future use and improved usability. about pads to your local storage for future use and improved usability.
Additionally, there is some basic functionality for import/export. Additionally, there is some basic functionality for import/export.
*/ */
var postMessage = function (cmd, data, cb) { var urlArgs = Util.find(Config, ['requireConf', 'urlArgs']) || '';
setTimeout(function () {
var postMessage = function (/*cmd, data, cb*/) {
/*setTimeout(function () {
AStore.query(cmd, data, cb); AStore.query(cmd, data, cb);
}); });*/
console.error('NOT_READY');
}; };
var tryParsing = function (x) { var tryParsing = function (x) {
try { return JSON.parse(x); } try { return JSON.parse(x); }
@ -531,6 +533,11 @@ define([
var messaging = common.messaging = {}; var messaging = common.messaging = {};
messaging.onFriendRequest = Util.mkEvent(); messaging.onFriendRequest = Util.mkEvent();
messaging.onFriendComplete = Util.mkEvent(); messaging.onFriendComplete = Util.mkEvent();
messaging.addHandlers = function (href) {
postMessage("ADD_DIRECT_MESSAGE_HANDLERS", {
href: href
});
};
// Messenger // Messenger
var messenger = common.messenger = {}; var messenger = common.messenger = {};
@ -570,8 +577,8 @@ define([
// Pad RPC // Pad RPC
var pad = common.padRpc = {}; var pad = common.padRpc = {};
pad.joinPad = function (data, cb) { pad.joinPad = function (data) {
postMessage("JOIN_PAD", data, cb); postMessage("JOIN_PAD", data);
}; };
pad.sendPadMsg = function (data, cb) { pad.sendPadMsg = function (data, cb) {
postMessage("SEND_PAD_MSG", data, cb); postMessage("SEND_PAD_MSG", data, cb);
@ -581,6 +588,7 @@ define([
pad.onJoinEvent = Util.mkEvent(); pad.onJoinEvent = Util.mkEvent();
pad.onLeaveEvent = Util.mkEvent(); pad.onLeaveEvent = Util.mkEvent();
pad.onDisconnectEvent = Util.mkEvent(); pad.onDisconnectEvent = Util.mkEvent();
pad.onConnectEvent = Util.mkEvent();
pad.onErrorEvent = Util.mkEvent(); pad.onErrorEvent = Util.mkEvent();
// Loading events // Loading events
@ -669,41 +677,25 @@ define([
window.location.href = '/login/'; window.location.href = '/login/';
}; };
common.startAccountDeletion = function (cb) { common.startAccountDeletion = function (data, cb) {
// Logout other tabs // Logout other tabs
LocalStore.logout(null, true); LocalStore.logout(null, true);
cb(); cb();
}; };
var onMessage = function (cmd, data, cb) { var queries = {
cb = cb || function () {}; REQUEST_LOGIN: requestLogin,
switch (cmd) { UPDATE_METADATA: common.changeMetadata,
case 'REQUEST_LOGIN': { UPDATE_TOKEN: function (data) {
requestLogin();
break;
}
case 'UPDATE_METADATA': {
common.changeMetadata();
break;
}
case 'UPDATE_TOKEN': {
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey)); var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
if (localToken !== data.token) { requestLogin(); } if (localToken !== data.token) { requestLogin(); }
break; },
} // Messaging
case 'Q_FRIEND_REQUEST': { Q_FRIEND_REQUEST: common.messaging.onFriendRequest.fire,
common.messaging.onFriendRequest.fire(data, cb); EV_FIREND_COMPLETE: common.messaging.onFriendComplete.fire,
break;
}
case 'EV_FRIEND_COMPLETE': {
common.messaging.onFriendComplete.fire(data);
break;
}
// Network // Network
case 'NETWORK_DISCONNECT': { NETWORK_DISCONNECT: common.onNetworkDisconnect.fire,
common.onNetworkDisconnect.fire(); break; NETWORK_RECONNECT: function (data) {
}
case 'NETWORK_RECONNECT': {
require(['/api/config?' + (+new Date())], function (NewConfig) { require(['/api/config?' + (+new Date())], function (NewConfig) {
var update = updateLocalVersion(NewConfig.requireConf && NewConfig.requireConf.urlArgs); var update = updateLocalVersion(NewConfig.requireConf && NewConfig.requireConf.urlArgs);
if (update) { if (update) {
@ -712,65 +704,30 @@ define([
} }
common.onNetworkReconnect.fire(data); common.onNetworkReconnect.fire(data);
}); });
break; },
}
// Messenger // Messenger
case 'CONTACTS_MESSAGE': { CONTACTS_MESSAGE: common.messenger.onMessageEvent.fire,
common.messenger.onMessageEvent.fire(data); break; CONTACTS_JOIN: common.messenger.onJoinEvent.fire,
} CONTACTS_LEAVE: common.messenger.onLeaveEvent.fire,
case 'CONTACTS_JOIN': { CONTACTS_UPDATE: common.messenger.onUpdateEvent.fire,
common.messenger.onJoinEvent.fire(data); break; CONTACTS_FRIEND: common.messenger.onFriendEvent.fire,
} CONTACTS_UNFRIEND: common.messenger.onUnfriendEvent.fire,
case 'CONTACTS_LEAVE': {
common.messenger.onLeaveEvent.fire(data); break;
}
case 'CONTACTS_UPDATE': {
common.messenger.onUpdateEvent.fire(data); break;
}
case 'CONTACTS_FRIEND': {
common.messenger.onFriendEvent.fire(data); break;
}
case 'CONTACTS_UNFRIEND': {
common.messenger.onUnfriendEvent.fire(data); break;
}
// Pad // Pad
case 'PAD_READY': { PAD_READY: common.padRpc.onReadyEvent.fire,
common.padRpc.onReadyEvent.fire(); break; PAD_MESSAGE: common.padRpc.onMessageEvent.fire,
} PAD_JOIN: common.padRpc.onJoinEvent.fire,
case 'PAD_MESSAGE': { PAD_LEAVE: common.padRpc.onLeaveEvent.fire,
common.padRpc.onMessageEvent.fire(data); break; PAD_DISCONNECT: common.padRpc.onDisconnectEvent.fire,
} PAD_CONNECT: common.padRpc.onConnectEvent.fire,
case 'PAD_JOIN': { PAD_ERROR: common.padRpc.onErrorEvent.fire,
common.padRpc.onJoinEvent.fire(data); break;
}
case 'PAD_LEAVE': {
common.padRpc.onLeaveEvent.fire(data); break;
}
case 'PAD_DISCONNECT': {
common.padRpc.onDisconnectEvent.fire(data); break;
}
case 'PAD_ERROR': {
common.padRpc.onErrorEvent.fire(data); break;
}
// Drive // Drive
case 'DRIVE_LOG': { DRIVE_LOG: common.drive.onLog.fire,
common.drive.onLog.fire(data); break; DRIVE_CHANGE: common.drive.onChange.fire,
} DRIVE_REMOVE: common.drive.onRemove.fire,
case 'DRIVE_CHANGE': {
common.drive.onChange.fire(data); break;
}
case 'DRIVE_REMOVE': {
common.drive.onRemove.fire(data); break;
}
// Account deletion // Account deletion
case 'DELETE_ACCOUNT': { DELETE_ACCOUNT: common.startAccountDeletion,
common.startAccountDeletion(cb); break;
}
// Loading // Loading
case 'LOADING_DRIVE': { LOADING_DRIVE: common.loading.onDriveEvent.fire
common.loading.onDriveEvent.fire(data); break;
}
}
}; };
common.ready = (function () { common.ready = (function () {
@ -828,19 +785,137 @@ define([
} }
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
var cfg = { var cfg = {
query: onMessage, // TODO temporary, will be replaced by a webworker channel init: true,
//query: onMessage, // TODO temporary, will be replaced by a webworker channel
userHash: LocalStore.getUserHash(), userHash: LocalStore.getUserHash(),
anonHash: LocalStore.getFSHash(), anonHash: LocalStore.getFSHash(),
localToken: tryParsing(localStorage.getItem(Constants.tokenKey)), localToken: tryParsing(localStorage.getItem(Constants.tokenKey)),
language: common.getLanguage(), language: common.getLanguage(),
messenger: rdyCfg.messenger, messenger: rdyCfg.messenger, // Boolean
driveEvents: rdyCfg.driveEvents driveEvents: rdyCfg.driveEvents // Boolean
}; };
if (sessionStorage[Constants.newPadPathKey]) { if (sessionStorage[Constants.newPadPathKey]) {
cfg.initialPath = sessionStorage[Constants.newPadPathKey]; cfg.initialPath = sessionStorage[Constants.newPadPathKey];
delete sessionStorage[Constants.newPadPathKey]; delete sessionStorage[Constants.newPadPathKey];
} }
AStore.query("CONNECT", cfg, waitFor(function (data) {
var channelIsReady = waitFor();
var msgEv = Util.mkEvent();
var postMsg, worker;
Nthen(function (waitFor2) {
if (typeof(SharedWorker) !== "undefined") {
worker = new SharedWorker('/common/outer/sharedworker.js?' + urlArgs);
worker.onerror = function (e) {
console.error(e);
};
worker.port.onmessage = function (ev) {
if (ev.data === "SW_READY") {
return;
}
msgEv.fire(ev);
};
postMsg = function (data) {
worker.port.postMessage(data);
};
postMsg('INIT');
window.addEventListener('beforeunload', function () {
postMsg('CLOSE');
});
} else if (false && 'serviceWorker' in navigator) {
var initializing = true;
var stopWaiting = waitFor2(); // Call this function when we're ready
postMsg = function (data) {
if (worker) { return void worker.postMessage(data); }
};
navigator.serviceWorker.register('/common/outer/serviceworker.js?' + urlArgs, {scope: '/'})
.then(function(reg) {
// Add handler for receiving messages from the service worker
navigator.serviceWorker.addEventListener('message', function (ev) {
if (initializing && ev.data === "SW_READY") {
initializing = false;
} else {
msgEv.fire(ev);
}
});
// Initialize the worker
// If it is active (probably running in another tab), just post INIT
if (reg.active) {
worker = reg.active;
postMsg("INIT");
}
// If it was not active, wait for the "activated" state and post INIT
reg.onupdatefound = function () {
if (initializing) {
var w = reg.installing;
var onStateChange = function () {
if (w.state === "activated") {
worker = w;
postMsg("INIT");
w.removeEventListener("statechange", onStateChange);
}
};
w.addEventListener('statechange', onStateChange);
return;
}
// XXX
// New version detected (from another tab): kill?
console.error('New version detected: ABORT?');
};
return void stopWaiting();
}).catch(function(error) {
/**/console.log('Registration failed with ' + error);
});
window.addEventListener('beforeunload', function () {
postMsg('CLOSE');
});
} else if (Worker) {
worker = new Worker('/common/outer/webworker.js?' + urlArgs);
worker.onmessage = function (ev) {
msgEv.fire(ev);
};
postMsg = function (data) {
worker.postMessage(data);
};
} else {
require(['/common/outer/noworker.js'], waitFor2(function (NoWorker) {
NoWorker.onMessage(function (data) {
msgEv.fire({data: data});
});
postMsg = function (d) { setTimeout(function () { NoWorker.query(d); }); };
NoWorker.create();
}));
}
}).nThen(function () {
Channel.create(msgEv, postMsg, function (chan) {
console.log('Outer ready');
Object.keys(queries).forEach(function (q) {
chan.on(q, function (data, cb) {
try {
queries[q](data, cb);
} catch (e) {
console.error("Error in outer when executing query " + q);
console.error(e);
console.log(data);
}
});
});
postMessage = function (cmd, data, cb) {
cb = cb || function () {};
chan.query(cmd, data, function (err, data) {
if (err) { return void cb ({error: err}); }
cb(data);
});
};
console.log('Posting CONNECT');
postMessage('CONNECT', cfg, function (data) {
if (data.error) { throw new Error(data.error); } if (data.error) { throw new Error(data.error); }
if (data.state === 'ALREADY_INIT') { if (data.state === 'ALREADY_INIT') {
data = data.returned; data = data.returned;
@ -864,7 +939,12 @@ define([
initFeedback(data.feedback); initFeedback(data.feedback);
initialized = true; initialized = true;
})); channelIsReady();
});
}, false);
});
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
// Load the new pad when the hash has changed // Load the new pad when the hash has changed
var oldHref = document.location.href; var oldHref = document.location.href;
@ -893,9 +973,12 @@ define([
document.location.reload(); document.location.reload();
} else if (o && !n) { } else if (o && !n) {
LocalStore.logout(); LocalStore.logout();
postMessage("DISCONNECT");
} }
}); });
LocalStore.onLogout(function () {
console.log('onLogout: disconnect');
postMessage("DISCONNECT");
});
if (PINNING_ENABLED && LocalStore.isLoggedIn()) { if (PINNING_ENABLED && LocalStore.isLoggedIn()) {
console.log("logged in. pads will be pinned"); console.log("logged in. pads will be pinned");

@ -23,7 +23,10 @@ define([
Crypto, ChainPad, Listmap, nThen, Saferphore) { Crypto, ChainPad, Listmap, nThen, Saferphore) {
var Store = {}; var Store = {};
var create = function () {
var postMessage = function () {}; var postMessage = function () {};
var broadcast = function () {};
var sendDriveEvent = function () {};
var storeHash; var storeHash;
@ -35,10 +38,10 @@ define([
}; };
Store.get = function (key, cb) { Store.get = function (clientId, key, cb) {
cb(Util.find(store.proxy, key)); cb(Util.find(store.proxy, key));
}; };
Store.set = function (data, cb) { Store.set = function (clientId, data, cb) {
var path = data.key.slice(); var path = data.key.slice();
var key = path.pop(); var key = path.pop();
var obj = Util.find(store.proxy, path); var obj = Util.find(store.proxy, path);
@ -48,6 +51,7 @@ define([
} else { } else {
obj[key] = data.value; obj[key] = data.value;
} }
broadcast([clientId], "UPDATE_METADATA");
onSync(cb); onSync(cb);
}; };
@ -131,7 +135,7 @@ define([
/////////////////////// RPC ////////////////////////////////////// /////////////////////// RPC //////////////////////////////////////
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
Store.pinPads = function (data, cb) { Store.pinPads = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') { if (typeof(cb) !== 'function') {
console.error('expected a callback'); console.error('expected a callback');
@ -143,7 +147,7 @@ define([
}); });
}; };
Store.unpinPads = function (data, cb) { Store.unpinPads = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.unpin(data, function (e, hash) { store.rpc.unpin(data, function (e, hash) {
@ -154,7 +158,7 @@ define([
var account = {}; var account = {};
Store.getPinnedUsage = function (data, cb) { Store.getPinnedUsage = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.getFileListSize(function (err, bytes) { store.rpc.getFileListSize(function (err, bytes) {
@ -166,7 +170,7 @@ define([
}; };
// Update for all users from accounts and return current user limits // Update for all users from accounts and return current user limits
Store.updatePinLimit = function (data, cb) { Store.updatePinLimit = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.updatePinLimits(function (e, limit, plan, note) { store.rpc.updatePinLimits(function (e, limit, plan, note) {
if (e) { return void cb({error: e}); } if (e) { return void cb({error: e}); }
@ -177,7 +181,7 @@ define([
}); });
}; };
// Get current user limits // Get current user limits
Store.getPinLimit = function (data, cb) { Store.getPinLimit = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var ALWAYS_REVALIDATE = true; var ALWAYS_REVALIDATE = true;
@ -195,14 +199,14 @@ define([
cb(account); cb(account);
}; };
Store.clearOwnedChannel = function (data, cb) { Store.clearOwnedChannel = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.clearOwnedChannel(data, function (err) { store.rpc.clearOwnedChannel(data, function (err) {
cb({error:err}); cb({error:err});
}); });
}; };
Store.removeOwnedChannel = function (data, cb) { Store.removeOwnedChannel = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.removeOwnedChannel(data, function (err) { store.rpc.removeOwnedChannel(data, function (err) {
cb({error:err}); cb({error:err});
@ -230,7 +234,7 @@ define([
}); });
}; };
Store.uploadComplete = function (data, cb) { Store.uploadComplete = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
if (data.owned) { if (data.owned) {
// Owned file // Owned file
@ -247,7 +251,7 @@ define([
}); });
}; };
Store.uploadStatus = function (data, cb) { Store.uploadStatus = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.uploadStatus(data.size, function (err, res) { store.rpc.uploadStatus(data.size, function (err, res) {
if (err) { return void cb({error:err}); } if (err) { return void cb({error:err}); }
@ -255,7 +259,7 @@ define([
}); });
}; };
Store.uploadCancel = function (data, cb) { Store.uploadCancel = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.uploadCancel(data.size, function (err, res) { store.rpc.uploadCancel(data.size, function (err, res) {
if (err) { return void cb({error:err}); } if (err) { return void cb({error:err}); }
@ -263,7 +267,7 @@ define([
}); });
}; };
Store.uploadChunk = function (data, cb) { Store.uploadChunk = function (clientId, data, cb) {
store.rpc.send.unauthenticated('UPLOAD', data.chunk, function (e, msg) { store.rpc.send.unauthenticated('UPLOAD', data.chunk, function (e, msg) {
cb({ cb({
error: e, error: e,
@ -272,14 +276,15 @@ define([
}); });
}; };
Store.initRpc = function (data, cb) { Store.initRpc = function (clientId, data, cb) {
if (store.rpc) { return void cb(account); }
require(['/common/pinpad.js'], function (Pinpad) { require(['/common/pinpad.js'], function (Pinpad) {
Pinpad.create(store.network, store.proxy, function (e, call) { Pinpad.create(store.network, store.proxy, function (e, call) {
if (e) { return void cb({error: e}); } if (e) { return void cb({error: e}); }
store.rpc = call; store.rpc = call;
Store.getPinLimit(null, function (obj) { Store.getPinLimit(null, null, function (obj) {
if (obj.error) { console.error(obj.error); } if (obj.error) { console.error(obj.error); }
account.limit = obj.limit; account.limit = obj.limit;
account.plan = obj.plan; account.plan = obj.plan;
@ -302,7 +307,7 @@ define([
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
////////////////// ANON RPC ////////////////////////////////////// ////////////////// ANON RPC //////////////////////////////////////
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
Store.anonRpcMsg = function (data, cb) { Store.anonRpcMsg = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); } if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
store.anon_rpc.send(data.msg, data.data, function (err, res) { store.anon_rpc.send(data.msg, data.data, function (err, res) {
if (err) { return void cb({error: err}); } if (err) { return void cb({error: err}); }
@ -310,7 +315,7 @@ define([
}); });
}; };
Store.getFileSize = function (data, cb) { Store.getFileSize = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); } if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href, data.password); var channelId = Hash.hrefToHexChannelId(data.href, data.password);
@ -324,7 +329,7 @@ define([
}); });
}; };
Store.isNewChannel = function (data, cb) { Store.isNewChannel = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); } if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href, data.password); var channelId = Hash.hrefToHexChannelId(data.href, data.password);
store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) { store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) {
@ -339,7 +344,7 @@ define([
}); });
}; };
Store.getMultipleFileSize = function (data, cb) { Store.getMultipleFileSize = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); } if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
if (!Array.isArray(data.files)) { if (!Array.isArray(data.files)) {
return void cb({error: 'INVALID_FILE_LIST'}); return void cb({error: 'INVALID_FILE_LIST'});
@ -355,7 +360,7 @@ define([
}); });
}; };
Store.getDeletedPads = function (data, cb) { Store.getDeletedPads = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); } if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var list = getCanonicalChannelList(true); var list = getCanonicalChannelList(true);
if (!Array.isArray(list)) { if (!Array.isArray(list)) {
@ -372,7 +377,8 @@ define([
}); });
}; };
Store.initAnonRpc = function (data, cb) { Store.initAnonRpc = function (clientId, data, cb) {
if (store.anon_rpc) { return void cb(); }
require([ require([
'/common/rpc.js', '/common/rpc.js',
], function (Rpc) { ], function (Rpc) {
@ -389,7 +395,7 @@ define([
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// Get the metadata for sframe-common-outer // Get the metadata for sframe-common-outer
Store.getMetadata = function (data, cb) { Store.getMetadata = function (clientId, data, cb) {
var disableThumbnails = Util.find(store.proxy, ['settings', 'general', 'disableThumbnails']); var disableThumbnails = Util.find(store.proxy, ['settings', 'general', 'disableThumbnails']);
var metadata = { var metadata = {
// "user" is shared with everybody via the userlist // "user" is shared with everybody via the userlist
@ -421,7 +427,7 @@ define([
}; };
}; };
Store.addPad = function (data, cb) { Store.addPad = function (clientId, data, cb) {
if (!data.href) { return void cb({error:'NO_HREF'}); } if (!data.href) { return void cb({error:'NO_HREF'}); }
var pad = makePad(data.href, data.title); var pad = makePad(data.href, data.title);
if (data.owners) { pad.owners = data.owners; } if (data.owners) { pad.owners = data.owners; }
@ -432,6 +438,9 @@ define([
if (e) { return void cb({error: "Error while adding a template:"+ e}); } if (e) { return void cb({error: "Error while adding a template:"+ e}); }
var path = data.path || ['root']; var path = data.path || ['root'];
store.userObject.add(id, path); store.userObject.add(id, path);
sendDriveEvent('DRIVE_CHANGE', {
path: ['drive', UserObject.FILES_DATA]
}, clientId);
onSync(cb); onSync(cb);
}); });
}; };
@ -465,7 +474,7 @@ define([
ownedPads.forEach(function (c) { ownedPads.forEach(function (c) {
var w = waitFor(); var w = waitFor();
sem.take(function (give) { sem.take(function (give) {
Store.removeOwnedChannel(c, give(function (obj) { Store.removeOwnedChannel(null, c, give(function (obj) {
if (obj && obj.error) { console.error(obj.error); } if (obj && obj.error) { console.error(obj.error); }
w(); w();
})); }));
@ -473,11 +482,11 @@ define([
}); });
}; };
Store.deleteAccount = function (data, cb) { Store.deleteAccount = function (clientId, data, cb) {
var edPublic = store.proxy.edPublic; var edPublic = store.proxy.edPublic;
// No password for drive // No password for drive
var secret = Hash.getSecrets('drive', storeHash); var secret = Hash.getSecrets('drive', storeHash);
Store.anonRpcMsg({ Store.anonRpcMsg(clientId, {
msg: 'GET_METADATA', msg: 'GET_METADATA',
data: secret.channel data: secret.channel
}, function (data) { }, function (data) {
@ -488,7 +497,7 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER); var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
store.proxy[Constants.tokenKey] = token; store.proxy[Constants.tokenKey] = token;
postMessage("DELETE_ACCOUNT", token, waitFor()); postMessage(clientId, "DELETE_ACCOUNT", token, waitFor());
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
removeOwnedPads(waitFor); removeOwnedPads(waitFor);
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
@ -498,7 +507,7 @@ define([
})); }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
// Delete Drive // Delete Drive
Store.removeOwnedChannel(secret.channel, waitFor()); Store.removeOwnedChannel(clientId, secret.channel, waitFor());
}).nThen(function () { }).nThen(function () {
store.network.disconnect(); store.network.disconnect();
cb({ cb({
@ -537,7 +546,7 @@ define([
* - driveReadme * - driveReadme
* - driveReadmeTitle * - driveReadmeTitle
*/ */
Store.createReadme = function (data, cb) { Store.createReadme = function (clientId, data, cb) {
require(['/common/cryptget.js'], function (Crypt) { require(['/common/cryptget.js'], function (Crypt) {
var hash = Hash.createRandomHash('pad'); var hash = Hash.createRandomHash('pad');
Crypt.put(hash, data.driveReadme, function (e) { Crypt.put(hash, data.driveReadme, function (e) {
@ -551,7 +560,7 @@ define([
channel: channel, channel: channel,
title: data.driveReadmeTitle, title: data.driveReadmeTitle,
}; };
Store.addPad(fileData, cb); Store.addPad(clientId, fileData, cb);
}); });
}); });
}; };
@ -562,7 +571,7 @@ define([
* data * data
* - anonHash * - anonHash
*/ */
Store.migrateAnonDrive = function (data, cb) { Store.migrateAnonDrive = function (clientId, data, cb) {
require(['/common/mergeDrive.js'], function (Merge) { require(['/common/mergeDrive.js'], function (Merge) {
var hash = data.anonHash; var hash = data.anonHash;
Merge.anonDriveIntoUser(store, hash, cb); Merge.anonDriveIntoUser(store, hash, cb);
@ -573,6 +582,7 @@ define([
if (typeof attr === "string") { if (typeof attr === "string") {
console.error('DEPRECATED: use setAttribute with an array, not a string'); console.error('DEPRECATED: use setAttribute with an array, not a string');
return { return {
path: ['settings'],
obj: store.proxy.settings, obj: store.proxy.settings,
key: attr key: attr
}; };
@ -589,23 +599,28 @@ define([
obj = obj[el]; obj = obj[el];
}); });
return { return {
path: ['settings'].concat(attr),
obj: obj, obj: obj,
key: attr[attr.length-1] key: attr[attr.length-1]
}; };
}; };
// Set the display name (username) in the proxy // Set the display name (username) in the proxy
Store.setDisplayName = function (value, cb) { Store.setDisplayName = function (clientId, value, cb) {
store.proxy[Constants.displayNameKey] = value; store.proxy[Constants.displayNameKey] = value;
broadcast([clientId], "UPDATE_METADATA");
onSync(cb); onSync(cb);
}; };
// Reset the drive part of the userObject (from settings) // Reset the drive part of the userObject (from settings)
Store.resetDrive = function (data, cb) { Store.resetDrive = function (clientId, data, cb) {
nThen(function (waitFor) { nThen(function (waitFor) {
removeOwnedPads(waitFor); removeOwnedPads(waitFor);
}).nThen(function () { }).nThen(function () {
store.proxy.drive = store.fo.getStructure(); store.proxy.drive = store.fo.getStructure();
sendDriveEvent('DRIVE_CHANGE', {
path: ['drive', 'filesData']
}, clientId);
onSync(cb); onSync(cb);
}); });
}; };
@ -617,25 +632,28 @@ define([
* - attr (Array) * - attr (Array)
* - value (String) * - value (String)
*/ */
Store.setPadAttribute = function (data, cb) { Store.setPadAttribute = function (clientId, data, cb) {
store.userObject.setPadAttribute(data.href, data.attr, data.value, function () { store.userObject.setPadAttribute(data.href, data.attr, data.value, function () {
sendDriveEvent('DRIVE_CHANGE', {
path: ['drive', UserObject.FILES_DATA]
}, clientId);
onSync(cb); onSync(cb);
}); });
}; };
Store.getPadAttribute = function (data, cb) { Store.getPadAttribute = function (clientId, data, cb) {
store.userObject.getPadAttribute(data.href, data.attr, function (err, val) { store.userObject.getPadAttribute(data.href, data.attr, function (err, val) {
if (err) { return void cb({error: err}); } if (err) { return void cb({error: err}); }
cb(val); cb(val);
}); });
}; };
Store.setAttribute = function (data, cb) { Store.setAttribute = function (clientId, data, cb) {
try { try {
var object = getAttributeObject(data.attr); var object = getAttributeObject(data.attr);
object.obj[object.key] = data.value; object.obj[object.key] = data.value;
} catch (e) { return void cb({error: e}); } } catch (e) { return void cb({error: e}); }
onSync(cb); onSync(cb);
}; };
Store.getAttribute = function (data, cb) { Store.getAttribute = function (clientId, data, cb) {
var object; var object;
try { try {
object = getAttributeObject(data.attr); object = getAttributeObject(data.attr);
@ -644,12 +662,12 @@ define([
}; };
// Tags // Tags
Store.listAllTags = function (data, cb) { Store.listAllTags = function (clientId, data, cb) {
cb(store.userObject.getTagsList()); cb(store.userObject.getTagsList());
}; };
// Templates // Templates
Store.getTemplates = function (data, cb) { Store.getTemplates = function (clientId, data, cb) {
var templateFiles = store.userObject.getFiles(['template']); var templateFiles = store.userObject.getFiles(['template']);
var res = []; var res = [];
templateFiles.forEach(function (f) { templateFiles.forEach(function (f) {
@ -658,7 +676,7 @@ define([
}); });
cb(res); cb(res);
}; };
Store.incrementTemplateUse = function (href) { Store.incrementTemplateUse = function (clientId, href) {
store.userObject.getPadAttribute(href, 'used', function (err, data) { store.userObject.getPadAttribute(href, 'used', function (err, data) {
// This is a not critical function, abort in case of error to make sure we won't // This is a not critical function, abort in case of error to make sure we won't
// create any issue with the user object or the async store // create any issue with the user object or the async store
@ -669,12 +687,15 @@ define([
}; };
// Pads // Pads
Store.moveToTrash = function (data, cb) { Store.moveToTrash = function (clientId, data, cb) {
var href = Hash.getRelativeHref(data.href); var href = Hash.getRelativeHref(data.href);
store.userObject.forget(href); store.userObject.forget(href);
sendDriveEvent('DRIVE_CHANGE', {
path: ['drive', UserObject.FILES_DATA]
}, clientId);
onSync(cb); onSync(cb);
}; };
Store.setPadTitle = function (data, cb) { Store.setPadTitle = function (clientId, data, cb) {
var title = data.title; var title = data.title;
var href = data.href; var href = data.href;
var channel = data.channel; var channel = data.channel;
@ -683,14 +704,16 @@ define([
if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); } if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); }
var channelData = Store.channels && Store.channels[channel];
var owners; var owners;
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) { if (channelData && channelData.wc && channel === channelData.wc.id) {
owners = Store.channel.data.owners || undefined; owners = channelData.data.owners || undefined;
} }
var expire; var expire;
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) { if (channelData && channelData.wc && channel === channelData.wc.id) {
expire = +Store.channel.data.expire || undefined; expire = +channelData.data.expire || undefined;
} }
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {}; var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
@ -757,7 +780,7 @@ define([
// Add the pad if it does not exist in our drive // Add the pad if it does not exist in our drive
if (!contains) { if (!contains) {
Store.addPad({ Store.addPad(clientId, {
href: href, href: href,
channel: channel, channel: channel,
title: title, title: title,
@ -767,12 +790,16 @@ define([
path: data.path path: data.path
}, cb); }, cb);
return; return;
} else {
sendDriveEvent('DRIVE_CHANGE', {
path: ['drive', UserObject.FILES_DATA]
}, clientId);
} }
onSync(cb); onSync(cb);
}; };
// Filepicker app // Filepicker app
Store.getSecureFilesList = function (query, cb) { Store.getSecureFilesList = function (clientId, query, cb) {
var list = {}; var list = {};
var hashes = []; var hashes = [];
var types = query.types; var types = query.types;
@ -801,38 +828,42 @@ define([
}); });
cb(list); cb(list);
}; };
Store.getPadData = function (id, cb) { Store.getPadData = function (clientId, id, cb) {
cb(store.userObject.getFileData(id)); cb(store.userObject.getFileData(id));
}; };
// Messaging (manage friends from the userlist) // Messaging (manage friends from the userlist)
var getMessagingCfg = function () { var getMessagingCfg = function (clientId) {
return { return {
proxy: store.proxy, proxy: store.proxy,
realtime: store.realtime, realtime: store.realtime,
network: store.network, network: store.network,
updateMetadata: function () { updateMetadata: function () {
postMessage("UPDATE_METADATA"); postMessage(clientId, "UPDATE_METADATA");
}, },
pinPads: Store.pinPads, pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
friendComplete: function (data) { friendComplete: function (data) {
postMessage("EV_FRIEND_COMPLETE", data); postMessage(clientId, "EV_FRIEND_COMPLETE", data);
}, },
friendRequest: function (data, cb) { friendRequest: function (data, cb) {
postMessage("Q_FRIEND_REQUEST", data, cb); postMessage(clientId, "Q_FRIEND_REQUEST", data, cb);
}, },
}; };
}; };
Store.inviteFromUserlist = function (data, cb) { Store.inviteFromUserlist = function (clientId, data, cb) {
var messagingCfg = getMessagingCfg(); var messagingCfg = getMessagingCfg(clientId);
Messaging.inviteFromUserlist(messagingCfg, data, cb); Messaging.inviteFromUserlist(messagingCfg, data, cb);
}; };
Store.addDirectMessageHandlers = function (clientId, data) {
var messagingCfg = getMessagingCfg(clientId);
Messaging.addDirectMessageHandler(messagingCfg, data.href);
};
// Messenger // Messenger
// Get hashes for the share button // Get hashes for the share button
Store.getStrongerHash = function (data, cb) { Store.getStrongerHash = function (clientId, data, cb) {
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {}; var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
// If we have a stronger version in drive, add it and add a redirect button // If we have a stronger version in drive, add it and add a redirect button
@ -918,51 +949,108 @@ define([
/////////////////////// PAD ////////////////////////////////////// /////////////////////// PAD //////////////////////////////////////
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// TODO with sharedworker var channels = Store.channels = {};
// channel will be an object storing the webchannel associated to each browser tab
var channel = Store.channel = { Store.joinPad = function (clientId, data) {
var isNew = typeof channels[data.channel] === "undefined";
var channel = channels[data.channel] = channels[data.channel] || {
queue: [], queue: [],
data: {} data: {},
clients: [],
bcast: function (cmd, data, notMe) {
channel.clients.forEach(function (cId) {
if (cId === notMe) { return; }
postMessage(cId, cmd, data);
});
},
history: [],
pushHistory: function (msg, isCp) {
if (isCp) {
channel.history.push('cp|' + msg);
var i;
for (i = channel.history.length - 2; i > 0; i--) {
if (/^cp\|/.test(channel.history[i])) { break; }
}
channel.history = channel.history.slice(i);
return;
}
channel.history.push(msg);
}
}; };
Store.joinPad = function (data, cb) { if (channel.clients.indexOf(clientId) === -1) {
channel.clients.push(clientId);
}
if (!isNew && channel.wc) {
postMessage(clientId, "PAD_CONNECT", {
myID: channel.wc.myID,
id: channel.wc.id,
members: channel.wc.members
});
channel.wc.members.forEach(function (m) {
postMessage(clientId, "PAD_JOIN", m);
});
channel.history.forEach(function (msg) {
postMessage(clientId, "PAD_MESSAGE", {
msg: CpNfWorker.removeCp(msg),
user: channel.wc.myID,
validateKey: channel.data.validateKey
});
});
postMessage(clientId, "PAD_READY");
return;
}
var conf = { var conf = {
onReady: function (padData) { onReady: function (padData) {
channel.data = padData || {}; channel.data = padData || {};
postMessage("PAD_READY"); postMessage(clientId, "PAD_READY");
}, // post EV_PAD_READY },
onMessage: function (user, m, validateKey) { onMessage: function (user, m, validateKey, isCp) {
postMessage("PAD_MESSAGE", { channel.pushHistory(m, isCp);
channel.bcast("PAD_MESSAGE", {
user: user, user: user,
msg: m, msg: m,
validateKey: validateKey validateKey: validateKey
}); });
}, // post EV_PAD_MESSAGE },
onJoin: function (m) { onJoin: function (m) {
postMessage("PAD_JOIN", m); channel.bcast("PAD_JOIN", m);
}, // post EV_PAD_JOIN },
onLeave: function (m) { onLeave: function (m) {
postMessage("PAD_LEAVE", m); channel.bcast("PAD_LEAVE", m);
}, // post EV_PAD_LEAVE },
onDisconnect: function () { onDisconnect: function () {
postMessage("PAD_DISCONNECT"); channel.bcast("PAD_DISCONNECT");
}, // post EV_PAD_DISCONNECT },
onError: function (err) { onError: function (err) {
postMessage("PAD_ERROR", err); channel.bcast("PAD_ERROR", err);
}, // post EV_PAD_ERROR delete channels[data.channel]; // TODO test?
},
channel: data.channel, channel: data.channel,
validateKey: data.validateKey, validateKey: data.validateKey,
owners: data.owners, owners: data.owners,
password: data.password, password: data.password,
expire: data.expire, expire: data.expire,
network: store.network, network: store.network,
readOnly: data.readOnly, //readOnly: data.readOnly,
onConnect: function (wc, sendMessage) { onConnect: function (wc, sendMessage) {
channel.sendMessage = sendMessage; channel.sendMessage = function (msg, cId, cb) {
// Send to server
sendMessage(msg, cb);
// Broadcast to other tabs
channel.pushHistory(CpNfWorker.removeCp(msg), /^cp\|/.test(msg));
channel.bcast("PAD_MESSAGE", {
user: wc.myID,
msg: CpNfWorker.removeCp(msg),
validateKey: channel.data.validateKey
}, cId);
};
channel.wc = wc; channel.wc = wc;
channel.queue.forEach(function (data) { channel.queue.forEach(function (data) {
sendMessage(data.message); channel.sendMessage(data.message, clientId);
}); });
cb({ channel.bcast("PAD_CONNECT", {
myID: wc.myID, myID: wc.myID,
id: wc.id, id: wc.id,
members: wc.members members: wc.members
@ -971,14 +1059,20 @@ define([
}; };
CpNfWorker.start(conf); CpNfWorker.start(conf);
}; };
Store.sendPadMsg = function (data, cb) { Store.sendPadMsg = function (clientId, data, cb) {
if (!channel.wc) { channel.queue.push(data); } var msg = data.msg;
channel.sendMessage(data, cb); var channel = channels[data.channel];
if (!channel) {
return; }
if (!channel.wc) {
channel.queue.push(msg);
return void cb();
}
channel.sendMessage(msg, clientId, cb);
}; };
// TODO
// GET_FULL_HISTORY from sframe-common-outer // GET_FULL_HISTORY from sframe-common-outer
Store.getFullHistory = function (data, cb) { Store.getFullHistory = function (clientId, data, cb) {
var network = store.network; var network = store.network;
var hkn = network.historyKeeper; var hkn = network.historyKeeper;
//var crypto = Crypto.createEncryptor(data.keys); //var crypto = Crypto.createEncryptor(data.keys);
@ -1013,67 +1107,187 @@ define([
network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', data.channel, data.validateKey])); network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', data.channel, data.validateKey]));
}; };
// TODO with sharedworker
// when the tab is closed, leave the pad
// Drive // Drive
Store.userObjectCommand = function (cmdData, cb) { Store.userObjectCommand = function (clientId, cmdData, cb) {
if (!cmdData || !cmdData.cmd) { return; } if (!cmdData || !cmdData.cmd) { return; }
var data = cmdData.data; var data = cmdData.data;
var cb2 = function (data2) {
var paths = data.paths || [data.path] || [];
paths = paths.concat(data.newPath || []);
paths.forEach(function (p) {
sendDriveEvent('DRIVE_CHANGE', {
//path: ['drive', UserObject.FILES_DATA]
path: ['drive'].concat(p)
}, clientId);
});
cb(data2);
};
switch (cmdData.cmd) { switch (cmdData.cmd) {
case 'move': case 'move':
store.userObject.move(data.paths, data.newPath, cb); break; store.userObject.move(data.paths, data.newPath, cb2); break;
case 'restore': case 'restore':
store.userObject.restore(data.path, cb); break; store.userObject.restore(data.path, cb2); break;
case 'addFolder': case 'addFolder':
store.userObject.addFolder(data.path, data.name, cb); break; store.userObject.addFolder(data.path, data.name, cb2); break;
case 'delete': case 'delete':
store.userObject.delete(data.paths, cb, data.nocheck, data.isOwnPadRemoved); break; store.userObject.delete(data.paths, cb2, data.nocheck, data.isOwnPadRemoved); break;
case 'emptyTrash': case 'emptyTrash':
store.userObject.emptyTrash(cb); break; store.userObject.emptyTrash(cb2); break;
case 'rename': case 'rename':
store.userObject.rename(data.path, data.newName, cb); break; store.userObject.rename(data.path, data.newName, cb2); break;
default: default:
cb(); cb();
} }
}; };
// Clients management
var driveEventClients = [];
var messengerEventClients = [];
var dropChannel = function (chanId) {
if (!Store.channels[chanId]) { return; }
if (Store.channels[chanId].wc) {
Store.channels[chanId].wc.leave('');
}
delete Store.channels[chanId];
};
Store._removeClient = function (clientId) {
var driveIdx = driveEventClients.indexOf(clientId);
if (driveIdx !== -1) {
driveEventClients.splice(driveIdx, 1);
}
var messengerIdx = messengerEventClients.indexOf(clientId);
if (messengerIdx !== -1) {
messengerEventClients.splice(messengerIdx, 1);
}
Object.keys(Store.channels).forEach(function (chanId) {
var chanIdx = Store.channels[chanId].clients.indexOf(clientId);
if (chanIdx !== -1) {
Store.channels[chanId].clients.splice(chanIdx, 1);
}
if (Store.channels[chanId].clients.length === 0) {
dropChannel(chanId);
}
});
};
// Special events
var driveEventInit = false;
sendDriveEvent = function (q, data, sender) {
driveEventClients.forEach(function (cId) {
if (cId === sender) { return; }
postMessage(cId, q, data);
});
};
Store._subscribeToDrive = function (clientId) {
if (driveEventClients.indexOf(clientId) === -1) {
driveEventClients.push(clientId);
}
if (!driveEventInit) {
store.proxy.on('change', [], function (o, n, p) {
sendDriveEvent('DRIVE_CHANGE', {
old: o,
new: n,
path: p
});
});
store.proxy.on('remove', [], function (o, p) {
sendDriveEvent(clientId, 'DRIVE_REMOVE', {
old: o,
path: p
});
});
driveEventInit = true;
}
};
var messengerEventInit = false;
var sendMessengerEvent = function (q, data) {
messengerEventClients.forEach(function (cId) {
postMessage(cId, q, data);
});
};
Store._subscribeToMessenger = function (clientId) {
if (messengerEventClients.indexOf(clientId) === -1) {
messengerEventClients.push(clientId);
}
if (!messengerEventInit) {
var messenger = store.messenger = Messenger.messenger(store);
messenger.on('message', function (message) {
sendMessengerEvent('CONTACTS_MESSAGE', message);
});
messenger.on('join', function (curvePublic, channel) {
sendMessengerEvent('CONTACTS_JOIN', {
curvePublic: curvePublic,
channel: channel,
});
});
messenger.on('leave', function (curvePublic, channel) {
sendMessengerEvent('CONTACTS_LEAVE', {
curvePublic: curvePublic,
channel: channel,
});
});
messenger.on('update', function (info, curvePublic) {
sendMessengerEvent('CONTACTS_UPDATE', {
curvePublic: curvePublic,
info: info,
});
});
messenger.on('friend', function (curvePublic) {
sendMessengerEvent('CONTACTS_FRIEND', {
curvePublic: curvePublic,
});
});
messenger.on('unfriend', function (curvePublic) {
sendMessengerEvent('CONTACTS_UNFRIEND', {
curvePublic: curvePublic,
});
});
messengerEventInit = true;
}
};
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
/////////////////////// Init ///////////////////////////////////// /////////////////////// Init /////////////////////////////////////
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
var onReady = function (returned, cb) { var onReady = function (clientId, returned, cb) {
var proxy = store.proxy; var proxy = store.proxy;
var userObject = store.userObject = UserObject.init(proxy.drive, { var userObject = store.userObject = UserObject.init(proxy.drive, {
pinPads: Store.pinPads, pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
unpinPads: Store.unpinPads, unpinPads: function (data, cb) { Store.unpinPads(null, data, cb); },
removeOwnedChannel: Store.removeOwnedChannel, removeOwnedChannel: function (data, cb) { Store.removeOwnedChannel(null, data, cb); },
edPublic: store.proxy.edPublic, edPublic: store.proxy.edPublic,
loggedIn: store.loggedIn, loggedIn: store.loggedIn,
log: function (msg) { log: function (msg) {
postMessage("DRIVE_LOG", msg); // broadcast to all drive apps
sendDriveEvent("DRIVE_LOG", msg);
} }
}); });
nThen(function (waitFor) { nThen(function (waitFor) {
postMessage('LOADING_DRIVE', { postMessage(clientId, 'LOADING_DRIVE', {
state: 2 state: 2
}); });
userObject.migrate(waitFor()); userObject.migrate(waitFor());
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
Migrate(proxy, waitFor(), function (version, progress) { Migrate(proxy, waitFor(), function (version, progress) {
postMessage('LOADING_DRIVE', { postMessage(clientId, 'LOADING_DRIVE', {
state: 2, state: 2,
progress: progress progress: progress
}); });
}); });
}).nThen(function () { }).nThen(function () {
postMessage('LOADING_DRIVE', { postMessage(clientId, 'LOADING_DRIVE', {
state: 3 state: 3
}); });
userObject.fixFiles(); userObject.fixFiles();
var requestLogin = function () { var requestLogin = function () {
postMessage("REQUEST_LOGIN"); broadcast([], "REQUEST_LOGIN");
}; };
if (store.loggedIn) { if (store.loggedIn) {
@ -1118,26 +1332,26 @@ define([
proxy.on('change', [Constants.displayNameKey], function (o, n) { proxy.on('change', [Constants.displayNameKey], function (o, n) {
if (typeof(n) !== "string") { return; } if (typeof(n) !== "string") { return; }
postMessage("UPDATE_METADATA"); broadcast([], "UPDATE_METADATA");
}); });
proxy.on('change', ['profile'], function () { proxy.on('change', ['profile'], function () {
// Trigger userlist update when the avatar has changed // Trigger userlist update when the avatar has changed
postMessage("UPDATE_METADATA"); broadcast([], "UPDATE_METADATA");
}); });
proxy.on('change', ['friends'], function () { proxy.on('change', ['friends'], function () {
// Trigger userlist update when the friendlist has changed // Trigger userlist update when the friendlist has changed
postMessage("UPDATE_METADATA"); broadcast([], "UPDATE_METADATA");
}); });
proxy.on('change', ['settings'], function () { proxy.on('change', ['settings'], function () {
postMessage("UPDATE_METADATA"); broadcast([], "UPDATE_METADATA");
}); });
proxy.on('change', [Constants.tokenKey], function () { proxy.on('change', [Constants.tokenKey], function () {
postMessage("UPDATE_TOKEN", { token: proxy[Constants.tokenKey] }); broadcast([], "UPDATE_TOKEN", { token: proxy[Constants.tokenKey] });
}); });
}); });
}; };
var connect = function (data, cb) { var connect = function (clientId, data, cb) {
var hash = data.userHash || data.anonHash || Hash.createRandomHash('drive'); var hash = data.userHash || data.anonHash || Hash.createRandomHash('drive');
storeHash = hash; storeHash = hash;
if (!hash) { if (!hash) {
@ -1177,9 +1391,9 @@ define([
&& !drive['filesData']) { && !drive['filesData']) {
drive[Constants.oldStorageKey] = []; drive[Constants.oldStorageKey] = [];
} }
postMessage('LOADING_DRIVE', { state: 1 }); postMessage(clientId, 'LOADING_DRIVE', { state: 1 });
// Drive already exist: return the existing drive, don't load data from legacy store // Drive already exist: return the existing drive, don't load data from legacy store
onReady(returned, cb); onReady(clientId, returned, cb);
}) })
.on('change', ['drive', 'migrate'], function () { .on('change', ['drive', 'migrate'], function () {
var path = arguments[2]; var path = arguments[2];
@ -1187,15 +1401,15 @@ define([
if (path[0] === 'drive' && path[1] === "migrate" && value === 1) { if (path[0] === 'drive' && path[1] === "migrate" && value === 1) {
rt.network.disconnect(); rt.network.disconnect();
rt.realtime.abort(); rt.realtime.abort();
postMessage('NETWORK_DISCONNECT'); broadcast([], 'NETWORK_DISCONNECT');
} }
}); });
rt.proxy.on('disconnect', function () { rt.proxy.on('disconnect', function () {
postMessage('NETWORK_DISCONNECT'); broadcast([], 'NETWORK_DISCONNECT');
}); });
rt.proxy.on('reconnect', function (info) { rt.proxy.on('reconnect', function (info) {
postMessage('NETWORK_RECONNECT', {myId: info.myId}); broadcast([], 'NETWORK_RECONNECT', {myId: info.myId});
}); });
}; };
@ -1210,7 +1424,7 @@ define([
* - requestLogin * - requestLogin
*/ */
var initialized = false; var initialized = false;
Store.init = function (data, callback) { Store.init = function (clientId, data, callback) {
if (initialized) { if (initialized) {
return void callback({ return void callback({
state: 'ALREADY_INIT', state: 'ALREADY_INIT',
@ -1218,75 +1432,21 @@ define([
}); });
} }
initialized = true; initialized = true;
postMessage = function (cmd, d, cb) { postMessage = function (clientId, cmd, d, cb) {
setTimeout(function () { data.query(clientId, cmd, d, cb);
data.query(cmd, d, cb); // TODO temporary, will be replaced by webworker channel };
}); broadcast = function (excludes, cmd, d, cb) {
data.broadcast(excludes, cmd, d, cb);
}; };
store.data = data; store.data = data;
connect(data, function (ret) { connect(clientId, data, function (ret) {
if (Object.keys(store.proxy).length === 1) { if (Object.keys(store.proxy).length === 1) {
Feedback.send("FIRST_APP_USE", true); Feedback.send("FIRST_APP_USE", true);
} }
store.returned = ret; store.returned = ret;
callback(ret); callback(ret);
var messagingCfg = getMessagingCfg();
Messaging.addDirectMessageHandler(messagingCfg);
// Send events whenever there is a change or a removal in the drive
if (data.driveEvents) {
store.proxy.on('change', [], function (o, n, p) {
postMessage('DRIVE_CHANGE', {
old: o,
new: n,
path: p
});
});
store.proxy.on('remove', [], function (o, p) {
postMessage('DRIVE_REMOVE', {
old: o,
path: p
});
});
}
if (data.messenger) {
var messenger = store.messenger = Messenger.messenger(store);
messenger.on('message', function (message) {
postMessage('CONTACTS_MESSAGE', message);
});
messenger.on('join', function (curvePublic, channel) {
postMessage('CONTACTS_JOIN', {
curvePublic: curvePublic,
channel: channel,
});
});
messenger.on('leave', function (curvePublic, channel) {
postMessage('CONTACTS_LEAVE', {
curvePublic: curvePublic,
channel: channel,
});
});
messenger.on('update', function (info, curvePublic) {
postMessage('CONTACTS_UPDATE', {
curvePublic: curvePublic,
info: info,
});
});
messenger.on('friend', function (curvePublic) {
postMessage('CONTACTS_FRIEND', {
curvePublic: curvePublic,
});
});
messenger.on('unfriend', function (curvePublic) {
postMessage('CONTACTS_UNFRIEND', {
curvePublic: curvePublic,
});
});
}
}); });
}; };
@ -1295,4 +1455,9 @@ define([
store.network.disconnect(); store.network.disconnect();
}; };
return Store; return Store;
};
return {
create: create
};
}); });

@ -22,6 +22,10 @@ define([], function () {
var unBencode = function (str) { return str.replace(/^\d+:/, ''); }; var unBencode = function (str) { return str.replace(/^\d+:/, ''); };
var removeCp = function (str) {
return str.replace(/^cp\|([A-Za-z0-9+\/=]{0,20}\|)?/, '');
};
var start = function (conf) { var start = function (conf) {
var channel = conf.channel; var channel = conf.channel;
var validateKey = conf.validateKey; var validateKey = conf.validateKey;
@ -72,7 +76,7 @@ define([], function () {
// at the beginning of each message on the server. // at the beginning of each message on the server.
// We have to make sure our regex ignores this nonce using {0,20} (our IDs // We have to make sure our regex ignores this nonce using {0,20} (our IDs
// should only be 8 characters long) // should only be 8 characters long)
return msg.replace(/^cp\|([A-Za-z0-9+\/=]{0,20}\|)?/, ''); return removeCp(msg);
}; };
var msgOut = function (msg) { var msgOut = function (msg) {
@ -124,6 +128,8 @@ define([], function () {
lastKnownHash = msg.slice(0,64); lastKnownHash = msg.slice(0,64);
var isCp = /^cp\|/.test(msg);
var message = msgIn(peer, msg); var message = msgIn(peer, msg);
verbose(message); verbose(message);
@ -134,7 +140,7 @@ define([], function () {
message = unBencode(message);//.slice(message.indexOf(':[') + 1); message = unBencode(message);//.slice(message.indexOf(':[') + 1);
// pass the message into Chainpad // pass the message into Chainpad
onMessage(peer, message, validateKey); onMessage(peer, message, validateKey, isCp);
//sframeChan.query('Q_RT_MESSAGE', message, function () { }); //sframeChan.query('Q_RT_MESSAGE', message, function () { });
}; };
@ -259,7 +265,8 @@ define([], function () {
}; };
return { return {
start: start start: start,
removeCp: removeCp
/*function (config) { /*function (config) {
config.sframeChan.whenReg('EV_RT_READY', function () { config.sframeChan.whenReg('EV_RT_READY', function () {
start(config); start(config);

@ -0,0 +1,103 @@
define([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js',
], function (Util, Channel, SRpc) {
var msgEv = Util.mkEvent();
var sendMsg = Util.mkEvent();
var create = function () {
var Rpc = SRpc();
var postMessage = function (data) {
sendMsg.fire(data);
};
Channel.create(msgEv, postMessage, function (chan) {
var clientId = '1';
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
console.log(data);
}
});
});
chan.on('CONNECT', function (cfg, cb) {
// load Store here, with cfg, and pass a "query" (chan.query)
// cId is a clientId used in ServiceWorker or SharedWorker
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
if (excludes.indexOf(clientId) !== -1) { return; }
chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (data && data.state === "ALREADY_INIT") {
return void cb(data);
}
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
cb(data);
});
});
var chanId;
chan.on('JOIN_PAD', function (data, cb) {
chanId = data.channel;
try {
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: chanId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
});
}, true);
};
return {
query: function (data) {
msgEv.fire({data: data});
},
onMessage: function (cb) {
sendMsg.reg(function (data) {
setTimeout(function () {
cb(data);
});
});
},
create: create
};
});

@ -0,0 +1,175 @@
/* jshint ignore:start */
importScripts('/bower_components/requirejs/require.js');
window = self;
localStorage = {
setItem: function (k, v) { localStorage[k] = v; },
getItem: function (k) { return localStorage[k]; }
};
self.tabs = {};
var postMsg = function (client, data) {
client.postMessage(data);
};
var debug = function (msg) { console.log(msg); };
// debug = function () {};
var init = function (client, cb) {
debug('SW INIT');
require([
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
debug('SW Required ressources loaded');
var msgEv = Util.mkEvent();
if (!self.Rpc) {
self.Rpc = SRpc();
}
var Rpc = self.Rpc;
var postToClient = function (data) {
postMsg(client, data);
};
Channel.create(msgEv, postToClient, function (chan) {
debug('SW Channel created');
var clientId = client.id;
self.tabs[clientId].chan = chan;
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
console.log(data);
}
if (q === "DISCONNECT") {
console.log('Deleting existing store!');
delete self.Rpc;
delete self.store;
}
});
});
chan.on('CONNECT', function (cfg, cb) {
debug('SW Connect callback');
if (self.store) {
debug('Store already exists!');
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
return void cb(self.store);
}
debug('Loading new async store');
// One-time initialization (init async-store)
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
Object.keys(self.tabs).forEach(function (cId) {
if (excludes.indexOf(cId) !== -1) { return; }
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
if (data && data.state === "ALREADY_INIT") {
return void cb(data.returned);
}
self.store = data;
cb(data);
});
});
chan.on('JOIN_PAD', function (data, cb) {
self.tabs[clientId].channelId = data.channel;
try {
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: self.tabs[clientId].channelId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
});
cb();
}, true);
self.tabs[client.id].msgEv = msgEv;
self.tabs[client.id].close = function () {
Rpc._removeClient(client.id);
};
});
});
};
self.addEventListener('message', function (e) {
var cId = e.source.id;
if (e.data === "INIT") {
if (tabs[cId]) { return; }
tabs[cId] = {
client: e.source
};
init(e.source, function () {
postMsg(e.source, 'SW_READY');
});
} else if (e.data === "CLOSE") {
if (tabs[cId] && tabs[cId].close) {
console.log('leave');
tabs[cId].close();
}
} else if (self.tabs[cId] && self.tabs[cId].msgEv) {
self.tabs[cId].msgEv.fire(e);
}
});
self.addEventListener('install', function (e) {
debug('V1 installing…');
self.skipWaiting();
});
self.addEventListener('activate', function (e) {
debug('V1 now ready to handle fetches!');
});

@ -0,0 +1,174 @@
/* jshint ignore:start */
importScripts('/bower_components/requirejs/require.js');
window = self;
localStorage = {
setItem: function (k, v) { localStorage[k] = v; },
getItem: function (k) { return localStorage[k]; }
};
self.tabs = {};
var postMsg = function (client, data) {
client.port.postMessage(data);
};
var debug = function (msg) { console.log(msg); };
// debug = function () {};
var init = function (client, cb) {
debug('SharedW INIT');
require([
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
debug('SharedW Required ressources loaded');
var msgEv = Util.mkEvent();
if (!self.Rpc) {
self.Rpc = SRpc();
}
var Rpc = self.Rpc;
var postToClient = function (data) {
postMsg(client, data);
};
Channel.create(msgEv, postToClient, function (chan) {
debug('SharedW Channel created');
var clientId = client.id;
client.chan = chan;
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
console.log(data);
}
if (q === "DISCONNECT") {
console.log('Deleting existing store!');
delete self.Rpc;
delete self.store;
}
});
});
chan.on('CONNECT', function (cfg, cb) {
debug('SharedW connecting to store...');
if (self.store) {
debug('Store already exists!');
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
return void cb(self.store);
}
debug('Loading new async store');
// One-time initialization (init async-store)
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
Object.keys(self.tabs).forEach(function (cId) {
if (excludes.indexOf(cId) !== -1) { return; }
self.tabs[cId].chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
if (data && data.state === "ALREADY_INIT") {
self.store = data.returned;
return void cb(data.returned);
}
self.store = data;
cb(data);
});
});
chan.on('JOIN_PAD', function (data, cb) {
client.channelId = data.channel;
try {
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: client.channelId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
});
cb();
}, true);
client.msgEv = msgEv;
client.close = function () {
Rpc._removeClient(client.id);
};
});
});
};
onconnect = function(e) {
debug('New SharedWorker client');
var port = e.ports[0];
var cId = Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
var client = self.tabs[cId] = {
id: cId,
port: port
};
port.onmessage = function (e) {
if (e.data === "INIT") {
if (client.init) { return; }
client.init = true;
init(client, function () {
postMsg(client, 'SW_READY');
});
} else if (e.data === "CLOSE") {
if (client && client.close) {
console.log('leave');
client.close();
}
} else if (client && client.msgEv) {
client.msgEv.fire(e);
}
};
};

@ -1,193 +1,96 @@
define([ define([
'/common/outer/async-store.js' '/common/outer/async-store.js'
], function (Store) { ], function (AStore) {
var create = function () {
var Store = AStore.create();
var Rpc = {}; var Rpc = {};
Rpc.query = function (cmd, data, cb) { var queries = Rpc.queries = {
switch (cmd) { // Ready
// READY CONNECT: Store.init,
case 'CONNECT': { DISCONNECT: Store.disconnect,
Store.init(data, cb); break; CREATE_README: Store.createReadme,
} MIGRATE_ANON_DRIVE: Store.migrateAnonDrive,
case 'DISCONNECT': {
Store.disconnect(data, cb); break;
}
case 'CREATE_README': {
Store.createReadme(data, cb); break;
}
case 'MIGRATE_ANON_DRIVE': {
Store.migrateAnonDrive(data, cb); break;
}
// RPC // RPC
case 'INIT_RPC': { INIT_RPC: Store.initRpc,
Store.initRpc(data, cb); break; UPDATE_PIN_LIMIT: Store.updatePinLimit,
} GET_PIN_LIMIT: Store.getPinLimit,
case 'UPDATE_PIN_LIMIT': { CLEAR_OWNED_CHANNEL: Store.clearOwnedChannel,
Store.updatePinLimit(data, cb); break; REMOVE_OWNED_CHANNEL: Store.removeOwnedChannel,
} UPLOAD_CHUNK: Store.uploadChunk,
case 'GET_PIN_LIMIT': { UPLOAD_COMPLETE: Store.uploadComplete,
Store.getPinLimit(data, cb); break; UPLOAD_STATUS: Store.uploadStatus,
} UPLOAD_CANCEL: Store.uploadCancel,
case 'CLEAR_OWNED_CHANNEL': { PIN_PADS: Store.pinPads,
Store.clearOwnedChannel(data, cb); break; UNPIN_PADS: Store.unpinPads,
} GET_DELETED_PADS: Store.getDeletedPads,
case 'REMOVE_OWNED_CHANNEL': { GET_PINNED_USAGE: Store.getPinnedUsage,
Store.removeOwnedChannel(data, cb); break;
}
case 'UPLOAD_CHUNK': {
Store.uploadChunk(data, cb); break;
}
case 'UPLOAD_COMPLETE': {
Store.uploadComplete(data, cb); break;
}
case 'UPLOAD_STATUS': {
Store.uploadStatus(data, cb); break;
}
case 'UPLOAD_CANCEL': {
Store.uploadCancel(data, cb); break;
}
case 'PIN_PADS': {
Store.pinPads(data, cb); break;
}
case 'UNPIN_PADS': {
Store.unpinPads(data, cb); break;
}
case 'GET_DELETED_PADS': {
Store.getDeletedPads(data, cb); break;
}
case 'GET_PINNED_USAGE': {
Store.getPinnedUsage(data, cb); break;
}
// ANON RPC // ANON RPC
case 'INIT_ANON_RPC': { INIT_ANON_RPC: Store.initAnonRpc,
Store.initAnonRpc(data, cb); break; ANON_RPC_MESSAGE: Store.anonRpcMsg,
} GET_FILE_SIZE: Store.getFileSize,
case 'ANON_RPC_MESSAGE': { GET_MULTIPLE_FILE_SIZE: Store.getMultipleFileSize,
Store.anonRpcMsg(data, cb); break;
}
case 'GET_FILE_SIZE': {
Store.getFileSize(data, cb); break;
}
case 'GET_MULTIPLE_FILE_SIZE': {
Store.getMultipleFileSize(data, cb); break;
}
// Store // Store
case 'GET': { GET: Store.get,
Store.get(data, cb); break; SET: Store.set,
} ADD_PAD: Store.addPad,
case 'SET': { SET_PAD_TITLE: Store.setPadTitle,
Store.set(data, cb); break; MOVE_TO_TRASH: Store.moveToTrash,
} RESET_DRIVE: Store.resetDrive,
case 'ADD_PAD': { GET_METADATA: Store.getMetadata,
Store.addPad(data, cb); break; SET_DISPLAY_NAME: Store.setDisplayName,
} SET_PAD_ATTRIBUTE: Store.setPadAttribute,
case 'SET_PAD_TITLE': { GET_PAD_ATTRIBUTE: Store.getPadAttribute,
Store.setPadTitle(data, cb); break; SET_ATTRIBUTE: Store.setAttribute,
} GET_ATTRIBUTE: Store.getAttribute,
case 'MOVE_TO_TRASH': { LIST_ALL_TAGS: Store.listAllTags,
Store.moveToTrash(data, cb); break; GET_TEMPLATES: Store.getTemplates,
} GET_SECURE_FILES_LIST: Store.getSecureFilesList,
case 'RESET_DRIVE': { GET_PAD_DATA: Store.getPadData,
Store.resetDrive(data, cb); break; GET_STRONGER_HASH: Store.getStrongerHash,
} INCREMENT_TEMPLATE_USE: Store.incrementTemplateUse,
case 'GET_METADATA': {
Store.getMetadata(data, cb); break;
}
case 'SET_DISPLAY_NAME': {
Store.setDisplayName(data, cb); break;
}
case 'SET_PAD_ATTRIBUTE': {
Store.setPadAttribute(data, cb); break;
}
case 'GET_PAD_ATTRIBUTE': {
Store.getPadAttribute(data, cb); break;
}
case 'SET_ATTRIBUTE': {
Store.setAttribute(data, cb); break;
}
case 'GET_ATTRIBUTE': {
Store.getAttribute(data, cb); break;
}
case 'LIST_ALL_TAGS': {
Store.listAllTags(data, cb); break;
}
case 'GET_TEMPLATES': {
Store.getTemplates(data, cb); break;
}
case 'GET_SECURE_FILES_LIST': {
Store.getSecureFilesList(data, cb); break;
}
case 'GET_PAD_DATA': {
Store.getPadData(data, cb); break;
}
case 'GET_STRONGER_HASH': {
Store.getStrongerHash(data, cb); break;
}
case 'INCREMENT_TEMPLATE_USE': {
Store.incrementTemplateUse(data); break;
}
// Messaging // Messaging
case 'INVITE_FROM_USERLIST': { INVITE_FROM_USERLIST: Store.inviteFromUserlist,
Store.inviteFromUserlist(data, cb); break; ADD_DIRECT_MESSAGE_HANDLERS: Store.addDirectMessageHandlers,
}
// Messenger // Messenger
case 'CONTACTS_GET_FRIEND_LIST': { CONTACTS_GET_FRIEND_LIST: Store.messenger.getFriendList,
Store.messenger.getFriendList(data, cb); break; CONTACTS_GET_MY_INFO: Store.messenger.getMyInfo,
} CONTACTS_GET_FRIEND_INFO: Store.messenger.getFriendInfo,
case 'CONTACTS_GET_MY_INFO': { CONTACTS_REMOVE_FRIEND: Store.messenger.removeFriend,
Store.messenger.getMyInfo(data, cb); break; CONTACTS_OPEN_FRIEND_CHANNEL: Store.messenger.openFriendChannel,
} CONTACTS_GET_FRIEND_STATUS: Store.messenger.getFriendStatus,
case 'CONTACTS_GET_FRIEND_INFO': { CONTACTS_GET_MORE_HISTORY: Store.messenger.getMoreHistory,
Store.messenger.getFriendInfo(data, cb); break; CONTACTS_SEND_MESSAGE: Store.messenger.sendMessage,
} CONTACTS_SET_CHANNEL_HEAD: Store.messenger.setChannelHead,
case 'CONTACTS_REMOVE_FRIEND': {
Store.messenger.removeFriend(data, cb); break;
}
case 'CONTACTS_OPEN_FRIEND_CHANNEL': {
Store.messenger.openFriendChannel(data, cb); break;
}
case 'CONTACTS_GET_FRIEND_STATUS': {
Store.messenger.getFriendStatus(data, cb); break;
}
case 'CONTACTS_GET_MORE_HISTORY': {
Store.messenger.getMoreHistory(data, cb); break;
}
case 'CONTACTS_SEND_MESSAGE': {
Store.messenger.sendMessage(data, cb); break;
}
case 'CONTACTS_SET_CHANNEL_HEAD': {
Store.messenger.setChannelHead(data, cb); break;
}
// Pad // Pad
case 'SEND_PAD_MSG': { SEND_PAD_MSG: Store.sendPadMsg,
Store.sendPadMsg(data, cb); break; JOIN_PAD: Store.joinPad,
} GET_FULL_HISTORY: Store.getFullHistory,
case 'JOIN_PAD': { IS_NEW_CHANNEL: Store.isNewChannel,
Store.joinPad(data, cb); break;
}
case 'GET_FULL_HISTORY': {
Store.getFullHistory(data, cb); break;
}
// Drive // Drive
case 'DRIVE_USEROBJECT': { DRIVE_USEROBJECT: Store.userObjectCommand,
Store.userObjectCommand(data, cb); break; // Settings,
} DELETE_ACCOUNT: Store.deleteAccount,
// Settings };
case 'DELETE_ACCOUNT': {
Store.deleteAccount(data, cb); break;
}
case 'IS_NEW_CHANNEL': {
Store.isNewChannel(data, cb); break;
}
default: {
console.error("UNHANDLED_STORE_RPC");
break; Rpc.query = function (cmd, data, cb) {
} if (queries[cmd]) {
queries[cmd]('0', data, cb);
} else {
console.error('UNHANDLED_STORE_RPC');
} }
}; };
// Internal calls
Rpc._removeClient = Store._removeClient;
Rpc._subscribeToDrive = Store._subscribeToDrive;
Rpc._subscribeToMessenger = Store._subscribeToMessenger;
return Rpc; return Rpc;
};
return create;
}); });

@ -0,0 +1,100 @@
/* jshint ignore:start */
importScripts('/bower_components/requirejs/require.js');
window = self;
localStorage = {
setItem: function (k, v) { localStorage[k] = v; },
getItem: function (k) { return localStorage[k]; }
};
require([
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
var msgEv = Util.mkEvent();
var Rpc = SRpc();
Channel.create(msgEv, postMessage, function (chan) {
var clientId = '1';
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
if (q === 'JOIN_PAD') { return; }
if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
console.log(data);
}
});
});
chan.on('CONNECT', function (cfg, cb) {
// load Store here, with cfg, and pass a "query" (chan.query)
// cId is a clientId used in ServiceWorker or SharedWorker
cfg.query = function (cId, cmd, data, cb) {
cb = cb || function () {};
chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
cfg.broadcast = function (excludes, cmd, data, cb) {
cb = cb || function () {};
if (excludes.indexOf(clientId) !== -1) { return; }
chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
cb(data2);
});
};
Rpc.queries['CONNECT'](clientId, cfg, function (data) {
if (data && data.state === "ALREADY_INIT") {
return void cb(data);
}
if (cfg.driveEvents) {
Rpc._subscribeToDrive(clientId);
}
if (cfg.messenger) {
Rpc._subscribeToMessenger(clientId);
}
cb(data);
});
});
var chanId;
chan.on('JOIN_PAD', function (data, cb) {
chanId = data.channel;
try {
Rpc.queries['JOIN_PAD'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query JOIN_PAD');
console.error(e);
console.log(data);
}
});
chan.on('SEND_PAD_MSG', function (msg, cb) {
var data = {
msg: msg,
channel: chanId
};
try {
Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query SEND_PAD_MSG');
console.error(e);
console.log(data);
}
});
}, true);
onmessage = function (e) {
msgEv.fire(e);
};
});
});

@ -0,0 +1,147 @@
// This file provides the API for the channel for talking to and from the sandbox iframe.
define([
//'/common/sframe-protocol.js',
'/common/common-util.js'
], function (/*SFrameProtocol,*/ Util) {
var mkTxid = function () {
return Math.random().toString(16).replace('0.', '') + Math.random().toString(16).replace('0.', '');
};
var create = function (onMsg, postMsg, cb, isWorker) {
if (!isWorker) {
var chanLoaded = false;
var waitingData = [];
onMsg.reg(function (data) {
if (chanLoaded) { return; }
waitingData.push(data);
});
}
var evReady = Util.mkEvent(true);
var handlers = {};
var queries = {};
// list of handlers which are registered from the other side...
var insideHandlers = [];
var callWhenRegistered = {};
var chan = {};
// Send a query. channel.query('Q_SOMETHING', { args: "whatever" }, function (reply) { ... });
chan.query = function (q, content, cb) {
var txid = mkTxid();
var timeout = setTimeout(function () {
delete queries[txid];
//console.log("Timeout making query " + q);
}, 30000);
queries[txid] = function (data, msg) {
clearTimeout(timeout);
delete queries[txid];
cb(undefined, data.content, msg);
};
evReady.reg(function () {
postMsg(JSON.stringify({
txid: txid,
content: content,
q: q
}));
});
};
// Fire an event. channel.event('EV_SOMETHING', { args: "whatever" });
var event = chan.event = function (e, content) {
evReady.reg(function () {
postMsg(JSON.stringify({ content: content, q: e }));
});
};
// Be notified on query or event. channel.on('EV_SOMETHING', function (args, reply) { ... });
// If the type is a query, your handler will be invoked with a reply function that takes
// one argument (the content to reply with).
chan.on = function (queryType, handler, quiet) {
(handlers[queryType] = handlers[queryType] || []).push(function (data, msg) {
handler(data.content, function (replyContent) {
postMsg(JSON.stringify({
txid: data.txid,
content: replyContent
}));
}, msg);
});
if (!quiet) {
event('EV_REGISTER_HANDLER', queryType);
}
};
// If a particular handler is registered, call the callback immediately, otherwise it will be called
// when that handler is first registered.
// channel.whenReg('Q_SOMETHING', function () { ...query Q_SOMETHING?... });
chan.whenReg = function (queryType, cb, always) {
var reg = always;
if (insideHandlers.indexOf(queryType) > -1) {
cb();
} else {
reg = true;
}
if (reg) {
(callWhenRegistered[queryType] = callWhenRegistered[queryType] || []).push(cb);
}
};
// Same as whenReg except it will invoke every time there is another registration, not just once.
chan.onReg = function (queryType, cb) { chan.whenReg(queryType, cb, true); };
chan.on('EV_REGISTER_HANDLER', function (content) {
if (callWhenRegistered[content]) {
callWhenRegistered[content].forEach(function (f) { f(); });
delete callWhenRegistered[content];
}
insideHandlers.push(content);
});
chan.whenReg('EV_REGISTER_HANDLER', evReady.fire);
// Make sure both iframes are ready
var isReady =false;
chan.onReady = function (h) {
if (isReady) {
return void h();
}
if (typeof(h) !== "function") { return; }
chan.on('EV_RPC_READY', function () { isReady = true; h(); });
};
chan.ready = function () {
chan.whenReg('EV_RPC_READY', function () {
chan.event('EV_RPC_READY');
});
};
onMsg.reg(function (msg) {
var data = JSON.parse(msg.data);
if (typeof(data.q) === 'string' && handlers[data.q]) {
handlers[data.q].forEach(function (f) {
f(data || JSON.parse(msg.data), msg);
data = undefined;
});
} else if (typeof(data.q) === 'undefined' && queries[data.txid]) {
queries[data.txid](data, msg);
} else {
console.log("DROP Unhandled message");
console.log(msg.data, isWorker);
console.log(msg);
}
});
if (isWorker) {
evReady.fire();
} else {
chanLoaded = true;
waitingData.forEach(function (d) {
onMsg.fire(d);
});
waitingData = [];
}
cb(chan);
};
return { create: create };
});

@ -116,6 +116,10 @@ define([], function () {
sframeChan.event('EV_RT_DISCONNECT'); sframeChan.event('EV_RT_DISCONNECT');
}); });
padRpc.onConnectEvent.reg(function (data) {
onOpen(data);
});
padRpc.onErrorEvent.reg(function (err) { padRpc.onErrorEvent.reg(function (err) {
sframeChan.event('EV_RT_ERROR', err); sframeChan.event('EV_RT_ERROR', err);
}); });
@ -128,8 +132,6 @@ define([], function () {
owners: owners, owners: owners,
password: password, password: password,
expire: expire expire: expire
}, function(data) {
onOpen(data);
}); });
}; };

@ -704,6 +704,9 @@ define([
readOnly: readOnly, readOnly: readOnly,
crypto: Crypto.createEncryptor(secret.keys), crypto: Crypto.createEncryptor(secret.keys),
onConnect: function () { onConnect: function () {
var href = parsed.getUrl();
// Add friends requests handlers when we have the final href
Cryptpad.messaging.addHandlers(href);
if (window.location.hash && window.location.hash !== '#') { if (window.location.hash && window.location.hash !== '#') {
window.location = parsed.getUrl({ window.location = parsed.getUrl({
present: parsed.hashData.present, present: parsed.hashData.present,

@ -51,6 +51,8 @@ define([
if (!window.Worker) { if (!window.Worker) {
return void $container.text("WebWorkers not supported by your browser"); return void $container.text("WebWorkers not supported by your browser");
} }
/*
// Shared worker
console.log('ready'); console.log('ready');
var myWorker = new SharedWorker('/worker/worker.js'); var myWorker = new SharedWorker('/worker/worker.js');
console.log(myWorker); console.log(myWorker);
@ -65,7 +67,73 @@ define([
} }
$container.append('<br>'); $container.append('<br>');
$container.append(e.data); $container.append(e.data);
};*/
// Service worker
if ('serviceWorker' in navigator) {
console.log('here');
var initializing = true;
var worker;
var postMessage = function (data) {
console.log(data, navigator.serviceWorker);
if (worker) {
return void worker.postMessage(data);
}
console.log('NOT READY');
/*if (navigator.serviceWorker && navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage(data);
}*/
};
navigator.serviceWorker.register('/worker/sw.js', {scope: '/'})
.then(function(reg) {
console.log(reg);
console.log('Registration succeeded. Scope is ' + reg.scope);
$container.append('<br>');
$container.append('Registered! (scope: ' + reg.scope +')');
reg.onupdatefound = function () {
if (initializing) {
var w = reg.installing;
var onStateChange = function () {
if (w.state === "activated") {
console.log(w);
worker = w;
postMessage("INIT");
w.removeEventListener("statechange", onStateChange);
}
}; };
w.addEventListener('statechange', onStateChange);
return;
}
console.log('new SW version found!');
// KILL EVERYTHING
UI.confirm("New version detected, you have to reload", function (yes) {
if (yes) { common.gotoURL(); }
});
};
// Here we add the event listener for receiving messages
navigator.serviceWorker.addEventListener('message', function (e) {
var data = e.data;
if (data && data.state === "READY") {
initializing = false;
$container.append('<hr>sw.js ready');
postMessage(["Hello worker"]);
return;
}
$container.append('<br>');
$container.append(e.data);
});
if (reg.active) {
worker = reg.active;
postMessage("INIT");
}
}).catch(function(error) {
console.log('Registration failed with ' + error);
$container.append('Registration error: ' + error);
});
} else {
console.log('NO SERVICE WORKER');
}
$container.append('<hr>inner.js ready'); $container.append('<hr>inner.js ready');
}); });
}); });

@ -0,0 +1,69 @@
/* jshint ignore:start */
var id;
//= Math.floor(Math.random()*100000);
var postMsg = function (client, data) {
client.postMessage(data);
};
var broadcast = function (data, excludes) {
// Loop over all available clients
clients.matchAll().then(function (clients) {
clients.forEach(function (client) {
if (excludes.indexOf(client.id) === -1) {
postMsg(client, data);
}
})
})
};
var sendTo = function (data, clientId){
clients.matchAll().then(function (clients) {
clients.some(function (client) {
if (client.id === clientId) {
postMsg(client, data)
}
})
})
};
var getClients = function () {
clients.matchAll().then(function (clients) {
var cl = clients.map(function (c) {
console.log(JSON.stringify(c));
return c.id;
});
console.log(cl);
});
};
self.addEventListener('message', function (e) {
console.log(clients);
console.log('worker received');
console.log(e.data);
console.log(e.source);
var cId = e.source.id;
if (e.data === "INIT") {
if (!id) {
id = Math.floor(Math.random()*100000);
}
broadcast(cId + ' has joined!', [cId]);
postMsg(e.source, {state: 'READY'});
postMsg(e.source, "Welcome to SW " + id + "!");
postMsg(e.source, "You are identified as " + cId);
} else {
console.log(e.data);
postMsg(e.source, 'Yo (Re: '+e.data+')');
}
});
self.addEventListener('install', function (e) {
console.log(e);
console.log('V1 installing…');
self.skipWaiting();
});
self.addEventListener('activate', function (e) {
console.log(e);
console.log('V1 now ready to handle fetches!');
});
Loading…
Cancel
Save