Merge branch 'refactor2' into staging

pull/1/head
yflory 7 years ago
commit 04e02871bc

@ -12,7 +12,7 @@ var map = {
var messages = {}; var messages = {};
var LS_LANG = "CRYPTPAD_LANG"; var LS_LANG = "CRYPTPAD_LANG";
var getStoredLanguage = function () { return localStorage.getItem(LS_LANG); }; var getStoredLanguage = function () { return localStorage && localStorage.getItem(LS_LANG); };
var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage || ''; }; var getBrowserLanguage = function () { return navigator.language || navigator.userLanguage || ''; };
var getLanguage = messages._getLanguage = function () { var getLanguage = messages._getLanguage = function () {
if (window.cryptpadLanguage) { return window.cryptpadLanguage; } if (window.cryptpadLanguage) { return window.cryptpadLanguage; }
@ -24,19 +24,17 @@ var getLanguage = messages._getLanguage = function () {
}; };
var language = getLanguage(); var language = getLanguage();
var req = ['jquery', '/customize/translations/messages.js']; var req = ['/common/common-util.js', '/customize/translations/messages.js'];
if (language && map[language]) { req.push('/customize/translations/messages.' + language + '.js'); } if (language && map[language]) { req.push('/customize/translations/messages.' + language + '.js'); }
define(req, function($, Default, Language) { define(req, function(Util, Default, Language) {
map.en = 'English'; map.en = 'English';
var defaultLanguage = 'en'; var defaultLanguage = 'en';
if (!Language || language === defaultLanguage || !map[language]) { Util.extend(messages, Default);
messages = $.extend(true, messages, Default); if (Language && language !== defaultLanguage) {
}
else {
// Add the translated keys to the returned object // Add the translated keys to the returned object
messages = $.extend(true, messages, Default, Language); Util.extend(messages, Language);
} }
messages._languages = map; messages._languages = map;

@ -4,8 +4,9 @@ define([
'/common/common-constants.js', '/common/common-constants.js',
'/common/outer/local-store.js', '/common/outer/local-store.js',
'/common/test.js', '/common/test.js',
'/bower_components/nthen/index.js',
'/bower_components/tweetnacl/nacl-fast.min.js' '/bower_components/tweetnacl/nacl-fast.min.js'
], function ($, Cryptpad, Constants, LocalStore, Test) { ], function ($, Cryptpad, Constants, LocalStore, Test, nThen) {
var Nacl = window.nacl; var Nacl = window.nacl;
var signMsg = function (msg, privKey) { var signMsg = function (msg, privKey) {
@ -25,11 +26,18 @@ define([
localStorage[Constants.userHashKey] = localStorage[Constants.userHashKey] || localStorage[Constants.userHashKey] = localStorage[Constants.userHashKey] ||
sessionStorage[Constants.userHashKey]; sessionStorage[Constants.userHashKey];
Cryptpad.ready(function () { var proxy;
nThen(function (waitFor) {
Cryptpad.ready(waitFor());
}).nThen(function (waitFor) {
Cryptpad.getUserObject(waitFor(function (obj) {
proxy = obj;
}));
}).nThen(function () {
console.log('IFRAME READY'); console.log('IFRAME READY');
Test(function () { Test(function () {
// This is only here to maybe trigger an error. // This is only here to maybe trigger an error.
window.drive = Cryptpad.getStore().getProxy().proxy['drive']; window.drive = proxy['drive'];
Test.passed(); Test.passed();
}); });
$(window).on("message", function (jqe) { $(window).on("message", function (jqe) {
@ -46,7 +54,6 @@ define([
} else if (!LocalStore.isLoggedIn()) { } else if (!LocalStore.isLoggedIn()) {
ret.error = "NOT_LOGGED_IN"; ret.error = "NOT_LOGGED_IN";
} else { } else {
var proxy = Cryptpad.getStore().getProxy().proxy;
var sig = signMsg(data.data, proxy.edPrivate); var sig = signMsg(data.data, proxy.edPrivate);
ret.res = { ret.res = {
uname: proxy.login_name, uname: proxy.login_name,

@ -10,5 +10,6 @@ define(function () {
displayNameKey: 'cryptpad.username', displayNameKey: 'cryptpad.username',
oldStorageKey: 'CryptPad_RECENTPADS', oldStorageKey: 'CryptPad_RECENTPADS',
storageKey: 'filesData', storageKey: 'filesData',
tokenKey: 'loginToken',
}; };
}); });

@ -1,15 +1,12 @@
define([ define([
'jquery',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/curve.js',
'/common/common-hash.js', '/common/common-hash.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-constants.js', '/common/common-constants.js',
'/customize/messages.js', '/customize/messages.js',
'/bower_components/marked/marked.min.js',
'/common/common-realtime.js', '/common/common-realtime.js',
], function ($, Crypto, Curve, Hash, Util, Constants, Messages, Marked, Realtime) { ], function (Crypto, Hash, Util, Constants, Messages, Realtime) {
var Msg = { var Msg = {
inputs: [], inputs: [],
}; };
@ -51,9 +48,8 @@ define([
}); });
}; };
Msg.getFriendChannelsList = function (common) { Msg.getFriendChannelsList = function (proxy) {
var list = []; var list = [];
var proxy = common.getProxy();
eachFriend(proxy.friends, function (friend) { eachFriend(proxy.friends, function (friend) {
list.push(friend.channel); list.push(friend.channel);
}); });
@ -61,7 +57,7 @@ define([
}; };
// TODO make this internal to the messenger // TODO make this internal to the messenger
var channels = Msg.channels = window.channels = {}; var channels = Msg.channels = {};
Msg.getLatestMessages = function () { Msg.getLatestMessages = function () {
Object.keys(channels).forEach(function (id) { Object.keys(channels).forEach(function (id) {
@ -74,8 +70,8 @@ define([
// Invitation // Invitation
// FIXME there are too many functions with this name // FIXME there are too many functions with this name
var addToFriendList = Msg.addToFriendList = function (common, data, cb) { var addToFriendList = Msg.addToFriendList = function (cfg, data, cb) {
var proxy = common.getProxy(); var proxy = cfg.proxy;
var friends = getFriendList(proxy); var friends = getFriendList(proxy);
var pubKey = data.curvePublic; // todo validata data var pubKey = data.curvePublic; // todo validata data
@ -83,19 +79,19 @@ define([
friends[pubKey] = data; friends[pubKey] = data;
Realtime.whenRealtimeSyncs(common.getRealtime(), function () { Realtime.whenRealtimeSyncs(cfg.realtime, function () {
cb(); cb();
common.pinPads([data.channel], function (e) { cfg.pinPads([data.channel], function (res) {
if (e) { console.error(e); } if (res.error) { console.error(res.error); }
}); });
}); });
common.changeDisplayName(proxy[Constants.displayNameKey]); cfg.updateMetadata();
}; };
/* Used to accept friend requests within apps other than /contacts/ */ /* Used to accept friend requests within apps other than /contacts/ */
Msg.addDirectMessageHandler = function (common) { Msg.addDirectMessageHandler = function (cfg) {
var network = common.getNetwork(); var network = cfg.network;
var proxy = common.getProxy(); var proxy = cfg.proxy;
if (!network) { return void console.error('Network not ready'); } if (!network) { return void console.error('Network not ready'); }
network.on('message', function (message, sender) { network.on('message', function (message, sender) {
var msg; var msg;
@ -138,8 +134,7 @@ define([
var confirmMsg = Messages._getKey('contacts_request', [ var confirmMsg = Messages._getKey('contacts_request', [
Util.fixHTML(msgData.displayName) Util.fixHTML(msgData.displayName)
]); ]);
common.onFriendRequest(confirmMsg, todo); cfg.friendRequest(confirmMsg, todo);
//UI.confirm(confirmMsg, todo, null, true);
return; return;
} }
if (msg[0] === "FRIEND_REQ_OK") { if (msg[0] === "FRIEND_REQ_OK") {
@ -147,14 +142,14 @@ define([
if (idx !== -1) { pendingRequests.splice(idx, 1); } if (idx !== -1) { pendingRequests.splice(idx, 1); }
// FIXME clarify this function's name // FIXME clarify this function's name
addToFriendList(common, msgData, function (err) { addToFriendList(cfg, msgData, function (err) {
if (err) { if (err) {
return void common.onFriendComplete({ return void cfg.friendComplete({
logText: Messages.contacts_addError, logText: Messages.contacts_addError,
netfluxId: sender netfluxId: sender
}); });
} }
common.onFriendComplete({ cfg.friendComplete({
logText: Messages.contacts_added, logText: Messages.contacts_added,
netfluxId: sender netfluxId: sender
}); });
@ -167,24 +162,24 @@ define([
if (msg[0] === "FRIEND_REQ_NOK") { if (msg[0] === "FRIEND_REQ_NOK") {
var i = pendingRequests.indexOf(sender); var i = pendingRequests.indexOf(sender);
if (i !== -1) { pendingRequests.splice(i, 1); } if (i !== -1) { pendingRequests.splice(i, 1); }
common.onFriendComplete({ cfg.friendComplete({
logText: Messages.contacts_rejected, logText: Messages.contacts_rejected,
netfluxId: sender netfluxId: sender
}); });
common.changeDisplayName(proxy[Constants.displayNameKey]); cfg.updateMetadata();
return; return;
} }
if (msg[0] === "FRIEND_REQ_ACK") { if (msg[0] === "FRIEND_REQ_ACK") {
var data = pending[sender]; var data = pending[sender];
if (!data) { return; } if (!data) { return; }
addToFriendList(common, data, function (err) { addToFriendList(cfg, data, function (err) {
if (err) { if (err) {
return void common.onFriendComplete({ return void cfg.friendComplete({
logText: Messages.contacts_addError, logText: Messages.contacts_addError,
netfluxId: sender netfluxId: sender
}); });
} }
common.onFriendComplete({ cfg.friendComplete({
logText: Messages.contacts_added, logText: Messages.contacts_added,
netfluxId: sender netfluxId: sender
}); });
@ -198,17 +193,14 @@ define([
}); });
}; };
Msg.getPending = function () { Msg.inviteFromUserlist = function (cfg, data, cb) {
return pendingRequests; var network = cfg.network;
}; var netfluxId = data.netfluxId;
var parsed = Hash.parsePadUrl(data.href);
Msg.inviteFromUserlist = function (common, netfluxId) {
var network = common.getNetwork();
var parsed = Hash.parsePadUrl(window.location.href);
if (!parsed.hashData) { return; } if (!parsed.hashData) { return; }
// Message // Message
var chan = parsed.hashData.channel; var chan = parsed.hashData.channel;
var myData = createData(common.getProxy()); 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 keyStr = parsed.hashData.key;
@ -218,12 +210,10 @@ define([
// Send encrypted message // Send encrypted message
if (pendingRequests.indexOf(netfluxId) === -1) { if (pendingRequests.indexOf(netfluxId) === -1) {
pendingRequests.push(netfluxId); pendingRequests.push(netfluxId);
var proxy = common.getProxy(); cfg.updateMetadata(); // redraws the userlist in pad
// this redraws the userlist after a change has occurred
// TODO rename this function to reflect its purpose
common.changeDisplayName(proxy[Constants.displayNameKey]);
} }
network.sendto(netfluxId, msgStr); network.sendto(netfluxId, msgStr);
cb();
}; };
return Msg; return Msg;

@ -5,7 +5,8 @@ define([
'/common/common-hash.js', '/common/common-hash.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-realtime.js', '/common/common-realtime.js',
], function ($, Crypto, Curve, Hash, Util, Realtime) { '/common/common-constants.js',
], function ($, Crypto, Curve, Hash, Util, Realtime, Constants) {
'use strict'; 'use strict';
var Msg = { var Msg = {
inputs: [], inputs: [],
@ -28,7 +29,7 @@ define([
var createData = Msg.createData = function (proxy, hash) { var createData = Msg.createData = function (proxy, hash) {
return { return {
channel: hash || Hash.createChannelId(), channel: hash || Hash.createChannelId(),
displayName: proxy['cryptpad.username'], displayName: proxy[Constants.displayNameKey],
profile: proxy.profile && proxy.profile.view, profile: proxy.profile && proxy.profile.view,
edPublic: proxy.edPublic, edPublic: proxy.edPublic,
curvePublic: proxy.curvePublic, curvePublic: proxy.curvePublic,
@ -56,7 +57,7 @@ define([
}); });
}; };
Msg.messenger = function (common) { Msg.messenger = function (store) {
var messenger = { var messenger = {
handlers: { handlers: {
message: [], message: [],
@ -89,9 +90,9 @@ define([
var joining = {}; var joining = {};
// declare common variables // declare common variables
var network = common.getNetwork(); var network = store.network;
var proxy = common.getProxy(); var proxy = store.proxy;
var realtime = common.getRealtime(); var realtime = store.realtime;
Msg.hk = network.historyKeeper; Msg.hk = network.historyKeeper;
var friends = getFriendList(proxy); var friends = getFriendList(proxy);
@ -629,14 +630,7 @@ define([
messenger.getMyInfo = function (cb) { messenger.getMyInfo = function (cb) {
cb(void 0, { cb(void 0, {
curvePublic: proxy.curvePublic, curvePublic: proxy.curvePublic,
displayName: common.getDisplayName(), displayName: proxy[Constants.displayNameKey]
});
};
messenger.clearOwnedChannel = function (channel, cb) {
common.clearOwnedChannel(channel, function (e) {
if (e) { return void cb(e); }
cb();
}); });
}; };

@ -1,18 +1,6 @@
define([ define([], function () {
'/customize/application_config.js',
'/customize/messages.js',
'/common/common-interface.js',
], function (AppConfig, Messages, UI) {
var common = {}; var common = {};
common.infiniteSpinnerDetected = false;
var BAD_STATE_TIMEOUT = typeof(AppConfig.badStateTimeout) === 'number'?
AppConfig.badStateTimeout: 30000;
var connected = false;
var intr;
var infiniteSpinnerHandlers = [];
/* /*
TODO make this not blow up when disconnected or lagging... TODO make this not blow up when disconnected or lagging...
*/ */
@ -20,7 +8,7 @@ define([
if (typeof(realtime.getAuthDoc) !== 'function') { if (typeof(realtime.getAuthDoc) !== 'function') {
return void console.error('improper use of this function'); return void console.error('improper use of this function');
} }
window.setTimeout(function () { setTimeout(function () {
if (realtime.getAuthDoc() === realtime.getUserDoc()) { if (realtime.getAuthDoc() === realtime.getUserDoc()) {
return void cb(); return void cb();
} else { } else {
@ -29,36 +17,5 @@ define([
}, 0); }, 0);
}; };
common.beginDetectingInfiniteSpinner = function (realtime) {
if (intr) { return; }
intr = window.setInterval(function () {
var l;
try {
l = realtime.getLag();
} catch (e) {
throw new Error("ChainPad.getLag() does not exist, please `bower update`");
}
if (l.lag < BAD_STATE_TIMEOUT || !connected) { return; }
realtime.abort();
// don't launch more than one popup
if (common.infiniteSpinnerDetected) { return; }
infiniteSpinnerHandlers.forEach(function (ish) { ish(); });
// inform the user their session is in a bad state
UI.confirm(Messages.realtime_unrecoverableError, function (yes) {
if (!yes) { return; }
window.parent.location.reload();
});
common.infiniteSpinnerDetected = true;
}, 2000);
};
common.onInfiniteSpinner = function (f) { infiniteSpinnerHandlers.push(f); };
common.setConnectionState = function (bool) {
if (typeof(bool) !== 'boolean') { return; }
connected = bool;
};
return common; return common;
}); });

@ -571,12 +571,7 @@ define([
// getPinnedUsage updates common.account.usage, and other values // getPinnedUsage updates common.account.usage, and other values
// so we can just use those and only check for errors // so we can just use those and only check for errors
var $container = $('<span>', {'class':'cp-limit-container'}); var $container = $('<span>', {'class':'cp-limit-container'});
var todo; var todo = function (err, data) {
var updateUsage = Util.notAgainForAnother(function () {
common.getPinUsage(todo);
}, LIMIT_REFRESH_RATE);
todo = function (err, data) {
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
var usage = data.usage; var usage = data.usage;
@ -645,6 +640,10 @@ define([
$limit.append($usage).append($text); $limit.append($usage).append($text);
}; };
var updateUsage = Util.notAgainForAnother(function () {
common.getPinUsage(todo);
}, LIMIT_REFRESH_RATE);
setInterval(function () { setInterval(function () {
updateUsage(); updateUsage();
}, LIMIT_REFRESH_RATE * 3); }, LIMIT_REFRESH_RATE * 3);

@ -209,6 +209,43 @@ define([], function () {
xhr.send(); xhr.send();
}; };
// Check if an element is a plain object
Util.isObject = function (o) {
return typeof (o) === "object" &&
Object.prototype.toString.call(o) === '[object Object]';
};
Util.isCircular = function (o) {
try {
JSON.stringify(o);
return false;
} catch (e) { return true; }
};
/* recursively adds the properties of an object 'b' to 'a'
arrays are only shallow copies, so references to the original
might still be present. Be mindful if you will modify 'a' in the future */
Util.extend = function (a, b) {
if (!Util.isObject(a) || !Util.isObject(b)) {
return void console.log("Extend only works with 2 objects");
}
if (Util.isCircular(b)) {
return void console.log("Extend doesn't accept circular objects");
}
for (var k in b) {
if (Util.isObject(b[k])) {
a[k] = {};
Util.extend(a[k], b[k]);
continue;
}
if (Array.isArray(b[k])) {
a[k] = b[k].slice();
continue;
}
a[k] = b[k];
}
};
return Util; return Util;
}); });
}(self)); }(self));

@ -73,12 +73,12 @@ define([
realtime.contentUpdate(doc); realtime.contentUpdate(doc);
var to = window.setTimeout(function () { var to = setTimeout(function () {
cb(new Error("Timeout")); cb(new Error("Timeout"));
}, 5000); }, 5000);
Realtime.whenRealtimeSyncs(realtime, function () { Realtime.whenRealtimeSyncs(realtime, function () {
window.clearTimeout(to); clearTimeout(to);
realtime.abort(); realtime.abort();
finish(Session, void 0); finish(Session, void 0);
}); });

File diff suppressed because it is too large Load Diff

@ -194,8 +194,8 @@ define([
}; };
var onReady = function (f, proxy, Cryptpad, exp) { var onReady = function (f, proxy, Cryptpad, exp) {
var fo = exp.fo = FO.init(proxy.drive, { var fo = exp.userObject = exp.fo = FO.init(proxy.drive, {
Cryptpad: Cryptpad, pinPads: Cryptpad.pinPads,
loggedIn: LocalStore.isLoggedIn() loggedIn: LocalStore.isLoggedIn()
}); });
var todo = function () { var todo = function () {
@ -265,15 +265,15 @@ 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; }
Cryptpad.changeDisplayName(n); Cryptpad.changeMetadata();
}); });
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
Cryptpad.changeDisplayName(proxy[Constants.displayNameKey]); Cryptpad.changeMetadata();
}); });
proxy.on('change', ['friends'], function () { proxy.on('change', ['friends'], function () {
// Trigger userlist update when the avatar has changed // Trigger userlist update when the friendlist has changed
Cryptpad.changeDisplayName(proxy[Constants.displayNameKey]); Cryptpad.changeMetadata();
}); });
proxy.on('change', [tokenKey], function () { proxy.on('change', [tokenKey], function () {
var localToken = tryParsing(localStorage.getItem(tokenKey)); var localToken = tryParsing(localStorage.getItem(tokenKey));
@ -315,6 +315,7 @@ define([
exp.realtime = rt.realtime; exp.realtime = rt.realtime;
exp.proxy = rt.proxy; exp.proxy = rt.proxy;
exp.loggedIn = Cryptpad.isLoggedIn();
rt.proxy.on('create', function (info) { rt.proxy.on('create', function (info) {
exp.info = info; exp.info = info;
if (!LocalStore.getUserHash()) { if (!LocalStore.getUserHash()) {

@ -2,8 +2,8 @@ define([
'/common/cryptget.js', '/common/cryptget.js',
'/common/userObject.js', '/common/userObject.js',
'/common/common-hash.js', '/common/common-hash.js',
'/common/outer/local-store.js', '/common/common-realtime.js',
], function (Crypt, FO, Hash, LocalStore) { ], function (Crypt, FO, Hash, Realtime) {
var exp = {}; var exp = {};
var getType = function (el) { var getType = function (el) {
@ -86,7 +86,7 @@ define([
exp.anonDriveIntoUser = function (proxyData, fsHash, cb) { exp.anonDriveIntoUser = function (proxyData, fsHash, cb) {
// Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb // Make sure we have an FS_hash and we don't use it, otherwise just stop the migration and cb
if (!fsHash || !LocalStore.isLoggedIn()) { if (!fsHash || !proxyData.loggedIn) {
if (typeof(cb) === "function") { return void cb(); } if (typeof(cb) === "function") { return void cb(); }
} }
// Get the content of FS_hash and then merge the objects, remove the migration key and cb // Get the content of FS_hash and then merge the objects, remove the migration key and cb
@ -105,11 +105,11 @@ define([
if (parsed) { if (parsed) {
var proxy = proxyData.proxy; var proxy = proxyData.proxy;
var oldFo = FO.init(parsed.drive, { var oldFo = FO.init(parsed.drive, {
loggedIn: LocalStore.isLoggedIn() loggedIn: proxyData.loggedIn
}); });
var onMigrated = function () { var onMigrated = function () {
oldFo.fixFiles(); oldFo.fixFiles();
var newFo = proxyData.fo; var newFo = proxyData.userObject;
var oldRecentPads = parsed.drive[newFo.FILES_DATA]; var oldRecentPads = parsed.drive[newFo.FILES_DATA];
var newRecentPads = proxy.drive[newFo.FILES_DATA]; var newRecentPads = proxy.drive[newFo.FILES_DATA];
var oldFiles = oldFo.getFiles([newFo.FILES_DATA]); var oldFiles = oldFo.getFiles([newFo.FILES_DATA]);
@ -154,7 +154,9 @@ define([
proxy.FS_hashes = []; proxy.FS_hashes = [];
} }
proxy.FS_hashes.push(fsHash); proxy.FS_hashes.push(fsHash);
if (typeof(cb) === "function") { cb(); } if (typeof(cb) === "function") {
Realtime.whenRealtimeSyncs(proxyData.realtime, cb);
}
}; };
oldFo.migrate(onMigrated); oldFo.migrate(onMigrated);
return; return;

@ -59,7 +59,6 @@ define(['json.sortify'], function (Sortify) {
} }
if (metadataObj.title !== rememberedTitle) { if (metadataObj.title !== rememberedTitle) {
console.log("Title update\n" + metadataObj.title + '\n');
rememberedTitle = metadataObj.title; rememberedTitle = metadataObj.title;
titleChangeHandlers.forEach(function (f) { f(metadataObj.title); }); titleChangeHandlers.forEach(function (f) { f(metadataObj.title); });
} }
@ -73,30 +72,45 @@ define(['json.sortify'], function (Sortify) {
}); });
}; };
var netfluxId;
var isReady = false;
var readyHandlers = [];
sframeChan.on('EV_METADATA_UPDATE', function (ev) { sframeChan.on('EV_METADATA_UPDATE', function (ev) {
meta = ev; meta = ev;
if (ev.priv) { if (ev.priv) {
priv = ev.priv; priv = ev.priv;
} }
if (netfluxId) {
meta.user.netfluxId = netfluxId;
}
if (!isReady) {
isReady = true;
readyHandlers.forEach(function (f) { f(); });
}
change(true); change(true);
}); });
sframeChan.on('EV_RT_CONNECT', function (ev) { sframeChan.on('EV_RT_CONNECT', function (ev) {
meta.user.netfluxId = ev.myID; netfluxId = ev.myID;
members = ev.members; members = ev.members;
if (!meta.user) { return; }
meta.user.netfluxId = netfluxId;
change(true); change(true);
}); });
sframeChan.on('EV_RT_JOIN', function (ev) { sframeChan.on('EV_RT_JOIN', function (ev) {
members.push(ev); members.push(ev);
if (!meta.user) { return; }
change(false); change(false);
}); });
sframeChan.on('EV_RT_LEAVE', function (ev) { sframeChan.on('EV_RT_LEAVE', function (ev) {
var idx = members.indexOf(ev); var idx = members.indexOf(ev);
if (idx === -1) { console.log('Error: ' + ev + ' not in members'); return; } if (idx === -1) { console.log('Error: ' + ev + ' not in members'); return; }
members.splice(idx, 1); members.splice(idx, 1);
if (!meta.user) { return; }
change(false); change(false);
}); });
sframeChan.on('EV_RT_DISCONNECT', function () { sframeChan.on('EV_RT_DISCONNECT', function () {
members = []; members = [];
if (!meta.user) { return; }
change(true); change(true);
}); });
@ -140,6 +154,10 @@ define(['json.sortify'], function (Sortify) {
}, },
getNetfluxId : function () { getNetfluxId : function () {
return meta.user.netfluxId; return meta.user.netfluxId;
},
onReady: function (f) {
if (isReady) { return void f(); }
readyHandlers.push(f);
} }
}); });
}; };

@ -0,0 +1,924 @@
define([
'/common/userObject.js',
'/common/migrate-user-object.js',
'/common/common-hash.js',
'/common/common-util.js',
'/common/common-constants.js',
'/common/common-feedback.js',
'/common/common-realtime.js',
'/common/common-messaging.js',
'/common/common-messenger.js',
'/common/outer/network-config.js',
'/bower_components/chainpad-crypto/crypto.js?v=0.1.5',
'/bower_components/chainpad/chainpad.dist.js',
'/bower_components/chainpad-listmap/chainpad-listmap.js',
], function (UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger,
NetConfig,
Crypto, ChainPad, Listmap) {
var Store = {};
var postMessage = function () {};
var storeHash;
var store = {};
var onSync = function (cb) {
Realtime.whenRealtimeSyncs(store.realtime, cb);
};
Store.get = function (key, cb) {
cb(Util.find(store.proxy, key));
};
Store.set = function (data, cb) {
var path = data.key.slice();
var key = path.pop();
var obj = Util.find(store.proxy, path);
if (!obj || typeof(obj) !== "object") { return void cb({error: 'INVALID_PATH'}); }
if (typeof data.value === "undefined") {
delete obj[key];
} else {
obj[key] = data.value;
}
onSync(cb);
};
Store.hasSigningKeys = function () {
if (!store.proxy) { return; }
return typeof(store.proxy.edPrivate) === 'string' &&
typeof(store.proxy.edPublic) === 'string';
};
Store.hasCurveKeys = function () {
if (!store.proxy) { return; }
return typeof(store.proxy.curvePrivate) === 'string' &&
typeof(store.proxy.curvePublic) === 'string';
};
var getUserChannelList = function () {
// start with your userHash...
var userHash = storeHash;
if (!userHash) { return null; }
var userParsedHash = Hash.parseTypeHash('drive', userHash);
var userChannel = userParsedHash && userParsedHash.channel;
if (!userChannel) { return null; }
var list = store.userObject.getFiles([store.userObject.FILES_DATA]).map(function (id) {
return Hash.hrefToHexChannelId(store.userObject.getFileData(id).href);
})
.filter(function (x) { return x; });
// Get the avatar
var profile = store.proxy.profile;
if (profile) {
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit) : null;
if (profileChan) { list.push(profileChan); }
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar) : null;
if (avatarChan) { list.push(avatarChan); }
}
if (store.proxy.friends) {
var fList = Messaging.getFriendChannelsList(store.proxy);
list = list.concat(fList);
}
list.push(Util.base64ToHex(userChannel));
list.sort();
return list;
};
var getCanonicalChannelList = function () {
return Util.deduplicateString(getUserChannelList()).sort();
};
//////////////////////////////////////////////////////////////////
/////////////////////// RPC //////////////////////////////////////
//////////////////////////////////////////////////////////////////
Store.pinPads = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') {
console.error('expected a callback');
}
store.rpc.pin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
Store.unpinPads = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.unpin(data, function (e, hash) {
if (e) { return void cb({error: e}); }
cb({hash: hash});
});
};
var account = {};
Store.getPinnedUsage = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.getFileListSize(function (err, bytes) {
if (typeof(bytes) === 'number') {
account.usage = bytes;
}
cb({bytes: bytes});
});
};
// Update for all users from accounts and return current user limits
Store.updatePinLimit = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.updatePinLimits(function (e, limit, plan, note) {
if (e) { return void cb({error: e}); }
account.limit = limit;
account.plan = plan;
account.note = note;
cb(account);
});
};
// Get current user limits
Store.getPinLimit = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var ALWAYS_REVALIDATE = true;
if (ALWAYS_REVALIDATE || typeof(account.limit) !== 'number' ||
typeof(account.plan) !== 'string' ||
typeof(account.note) !== 'string') {
return void store.rpc.getLimit(function (e, limit, plan, note) {
if (e) { return void cb({error: e}); }
account.limit = limit;
account.plan = plan;
account.note = note;
cb(account);
});
}
cb(account);
};
Store.clearOwnedChannel = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.clearOwnedChannel(data, function (err) {
cb({error:err});
});
};
Store.uploadComplete = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.uploadComplete(function (err, res) {
if (err) { return void cb({error:err}); }
cb(res);
});
};
Store.uploadStatus = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.uploadStatus(data.size, function (err, res) {
if (err) { return void cb({error:err}); }
cb(res);
});
};
Store.uploadCancel = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.uploadCancel(function (err, res) {
if (err) { return void cb({error:err}); }
cb(res);
});
};
var arePinsSynced = function (cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var list = getCanonicalChannelList();
var local = Hash.hashChannelList(list);
store.rpc.getServerHash(function (e, hash) {
if (e) { return void cb(e); }
cb(null, hash === local);
});
};
var resetPins = function (cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var list = getCanonicalChannelList();
store.rpc.reset(list, function (e, hash) {
if (e) { return void cb(e); }
cb(null, hash);
});
};
Store.uploadChunk = function (data, cb) {
store.rpc.send.unauthenticated('UPLOAD', data.chunk, function (e, msg) {
cb({
error: e,
msg: msg
});
});
};
Store.initRpc = function (data, cb) {
require(['/common/pinpad.js'], function (Pinpad) {
Pinpad.create(store.network, store.proxy, function (e, call) {
if (e) { return void cb({error: e}); }
store.rpc = call;
Store.getPinLimit(null, function (obj) {
if (obj.error) { console.error(obj.error); }
account.limit = obj.limit;
account.plan = obj.plan;
account.note = obj.note;
cb(obj);
});
arePinsSynced(function (err, yes) {
if (!yes) {
resetPins(function (err) {
if (err) { return console.error(err); }
console.log('RESET DONE');
});
}
});
});
});
};
//////////////////////////////////////////////////////////////////
////////////////// ANON RPC //////////////////////////////////////
//////////////////////////////////////////////////////////////////
Store.anonRpcMsg = function (data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
store.anon_rpc.send(data.msg, data.data, function (err, res) {
if (err) { return void cb({error: err}); }
cb(res);
});
};
Store.getFileSize = function (data, cb) {
console.log(data, cb);
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href);
store.anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) {
if (e) { return void cb({error: e}); }
if (response && response.length && typeof(response[0]) === 'number') {
return void cb({size: response[0]});
} else {
cb({error: 'INVALID_RESPONSE'});
}
});
};
Store.getMultipleFileSize = function (data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
if (!Array.isArray(data.files)) {
return void cb({error: 'INVALID_FILE_LIST'});
}
store.anon_rpc.send('GET_MULTIPLE_FILE_SIZE', data.files, function (e, res) {
if (e) { return void cb({error: e}); }
if (res && res.length && typeof(res[0]) === 'object') {
cb({size: res[0]});
} else {
cb({error: 'UNEXPECTED_RESPONSE'});
}
});
};
Store.initAnonRpc = function (data, cb) {
require([
'/common/rpc.js',
], function (Rpc) {
Rpc.createAnonymous(store.network, function (e, call) {
if (e) { return void cb({error: e}); }
store.anon_rpc = call;
cb();
});
});
};
//////////////////////////////////////////////////////////////////
/////////////////////// Store ////////////////////////////////////
//////////////////////////////////////////////////////////////////
// Get the metadata for sframe-common-outer
Store.getMetadata = function (data, cb) {
var metadata = {
// "user" is shared with everybody via the userlist
user: {
name: store.proxy[Constants.displayNameKey],
uid: store.proxy.uid,
avatar: Util.find(store.proxy, ['profile', 'avatar']),
profile: Util.find(store.proxy, ['profile', 'view']),
curvePublic: store.proxy.curvePublic,
},
// "priv" is not shared with other users but is needed by the apps
priv: {
edPublic: store.proxy.edPublic,
friends: store.proxy.friends,
settings: store.proxy.settings,
thumbnails: !((store.proxy.settings || {}).general || {}).disableThumbnails
}
};
cb(JSON.parse(JSON.stringify(metadata)));
};
var makePad = function (href, title) {
var now = +new Date();
return {
href: href,
atime: now,
ctime: now,
title: title || Hash.getDefaultName(Hash.parsePadUrl(href)),
};
};
Store.addPad = function (data, cb) {
if (!data.href) { return void cb({error:'NO_HREF'}); }
var pad = makePad(data.href, data.title);
store.userObject.pushData(pad, function (e, id) {
if (e) { return void cb({error: "Error while adding a template:"+ e}); }
var path = data.path || ['root'];
store.userObject.add(id, path);
onSync(cb);
});
};
/**
* add a "What is CryptPad?" pad in the drive
* data
* - driveReadme
* - driveReadmeTitle
*/
Store.createReadme = function (data, cb) {
require(['/common/cryptget.js'], function (Crypt) {
var hash = Hash.createRandomHash();
Crypt.put(hash, data.driveReadme, function (e) {
if (e) {
return void cb({ error: "Error while creating the default pad:"+ e});
}
var href = '/pad/#' + hash;
var fileData = {
href: href,
title: data.driveReadmeTitle,
atime: +new Date(),
ctime: +new Date()
};
store.userObject.pushData(fileData, function (e, id) {
if (e) {
return void cb({ error: "Error while creating the default pad:"+ e});
}
store.userObject.add(id);
onSync(cb);
});
});
});
};
/**
* Merge the anonymous drive into the user drive at registration
* data
* - anonHash
*/
Store.migrateAnonDrive = function (data, cb) {
require(['/common/mergeDrive.js'], function (Merge) {
var hash = data.anonHash;
Merge.anonDriveIntoUser(store, hash, cb);
});
};
var getAttributeObject = function (attr) {
if (typeof attr === "string") {
console.error('DEPRECATED: use setAttribute with an array, not a string');
return {
obj: store.proxy.settings,
key: attr
};
}
if (!Array.isArray(attr)) { return void console.error("Attribute must be string or array"); }
if (attr.length === 0) { return void console.error("Attribute can't be empty"); }
var obj = store.proxy.settings;
attr.forEach(function (el, i) {
if (i === attr.length-1) { return; }
if (!obj[el]) {
obj[el] = {};
}
else if (typeof obj[el] !== "object") { return void console.error("Wrong attribute"); }
obj = obj[el];
});
return {
obj: obj,
key: attr[attr.length-1]
};
};
// Set the display name (username) in the proxy
Store.setDisplayName = function (value, cb) {
store.proxy[Constants.displayNameKey] = value;
onSync(cb);
};
// Reset the drive part of the userObject (from settings)
Store.resetDrive = function (data, cb) {
store.proxy.drive = store.fo.getStructure();
onSync(cb);
};
/**
* Settings & pad attributes
* data
* - href (String)
* - attr (Array)
* - value (String)
*/
Store.setPadAttribute = function (data, cb) {
store.userObject.setPadAttribute(data.href, data.attr, data.value, function () {
onSync(cb);
});
};
Store.getPadAttribute = function (data, cb) {
store.userObject.getPadAttribute(data.href, data.attr, function (err, val) {
if (err) { return void cb({error: err}); }
cb(val);
});
};
Store.setAttribute = function (data, cb) {
try {
var object = getAttributeObject(data.attr);
object.obj[object.key] = data.value;
} catch (e) { return void cb({error: e}); }
onSync(cb);
};
Store.getAttribute = function (data, cb) {
var object;
try {
object = getAttributeObject(data.attr);
} catch (e) { return void cb({error: e}); }
cb(object.obj[object.key]);
};
// Tags
Store.listAllTags = function (data, cb) {
var all = [];
var files = Util.find(store.proxy, ['drive', 'filesData']);
if (typeof(files) !== 'object') { return cb({error: 'invalid_drive'}); }
Object.keys(files).forEach(function (k) {
var file = files[k];
if (!Array.isArray(file.tags)) { return; }
file.tags.forEach(function (tag) {
if (all.indexOf(tag) === -1) { all.push(tag); }
});
});
cb(all);
};
// Templates
Store.getTemplates = function (data, cb) {
var templateFiles = store.userObject.getFiles(['template']);
var res = [];
templateFiles.forEach(function (f) {
var data = store.userObject.getFileData(f);
res.push(JSON.parse(JSON.stringify(data)));
});
cb(res);
};
// Pads
Store.moveToTrash = function (data, cb) {
var href = Hash.getRelativeHref(data.href);
store.userObject.forget(href);
onSync(cb);
};
Store.setPadTitle = function (data, cb) {
var title = data.title;
var href = data.href;
var p = Hash.parsePadUrl(href);
var h = p.hashData;
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
var isStronger;
// If we don't find the new channel in our existing pads, we'll have to add the pads
// to filesData
var contains;
// Update all pads that use the same channel but with a weaker hash
// Edit > Edit (present) > View > View (present)
for (var id in allPads) {
var pad = allPads[id];
if (!pad.href) { continue; }
var p2 = Hash.parsePadUrl(pad.href);
var h2 = p2.hashData;
// Different types, proceed to the next one
// No hash data: corrupted pad?
if (p.type !== p2.type || !h2) { continue; }
var shouldUpdate = p.hash.replace(/\/$/, '') === p2.hash.replace(/\/$/, '');
// If the hash is different but represents the same channel, check if weaker or stronger
if (!shouldUpdate &&
h.version === 1 && h2.version === 1 &&
h.channel === h2.channel) {
// We had view & now we have edit, update
if (h2.mode === 'view' && h.mode === 'edit') { shouldUpdate = true; }
// Same mode and we had present URL, update
else if (h.mode === h2.mode && h2.present) { shouldUpdate = true; }
// If we're here it means we have a weaker URL:
// update the date but keep the existing hash
else {
pad.atime = +new Date();
contains = true;
continue;
}
}
if (shouldUpdate) {
contains = true;
pad.atime = +new Date();
pad.title = title;
// If the href is different, it means we have a stronger one
if (href !== pad.href) { isStronger = true; }
pad.href = href;
}
}
if (isStronger) {
// If we have a stronger url, remove the possible weaker from the trash.
// If all of the weaker ones were in the trash, add the stronger to ROOT
store.userObject.restoreHref(href);
}
// Add the pad if it does not exist in our drive
if (!contains) {
Store.addPad({
href: href,
title: title,
path: data.path || (store.data && store.data.initialPath)
}, cb);
return;
}
onSync(cb);
};
// Filepicker app
Store.getSecureFilesList = function (query, cb) {
var list = {};
var hashes = [];
var types = query.types;
var where = query.where;
var filter = query.filter || {};
var isFiltered = function (type, data) {
var filtered;
var fType = filter.fileType || [];
if (type === 'file' && fType.length) {
if (!data.fileType) { return true; }
filtered = !fType.some(function (t) {
return data.fileType.indexOf(t) === 0;
});
}
return filtered;
};
store.userObject.getFiles(where).forEach(function (id) {
var data = store.userObject.getFileData(id);
var parsed = Hash.parsePadUrl(data.href);
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) &&
hashes.indexOf(parsed.hash) === -1 &&
!isFiltered(parsed.type, data)) {
hashes.push(parsed.hash);
list[id] = data;
}
});
cb(list);
};
// Messaging (manage friends from the userlist)
var getMessagingCfg = function () {
return {
proxy: store.proxy,
realtime: store.realtime,
network: store.network,
updateMetadata: function () {
postMessage("UPDATE_METADATA");
},
pinPads: Store.pinPads,
friendComplete: function (data, cb) {
postMessage("Q_FRIEND_COMPLETE", data, cb);
},
friendRequest: function (data) {
postMessage("EV_FRIEND_REQUEST", data);
},
};
};
Store.inviteFromUserlist = function (data, cb) {
var messagingCfg = getMessagingCfg();
Messaging.inviteFromUserlist(messagingCfg, data, cb);
};
// Messenger
// Get hashes for the share button
Store.getStrongerHash = function (data, cb) {
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
// If we have a stronger version in drive, add it and add a redirect button
var stronger = Hash.findStronger(data.href, allPads);
if (stronger) {
var parsed2 = Hash.parsePadUrl(stronger);
return void cb(parsed2.hash);
}
cb();
};
Store.messenger = {
getFriendList: function (data, cb) {
store.messenger.getFriendList(function (e, keys) {
cb({
error: e,
data: keys,
});
});
},
getMyInfo: function (data, cb) {
store.messenger.getMyInfo(function (e, info) {
cb({
error: e,
data: info,
});
});
},
getFriendInfo: function (data, cb) {
store.messenger.getFriendInfo(data, function (e, info) {
cb({
error: e,
data: info,
});
});
},
removeFriend: function (data, cb) {
store.messenger.removeFriend(data, function (e, info) {
cb({
error: e,
data: info,
});
});
},
openFriendChannel: function (data, cb) {
store.messenger.openFriendChannel(data, function (e) {
cb({ error: e, });
});
},
getFriendStatus: function (data, cb) {
store.messenger.getStatus(data, function (e, online) {
cb({
error: e,
data: online,
});
});
},
getMoreHistory: function (data, cb) {
store.messenger.getMoreHistory(data.curvePublic, data.sig, data.count, function (e, history) {
cb({
error: e,
data: history,
});
});
},
sendMessage: function (data, cb) {
store.messenger.sendMessage(data.curvePublic, data.content, function (e) {
cb({
error: e,
});
});
},
setChannelHead: function (data, cb) {
store.messenger.setChannelHead(data.curvePublic, data.sig, function (e) {
cb({
error: e
});
});
}
};
var onReady = function (returned, cb) {
var proxy = store.proxy;
var userObject = store.userObject = UserObject.init(proxy.drive, {
pinPads: Store.pinPads,
loggedIn: store.loggedIn
});
var todo = function () {
userObject.fixFiles();
Migrate(proxy);
var requestLogin = function () {
postMessage("REQUEST_LOGIN");
};
if (store.loggedIn) {
/* This isn't truly secure, since anyone who can read the user's object can
set their local loginToken to match that in the object. However, it exposes
a UI that will work most of the time. */
// every user object should have a persistent, random number
if (typeof(proxy.loginToken) !== 'number') {
proxy[Constants.tokenKey] = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
}
returned[Constants.tokenKey] = proxy[Constants.tokenKey];
if (store.data.localToken && store.data.localToken !== proxy[Constants.tokenKey]) {
// the local number doesn't match that in
// the user object, request that they reauthenticate.
return void requestLogin();
}
}
if (!proxy.settings || !proxy.settings.general ||
typeof(proxy.settings.general.allowUserFeedback) !== 'boolean') {
proxy.settings = proxy.settings || {};
proxy.settings.general = proxy.settings.general || {};
proxy.settings.general.allowUserFeedback = true;
}
returned.feedback = proxy.settings.general.allowUserFeedback;
if (typeof(cb) === 'function') { cb(returned); }
if (typeof(proxy.uid) !== 'string' || proxy.uid.length !== 32) {
// even anonymous users should have a persistent, unique-ish id
console.log('generating a persistent identifier');
proxy.uid = Hash.createChannelId();
}
// if the user is logged in, but does not have signing keys...
if (store.loggedIn && (!Store.hasSigningKeys() ||
!Store.hasCurveKeys())) {
return void requestLogin();
}
proxy.on('change', [Constants.displayNameKey], function (o, n) {
if (typeof(n) !== "string") { return; }
postMessage("UPDATE_METADATA");
});
proxy.on('change', ['profile'], function () {
// Trigger userlist update when the avatar has changed
postMessage("UPDATE_METADATA");
});
proxy.on('change', ['friends'], function () {
// Trigger userlist update when the friendlist has changed
postMessage("UPDATE_METADATA");
});
proxy.on('change', ['settings'], function () {
postMessage("UPDATE_METADATA");
});
proxy.on('change', [Constants.tokenKey], function () {
postMessage("UPDATE_TOKEN", { token: proxy[Constants.tokenKey] });
});
};
userObject.migrate(todo);
};
var connect = function (data, cb) {
var hash = data.userHash || data.anonHash || Hash.createRandomHash();
storeHash = hash;
if (!hash) {
throw new Error('[Store.init] Unable to find or create a drive hash. Aborting...');
}
var secret = Hash.getSecrets('drive', hash);
var listmapConfig = {
data: {},
websocketURL: NetConfig.getWebsocketURL(),
channel: secret.channel,
readOnly: false,
validateKey: secret.keys.validateKey || undefined,
crypto: Crypto.createEncryptor(secret.keys),
userName: 'fs',
logLevel: 1,
ChainPad: ChainPad,
classic: true,
};
var rt = Listmap.create(listmapConfig);
store.proxy = rt.proxy;
store.loggedIn = typeof(data.userHash) !== "undefined";
var returned = {};
rt.proxy.on('create', function (info) {
store.realtime = info.realtime;
store.network = info.network;
if (!data.userHash) {
returned.anonHash = Hash.getEditHashFromKeys(info.channel, secret.keys);
}
}).on('ready', function () {
if (store.userObject) { return; } // the store is already ready, it is a reconnection
if (!rt.proxy.drive || typeof(rt.proxy.drive) !== 'object') { rt.proxy.drive = {}; }
var drive = rt.proxy.drive;
// Creating a new anon drive: import anon pads from localStorage
if ((!drive[Constants.oldStorageKey] || !Array.isArray(drive[Constants.oldStorageKey]))
&& !drive['filesData']) {
drive[Constants.oldStorageKey] = [];
}
// Drive already exist: return the existing drive, don't load data from legacy store
onReady(returned, cb);
})
.on('change', ['drive', 'migrate'], function () {
var path = arguments[2];
var value = arguments[1];
if (path[0] === 'drive' && path[1] === "migrate" && value === 1) {
rt.network.disconnect();
rt.realtime.abort();
}
});
};
/**
* Data:
* - userHash or anonHash
* Todo in cb
* - LocalStore.setFSHash if needed
* - sessionStorage.User_Hash
* - stuff with tokenKey
* Event to outer
* - requestLogin
*/
var initialized = false;
Store.init = function (data, callback) {
if (initialized) {
return void callback({
error: 'ALREADY_INIT'
});
}
initialized = true;
postMessage = function (cmd, d, cb) {
setTimeout(function () {
data.query(cmd, d, cb); // TODO temporary, will be replaced by webworker channel
});
};
store.data = data;
connect(data, function (ret) {
if (Object.keys(store.proxy).length === 1) {
Feedback.send("FIRST_APP_USE", true);
}
callback(ret);
var messagingCfg = getMessagingCfg();
Messaging.addDirectMessageHandler(messagingCfg);
if (data.messenger) {
var messenger = store.messenger = Messenger.messenger(store); // TODO
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,
});
});
}
});
};
Store.disconnect = function () {
if (!store.network) { return; }
store.network.disconnect();
};
return Store;
});

@ -0,0 +1,159 @@
define([
'/common/outer/async-store.js'
], function (Store) {
var Rpc = {};
Rpc.query = function (cmd, data, cb) {
switch (cmd) {
// READY
case 'CONNECT': {
Store.init(data, cb); break;
}
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
case 'INIT_RPC': {
Store.initRpc(data, cb); break;
}
case 'UPDATE_PIN_LIMIT': {
Store.updatePinLimit(data, cb); break;
}
case 'GET_PIN_LIMIT': {
Store.getPinLimit(data, cb); break;
}
case 'CLEAR_OWNED_CHANNEL': {
Store.clearOwnedChannel(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_PINNED_USAGE': {
Store.getPinnedUsage(data, cb); break;
}
// ANON RPC
case 'INIT_ANON_RPC': {
Store.initAnonRpc(data, cb); break;
}
case 'ANON_RPC_MESSAGE': {
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
case 'GET': {
Store.get(data, cb); break;
}
case 'SET': {
Store.set(data, cb); break;
}
case 'ADD_PAD': {
Store.addPad(data, cb); break;
}
case 'SET_PAD_TITLE': {
Store.setPadTitle(data, cb); break;
}
case 'MOVE_TO_TRASH': {
Store.moveToTrash(data, cb); break;
}
case 'RESET_DRIVE': {
Store.resetDrive(data, cb); break;
}
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_STRONGER_HASH': {
Store.getStrongerHash(data, cb); break;
}
// Messaging
case 'INVITE_FROM_USERLIST': {
Store.inviteFromUserlist(data, cb); break;
}
// Messenger
case 'CONTACTS_GET_FRIEND_LIST': {
Store.messenger.getFriendList(data, cb); break;
}
case 'CONTACTS_GET_MY_INFO': {
Store.messenger.getMyInfo(data, cb); break;
}
case 'CONTACTS_GET_FRIEND_INFO': {
Store.messenger.getFriendInfo(data, cb); break;
}
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;
}
default: {
break;
}
}
};
return Rpc;
});

@ -20,7 +20,7 @@ define([
var sendChunk = function (box, cb) { var sendChunk = function (box, cb) {
var enc = Nacl.util.encodeBase64(box); var enc = Nacl.util.encodeBase64(box);
common.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) { common.uploadChunk(enc, function (e, msg) {
cb(e, msg); cb(e, msg);
}); });
}; };
@ -58,8 +58,7 @@ define([
if (noStore) { return void onComplete(href); } if (noStore) { return void onComplete(href); }
common.initialPath = path; common.setPadTitle(title || "", href, path, function (err) {
common.renamePad(title || "", href, function (err) {
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
onComplete(href); onComplete(href);
common.setPadAttribute('fileType', metadata.type, null, href); common.setPadAttribute('fileType', metadata.type, null, href);

@ -3,13 +3,13 @@ define([
], function (Rpc) { ], function (Rpc) {
var create = function (network, proxy, cb) { var create = function (network, proxy, cb) {
if (!network) { if (!network) {
window.setTimeout(function () { setTimeout(function () {
cb('INVALID_NETWORK'); cb('INVALID_NETWORK');
}); });
return; return;
} }
if (!proxy) { if (!proxy) {
window.setTimeout(function () { setTimeout(function () {
cb('INVALID_PROXY'); cb('INVALID_PROXY');
}); });
return; return;
@ -19,7 +19,7 @@ define([
var edPublic = proxy.edPublic; var edPublic = proxy.edPublic;
if (!(edPrivate && edPublic)) { if (!(edPrivate && edPublic)) {
window.setTimeout(function () { setTimeout(function () {
cb('INVALID_KEYS'); cb('INVALID_KEYS');
}); });
return; return;
@ -39,7 +39,7 @@ define([
// you can ask the server to pin a particular channel for you // you can ask the server to pin a particular channel for you
exp.pin = function (channels, cb) { exp.pin = function (channels, cb) {
if (!Array.isArray(channels)) { if (!Array.isArray(channels)) {
window.setTimeout(function () { setTimeout(function () {
cb('[TypeError] pin expects an array'); cb('[TypeError] pin expects an array');
}); });
return; return;
@ -50,7 +50,7 @@ define([
// you can also ask to unpin a particular channel // you can also ask to unpin a particular channel
exp.unpin = function (channels, cb) { exp.unpin = function (channels, cb) {
if (!Array.isArray(channels)) { if (!Array.isArray(channels)) {
window.setTimeout(function () { setTimeout(function () {
cb('[TypeError] pin expects an array'); cb('[TypeError] pin expects an array');
}); });
return; return;
@ -71,7 +71,7 @@ define([
// if local and remote hashes don't match, send a reset // if local and remote hashes don't match, send a reset
exp.reset = function (channels, cb) { exp.reset = function (channels, cb) {
if (!Array.isArray(channels)) { if (!Array.isArray(channels)) {
window.setTimeout(function () { setTimeout(function () {
cb('[TypeError] pin expects an array'); cb('[TypeError] pin expects an array');
}); });
return; return;
@ -163,7 +163,7 @@ define([
exp.uploadStatus = function (size, cb) { exp.uploadStatus = function (size, cb) {
if (typeof(size) !== 'number') { if (typeof(size) !== 'number') {
return void window.setTimeout(function () { return void setTimeout(function () {
cb('INVALID_SIZE'); cb('INVALID_SIZE');
}); });
} }

@ -140,7 +140,7 @@ types of messages:
var send = ctx.send = function (type, msg, cb) { var send = ctx.send = function (type, msg, cb) {
if (!ctx.connected && type !== 'COOKIE') { if (!ctx.connected && type !== 'COOKIE') {
return void window.setTimeout(function () { return void setTimeout(function () {
cb('DISCONNECTED'); cb('DISCONNECTED');
}); });
} }
@ -185,7 +185,7 @@ types of messages:
send.unauthenticated = function (type, msg, cb) { send.unauthenticated = function (type, msg, cb) {
if (!ctx.connected) { if (!ctx.connected) {
return void window.setTimeout(function () { return void setTimeout(function () {
cb('DISCONNECTED'); cb('DISCONNECTED');
}); });
} }
@ -276,7 +276,7 @@ types of messages:
var send = ctx.send = function (type, msg, cb) { var send = ctx.send = function (type, msg, cb) {
if (!ctx.connected) { if (!ctx.connected) {
return void window.setTimeout(function () { return void setTimeout(function () {
cb('DISCONNECTED'); cb('DISCONNECTED');
}); });
} }

@ -9,6 +9,7 @@ define([
common.start = function (cfg) { common.start = function (cfg) {
cfg = cfg || {}; cfg = cfg || {};
var realtime = !cfg.noRealtime; var realtime = !cfg.noRealtime;
var network;
var secret; var secret;
var hashes; var hashes;
var CpNfOuter; var CpNfOuter;
@ -18,7 +19,7 @@ define([
var SFrameChannel; var SFrameChannel;
var sframeChan; var sframeChan;
var FilePicker; var FilePicker;
var Messenger; //var Messenger;
var Messaging; var Messaging;
var Notifier; var Notifier;
var Utils = {}; var Utils = {};
@ -32,7 +33,6 @@ define([
'/common/cryptget.js', '/common/cryptget.js',
'/common/sframe-channel.js', '/common/sframe-channel.js',
'/filepicker/main.js', '/filepicker/main.js',
'/common/common-messenger.js',
'/common/common-messaging.js', '/common/common-messaging.js',
'/common/common-notifier.js', '/common/common-notifier.js',
'/common/common-hash.js', '/common/common-hash.js',
@ -41,16 +41,17 @@ 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/network-config.js',
'/bower_components/netflux-websocket/netflux-client.js',
], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel, ], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel,
_FilePicker, _Messenger, _Messaging, _Notifier, _Hash, _Util, _Realtime, _FilePicker, _Messaging, _Notifier, _Hash, _Util, _Realtime,
_Constants, _Feedback, _LocalStore) { _Constants, _Feedback, _LocalStore, NetConfig, Netflux) {
CpNfOuter = _CpNfOuter; CpNfOuter = _CpNfOuter;
Cryptpad = _Cryptpad; Cryptpad = _Cryptpad;
Crypto = _Crypto; Crypto = _Crypto;
Cryptget = _Cryptget; Cryptget = _Cryptget;
SFrameChannel = _SFrameChannel; SFrameChannel = _SFrameChannel;
FilePicker = _FilePicker; FilePicker = _FilePicker;
Messenger = _Messenger;
Messaging = _Messaging; Messaging = _Messaging;
Notifier = _Notifier; Notifier = _Notifier;
Utils.Hash = _Hash; Utils.Hash = _Hash;
@ -84,7 +85,15 @@ define([
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) { SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
sframeChan = sfc; sframeChan = sfc;
}), false, { cache: cache, localStore: localStore, language: Cryptpad.getLanguage() }); }), false, { cache: cache, localStore: localStore, language: Cryptpad.getLanguage() });
Cryptpad.ready(waitFor()); Cryptpad.ready(waitFor(), {
messenger: cfg.messaging
});
if (!cfg.newNetwork) {
Netflux.connect(NetConfig.getWebsocketURL()).then(waitFor(function (nw) {
network = nw;
}));
}
})); }));
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
$('#sbox-iframe').focus(); $('#sbox-iframe').focus();
@ -104,12 +113,23 @@ define([
}); });
}); });
secret = cfg.getSecrets ? cfg.getSecrets(Cryptpad, Utils) : Utils.Hash.getSecrets(); if (cfg.getSecrets) {
var w = waitFor();
cfg.getSecrets(Cryptpad, Utils, waitFor(function (err, s) {
secret = s;
Cryptpad.getShareHashes(secret, function (err, h) {
hashes = h;
w();
});
}));
} else {
secret = Utils.Hash.getSecrets();
if (!secret.channel) { if (!secret.channel) {
// New pad: create a new random channel id // New pad: create a new random channel id
secret.channel = Utils.Hash.createChannelId(); secret.channel = Utils.Hash.createChannelId();
} }
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; })); Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
}
}).nThen(function () { }).nThen(function () {
var readOnly = secret.keys && !secret.keys.editKeyStr; var readOnly = secret.keys && !secret.keys.editKeyStr;
@ -117,59 +137,50 @@ define([
var parsed = Utils.Hash.parsePadUrl(window.location.href); var parsed = Utils.Hash.parsePadUrl(window.location.href);
if (!parsed.type) { throw new Error(); } if (!parsed.type) { throw new Error(); }
var defaultTitle = Utils.Hash.getDefaultName(parsed); var defaultTitle = Utils.Hash.getDefaultName(parsed);
var proxy = Cryptpad.getProxy();
var updateMeta = function () { var updateMeta = function () {
//console.log('EV_METADATA_UPDATE'); //console.log('EV_METADATA_UPDATE');
var name; var metaObj, isTemplate;
nThen(function (waitFor) { nThen(function (waitFor) {
Cryptpad.getLastName(waitFor(function (err, n) { Cryptpad.getMetadata(waitFor(function (err, m) {
if (err) { console.log(err); } if (err) { console.log(err); }
name = n; metaObj = m;
}));
Cryptpad.isTemplate(window.location.href, waitFor(function (err, t) {
if (err) { console.log(err); }
isTemplate = t;
})); }));
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var metaObj = { metaObj.doc = {
doc: {
defaultTitle: defaultTitle, defaultTitle: defaultTitle,
type: parsed.type type: parsed.type
}, };
user: { var additionalPriv = {
name: name,
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: proxy.curvePublic,
netfluxId: Cryptpad.getNetwork().webChannels[0].myID,
},
priv: {
edPublic: proxy.edPublic,
accountName: Utils.LocalStore.getAccountName(), accountName: Utils.LocalStore.getAccountName(),
origin: window.location.origin, origin: window.location.origin,
pathname: window.location.pathname, pathname: window.location.pathname,
fileHost: ApiConfig.fileHost, fileHost: ApiConfig.fileHost,
readOnly: readOnly, readOnly: readOnly,
availableHashes: hashes, availableHashes: hashes,
isTemplate: Cryptpad.isTemplate(window.location.href), isTemplate: isTemplate,
feedbackAllowed: Utils.Feedback.state, feedbackAllowed: Utils.Feedback.state,
friends: proxy.friends || {},
settings: proxy.settings || {},
isPresent: parsed.hashData && parsed.hashData.present, isPresent: parsed.hashData && parsed.hashData.present,
isEmbed: parsed.hashData && parsed.hashData.embed, isEmbed: parsed.hashData && parsed.hashData.embed,
thumbnails: !((proxy.settings || {}).general || {}).disableThumbnails,
accounts: { accounts: {
donateURL: Cryptpad.donateURL, donateURL: Cryptpad.donateURL,
upgradeURL: Cryptpad.upgradeURL upgradeURL: Cryptpad.upgradeURL
} }
}
}; };
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
if (cfg.addData) { if (cfg.addData) {
cfg.addData(metaObj.priv, Cryptpad); cfg.addData(metaObj.priv, Cryptpad);
} }
sframeChan.event('EV_METADATA_UPDATE', metaObj); sframeChan.event('EV_METADATA_UPDATE', metaObj);
}); });
}; };
Cryptpad.onDisplayNameChanged(updateMeta); Cryptpad.onMetadataChanged(updateMeta);
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
proxy.on('change', 'settings', updateMeta);
Utils.LocalStore.onLogout(function () { Utils.LocalStore.onLogout(function () {
sframeChan.event('EV_LOGOUT'); sframeChan.event('EV_LOGOUT');
@ -223,7 +234,7 @@ define([
sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) { sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) {
currentTitle = newTitle; currentTitle = newTitle;
setDocumentTitle(); setDocumentTitle();
Cryptpad.renamePad(newTitle, undefined, function (err) { Cryptpad.setPadTitle(newTitle, undefined, undefined, function (err) {
cb(err); cb(err);
}); });
}); });
@ -241,7 +252,7 @@ define([
cb('ERROR'); cb('ERROR');
return; return;
} }
Cryptpad.changeDisplayName(newName, true); Cryptpad.changeMetadata();
cb(); cb();
}); });
}); });
@ -287,7 +298,6 @@ define([
}; };
sframeChan.on('Q_GET_FULL_HISTORY', function (data, cb) { sframeChan.on('Q_GET_FULL_HISTORY', function (data, cb) {
var network = Cryptpad.getNetwork();
var hkn = network.historyKeeper; var hkn = network.historyKeeper;
var crypto = Crypto.createEncryptor(secret.keys); var crypto = Crypto.createEncryptor(secret.keys);
// Get the history messages and send them to the iframe // Get the history messages and send them to the iframe
@ -445,8 +455,9 @@ define([
Cryptpad.useTemplate(href, Cryptget, cb); Cryptpad.useTemplate(href, Cryptget, cb);
}); });
sframeChan.on('Q_TEMPLATE_EXIST', function (type, cb) { sframeChan.on('Q_TEMPLATE_EXIST', function (type, cb) {
var hasTemplate = Cryptpad.listTemplates(type).length > 0; Cryptpad.listTemplates(type, function (err, templates) {
cb(hasTemplate); cb(templates.length > 0);
});
}); });
sframeChan.on('EV_GOTO_URL', function (url) { sframeChan.on('EV_GOTO_URL', function (url) {
@ -494,117 +505,58 @@ define([
} }
if (cfg.messaging) { if (cfg.messaging) {
var messenger = Messenger.messenger(Cryptpad);
sframeChan.on('Q_CONTACTS_GET_FRIEND_LIST', function (data, cb) { sframeChan.on('Q_CONTACTS_GET_FRIEND_LIST', function (data, cb) {
messenger.getFriendList(function (e, keys) { Cryptpad.messenger.getFriendList(cb);
cb({
error: e,
data: keys,
});
});
}); });
sframeChan.on('Q_CONTACTS_GET_MY_INFO', function (data, cb) { sframeChan.on('Q_CONTACTS_GET_MY_INFO', function (data, cb) {
messenger.getMyInfo(function (e, info) { Cryptpad.messenger.getMyInfo(cb);
cb({
error: e,
data: info,
});
});
}); });
sframeChan.on('Q_CONTACTS_GET_FRIEND_INFO', function (curvePublic, cb) { sframeChan.on('Q_CONTACTS_GET_FRIEND_INFO', function (curvePublic, cb) {
messenger.getFriendInfo(curvePublic, function (e, info) { Cryptpad.messenger.getFriendInfo(curvePublic, cb);
cb({
error: e,
data: info,
});
});
}); });
sframeChan.on('Q_CONTACTS_REMOVE_FRIEND', function (curvePublic, cb) { sframeChan.on('Q_CONTACTS_REMOVE_FRIEND', function (curvePublic, cb) {
messenger.removeFriend(curvePublic, function (e, info) { Cryptpad.messenger.removeFriend(curvePublic, cb);
cb({
error: e,
data: info,
});
});
}); });
sframeChan.on('Q_CONTACTS_OPEN_FRIEND_CHANNEL', function (curvePublic, cb) { sframeChan.on('Q_CONTACTS_OPEN_FRIEND_CHANNEL', function (curvePublic, cb) {
messenger.openFriendChannel(curvePublic, function (e) { Cryptpad.messenger.openFriendChannel(curvePublic, cb);
cb({ error: e, });
});
}); });
sframeChan.on('Q_CONTACTS_GET_STATUS', function (curvePublic, cb) { sframeChan.on('Q_CONTACTS_GET_STATUS', function (curvePublic, cb) {
messenger.getStatus(curvePublic, function (e, online) { Cryptpad.messenger.getFriendStatus(curvePublic, cb);
cb({
error: e,
data: online,
});
});
}); });
sframeChan.on('Q_CONTACTS_GET_MORE_HISTORY', function (opt, cb) { sframeChan.on('Q_CONTACTS_GET_MORE_HISTORY', function (opt, cb) {
messenger.getMoreHistory(opt.curvePublic, opt.sig, opt.count, function (e, history) { Cryptpad.messenger.getMoreHistory(opt, cb);
cb({
error: e,
data: history,
});
});
}); });
sframeChan.on('Q_CONTACTS_SEND_MESSAGE', function (opt, cb) { sframeChan.on('Q_CONTACTS_SEND_MESSAGE', function (opt, cb) {
messenger.sendMessage(opt.curvePublic, opt.content, function (e) { Cryptpad.messenger.sendMessage(opt, cb);
cb({
error: e,
});
});
}); });
sframeChan.on('Q_CONTACTS_SET_CHANNEL_HEAD', function (opt, cb) { sframeChan.on('Q_CONTACTS_SET_CHANNEL_HEAD', function (opt, cb) {
messenger.setChannelHead(opt.curvePublic, opt.sig, function (e) { Cryptpad.messenger.setChannelHead(opt, cb);
cb({
error: e
});
});
}); });
sframeChan.on('Q_CONTACTS_CLEAR_OWNED_CHANNEL', function (channel, cb) { sframeChan.on('Q_CONTACTS_CLEAR_OWNED_CHANNEL', function (channel, cb) {
messenger.clearOwnedChannel(channel, function (e) { Cryptpad.clearOwnedChannel(channel, cb);
cb({
error: e,
});
});
}); });
messenger.on('message', function (message) { Cryptpad.messenger.onMessageEvent.reg(function (data) {
sframeChan.event('EV_CONTACTS_MESSAGE', message); sframeChan.event('EV_CONTACTS_MESSAGE', data);
});
messenger.on('join', function (curvePublic, channel) {
sframeChan.event('EV_CONTACTS_JOIN', {
curvePublic: curvePublic,
channel: channel,
});
});
messenger.on('leave', function (curvePublic, channel) {
sframeChan.event('EV_CONTACTS_LEAVE', {
curvePublic: curvePublic,
channel: channel,
});
});
messenger.on('update', function (info, curvePublic) {
sframeChan.event('EV_CONTACTS_UPDATE', {
curvePublic: curvePublic,
info: info,
}); });
Cryptpad.messenger.onJoinEvent.reg(function (data) {
sframeChan.event('EV_CONTACTS_JOIN', data);
}); });
messenger.on('friend', function (curvePublic) { Cryptpad.messenger.onLeaveEvent.reg(function (data) {
sframeChan.event('EV_CONTACTS_FRIEND', { sframeChan.event('EV_CONTACTS_LEAVE', data);
curvePublic: curvePublic,
}); });
Cryptpad.messenger.onUpdateEvent.reg(function (data) {
sframeChan.event('EV_CONTACTS_UPDATE', data);
}); });
messenger.on('unfriend', function (curvePublic) { Cryptpad.messenger.onFriendEvent.reg(function (data) {
sframeChan.event('EV_CONTACTS_UNFRIEND', { sframeChan.event('EV_CONTACTS_FRIEND', data);
curvePublic: curvePublic,
}); });
Cryptpad.messenger.onUnfriendEvent.reg(function (data) {
sframeChan.event('EV_CONTACTS_UNFRIEND', data);
}); });
} }
@ -629,7 +581,7 @@ define([
CpNfOuter.start({ CpNfOuter.start({
sframeChan: sframeChan, sframeChan: sframeChan,
channel: secret.channel, channel: secret.channel,
network: cfg.newNetwork || Cryptpad.getNetwork(), network: cfg.newNetwork || network,
validateKey: secret.keys.validateKey || undefined, validateKey: secret.keys.validateKey || undefined,
readOnly: readOnly, readOnly: readOnly,
crypto: Crypto.createEncryptor(secret.keys), crypto: Crypto.createEncryptor(secret.keys),

@ -328,7 +328,7 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
SFrameChannel.create(window.parent, waitFor(function (sfc) { ctx.sframeChan = sfc; }), true); SFrameChannel.create(window.parent, waitFor(function (sfc) { ctx.sframeChan = sfc; }), true);
// CpNfInner.start() should be here.... // CpNfInner.start() should be here....
}).nThen(function () { }).nThen(function (waitFor) {
localForage.clear(); localForage.clear();
ctx.metadataMgr = MetadataMgr.create(ctx.sframeChan); ctx.metadataMgr = MetadataMgr.create(ctx.sframeChan);
@ -373,10 +373,6 @@ define([
}); });
}); });
ctx.sframeChan.on('EV_RT_CONNECT', function () { CommonRealtime.setConnectionState(true); });
ctx.sframeChan.on('EV_RT_DISCONNECT', function () { CommonRealtime.setConnectionState(false); });
ctx.sframeChan.on('Q_INCOMING_FRIEND_REQUEST', function (confirmMsg, cb) { ctx.sframeChan.on('Q_INCOMING_FRIEND_REQUEST', function (confirmMsg, cb) {
UI.confirm(confirmMsg, cb, null, true); UI.confirm(confirmMsg, cb, null, true);
}); });
@ -393,6 +389,8 @@ define([
} catch (e) { Feedback.init(false); } } catch (e) { Feedback.init(false); }
}); });
ctx.metadataMgr.onReady(waitFor());
}).nThen(function () {
ctx.sframeChan.ready(); ctx.sframeChan.ready();
cb(funcs); cb(funcs);
}); });

@ -1,12 +1,11 @@
define([ define([
'jquery',
'/customize/application_config.js', '/customize/application_config.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-hash.js', '/common/common-hash.js',
'/common/common-realtime.js', '/common/common-realtime.js',
'/common/common-constants.js', '/common/common-constants.js',
'/customize/messages.js' '/customize/messages.js'
], function ($, AppConfig, Util, Hash, Realtime, Constants, Messages) { ], function (AppConfig, Util, Hash, Realtime, Constants, Messages) {
var module = {}; var module = {};
var ROOT = module.ROOT = "root"; var ROOT = module.ROOT = "root";
@ -21,13 +20,13 @@ define([
module.init = function (files, config) { module.init = function (files, config) {
var exp = {}; var exp = {};
var Cryptpad = config.Cryptpad; var pinPads = config.pinPads;
var loggedIn = config.loggedIn; var loggedIn = config.loggedIn;
var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Constants.storageKey; var FILES_DATA = module.FILES_DATA = exp.FILES_DATA = Constants.storageKey;
var OLD_FILES_DATA = module.OLD_FILES_DATA = exp.OLD_FILES_DATA = Constants.oldStorageKey; var OLD_FILES_DATA = module.OLD_FILES_DATA = exp.OLD_FILES_DATA = Constants.oldStorageKey;
var NEW_FOLDER_NAME = Messages.fm_newFolder; var NEW_FOLDER_NAME = Messages.fm_newFolder || 'New folder';
var NEW_FILE_NAME = Messages.fm_newFile; var NEW_FILE_NAME = Messages.fm_newFile || 'New file';
exp.ROOT = ROOT; exp.ROOT = ROOT;
exp.UNSORTED = UNSORTED; exp.UNSORTED = UNSORTED;
@ -101,7 +100,7 @@ define([
}; };
for (var f in element) { for (var f in element) {
if (trashRoot) { if (trashRoot) {
if ($.isArray(element[f])) { if (Array.isArray(element[f])) {
element[f].forEach(addSubfolder); element[f].forEach(addSubfolder);
} }
} else { } else {
@ -119,7 +118,7 @@ define([
}; };
for (var f in element) { for (var f in element) {
if (trashRoot) { if (trashRoot) {
if ($.isArray(element[f])) { if (Array.isArray(element[f])) {
element[f].forEach(addFile); element[f].forEach(addFile);
} }
} else { } else {
@ -148,14 +147,14 @@ define([
return data.filename || data.title || NEW_FILE_NAME; return data.filename || data.title || NEW_FILE_NAME;
}; };
exp.getPadAttribute = function (href, attr, cb) { exp.getPadAttribute = function (href, attr, cb) {
cb = cb || $.noop; cb = cb || function () {};
var id = exp.getIdFromHref(href); var id = exp.getIdFromHref(href);
if (!id) { return void cb(null, undefined); } if (!id) { return void cb(null, undefined); }
var data = getFileData(id); var data = getFileData(id);
cb(null, clone(data[attr])); cb(null, clone(data[attr]));
}; };
exp.setPadAttribute = function (href, attr, value, cb) { exp.setPadAttribute = function (href, attr, value, cb) {
cb = cb || $.noop; cb = cb || function () {};
var id = exp.getIdFromHref(href); var id = exp.getIdFromHref(href);
if (!id) { return void cb("E_INVAL_HREF"); } if (!id) { return void cb("E_INVAL_HREF"); }
if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); } if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); }
@ -167,7 +166,7 @@ define([
// PATHS // PATHS
var comparePath = exp.comparePath = function (a, b) { var comparePath = exp.comparePath = function (a, b) {
if (!a || !b || !$.isArray(a) || !$.isArray(b)) { return false; } if (!a || !b || !Array.isArray(a) || !Array.isArray(b)) { return false; }
if (a.length !== b.length) { return false; } if (a.length !== b.length) { return false; }
var result = true; var result = true;
var i = a.length - 1; var i = a.length - 1;
@ -265,7 +264,7 @@ define([
} }
}; };
for (var e in root) { for (var e in root) {
if (!$.isArray(root[e])) { if (!Array.isArray(root[e])) {
error("Trash contains a non-array element"); error("Trash contains a non-array element");
return; return;
} }
@ -487,8 +486,7 @@ define([
// FILES DATA // FILES DATA
exp.pushData = function (data, cb) { exp.pushData = function (data, cb) {
// TODO: can only be called from outside atm if (!pinPads) { return; }
if (!Cryptpad) { return; }
if (typeof cb !== "function") { cb = function () {}; } if (typeof cb !== "function") { cb = function () {}; }
var todo = function () { var todo = function () {
var id = Util.createRandomInteger(); var id = Util.createRandomInteger();
@ -498,8 +496,8 @@ define([
if (!loggedIn || !AppConfig.enablePinning || config.testMode) { if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
return void todo(); return void todo();
} }
Cryptpad.pinPads([Hash.hrefToHexChannelId(data.href)], function (e) { pinPads([Hash.hrefToHexChannelId(data.href)], function (obj) {
if (e) { return void cb(e); } if (obj && obj.error) { return void cb(obj.error); }
todo(); todo();
}); });
}; };
@ -968,7 +966,7 @@ define([
var addToClean = function (obj, idx, el) { var addToClean = function (obj, idx, el) {
if (typeof(obj) !== "object") { toClean.push(idx); return; } if (typeof(obj) !== "object") { toClean.push(idx); return; }
if (!isFile(obj.element, true) && !isFolder(obj.element)) { toClean.push(idx); return; } if (!isFile(obj.element, true) && !isFolder(obj.element)) { toClean.push(idx); return; }
if (!$.isArray(obj.path)) { toClean.push(idx); return; } if (!Array.isArray(obj.path)) { toClean.push(idx); return; }
if (typeof obj.element === "string") { if (typeof obj.element === "string") {
// We have an old file (href) which is not in filesData: add it // We have an old file (href) which is not in filesData: add it
var id = Util.createRandomInteger(); var id = Util.createRandomInteger();

@ -1,10 +1,8 @@
define([ define([
'jquery', 'jquery',
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/bower_components/textpatcher/TextPatcher.js',
'/common/toolbar3.js', '/common/toolbar3.js',
'json.sortify', 'json.sortify',
'/bower_components/chainpad-json-validator/json-ot.js',
'/common/common-util.js', '/common/common-util.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
@ -22,10 +20,8 @@ define([
], function ( ], function (
$, $,
Crypto, Crypto,
TextPatcher,
Toolbar, Toolbar,
JSONSortify, JSONSortify,
JsonOT,
Util, Util,
nThen, nThen,
SFCommon, SFCommon,
@ -61,7 +57,6 @@ define([
var config = APP.config = { var config = APP.config = {
readOnly: readOnly, readOnly: readOnly,
transformFunction: JsonOT.validate,
// cryptpad debug logging (default is 1) // cryptpad debug logging (default is 1)
// logLevel: 0, // logLevel: 0,
validateContent: function (content) { validateContent: function (content) {
@ -123,11 +118,7 @@ define([
config.onReady = function (info) { config.onReady = function (info) {
if (APP.realtime !== info.realtime) { if (APP.realtime !== info.realtime) {
var realtime = APP.realtime = info.realtime; APP.realtime = info.realtime;
APP.patchText = TextPatcher.create({
realtime: realtime,
//logging: true
});
} }
var userDoc = APP.realtime.getUserDoc(); var userDoc = APP.realtime.getUserDoc();

@ -5,9 +5,7 @@ define([
'jquery', 'jquery',
'/common/requireconfig.js', '/common/requireconfig.js',
'/common/sframe-common-outer.js', '/common/sframe-common-outer.js',
'/common/outer/network-config.js', ], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) {
'/bower_components/netflux-websocket/netflux-client.js',
], function (nThen, ApiConfig, $, RequireConfig, SFCommonO, NetConfig, Netflux) {
var requireConfig = RequireConfig(); var requireConfig = RequireConfig();
// Loaded in load #2 // Loaded in load #2
@ -38,10 +36,10 @@ define([
}; };
window.addEventListener('message', onMsg); window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad, Utils) { var getSecrets = function (Cryptpad, Utils, cb) {
var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() || var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() ||
Utils.LocalStore.getFSHash(); Utils.LocalStore.getFSHash();
return Utils.Hash.getSecrets('drive', hash); cb(null, Utils.Hash.getSecrets('drive', hash));
}; };
var addRpc = function (sframeChan, Cryptpad, Utils) { var addRpc = function (sframeChan, Cryptpad, Utils) {
sframeChan.on('EV_BURN_ANON_DRIVE', function () { sframeChan.on('EV_BURN_ANON_DRIVE', function () {
@ -51,13 +49,13 @@ define([
window.location.reload(); window.location.reload();
}); });
}; };
Netflux.connect(NetConfig.getWebsocketURL()).then(function (network) { //Netflux.connect(NetConfig.getWebsocketURL()).then(function (network) {
SFCommonO.start({ SFCommonO.start({
getSecrets: getSecrets, getSecrets: getSecrets,
newNetwork: network, //newNetwork: network,
noHash: true, noHash: true,
addRpc: addRpc addRpc: addRpc
}); });
}, function (err) { console.error(err); }); //}, function (err) { console.error(err); });
}); });
}); });

@ -70,7 +70,7 @@ define([
module.test = function (assert) { module.test = function (assert) {
var config = { var config = {
Cryptpad: Cryptpad, pinPads: Cryptpad.pinPads,
workgroup: false, workgroup: false,
testMode: true, testMode: true,
loggedIn: false loggedIn: false

@ -120,7 +120,6 @@ define([
decrypted.callback(); decrypted.callback();
} }
console.log(decrypted);
$dlview.show(); $dlview.show();
$dlform.hide(); $dlform.hide();
var $dlButton = $dlview.find('media-tag button'); var $dlButton = $dlview.find('media-tag button');
@ -174,7 +173,6 @@ define([
var progress = e.originalEvent; var progress = e.originalEvent;
var p = progress.percent +'%'; var p = progress.percent +'%';
$progress.width(p); $progress.width(p);
console.log(progress.percent);
}); });
/** /**

@ -46,41 +46,30 @@ define([
sframeChan = sfc; sframeChan = sfc;
})); }));
}).nThen(function () { }).nThen(function () {
var proxy = Cryptpad.getProxy();
var updateMeta = function () { var updateMeta = function () {
//console.log('EV_METADATA_UPDATE'); //console.log('EV_METADATA_UPDATE');
var name; var metaObj;
nThen(function (waitFor) { nThen(function (waitFor) {
Cryptpad.getLastName(waitFor(function (err, n) { Cryptpad.getMetadata(waitFor(function (err, n) {
if (err) { console.log(err); } if (err) { console.log(err); }
name = n; metaObj = n;
})); }));
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
sframeChan.event('EV_METADATA_UPDATE', { metaObj.doc = {};
doc: {}, var additionalPriv = {
user: {
name: name,
uid: Cryptpad.getUid(),
avatar: Cryptpad.getAvatarUrl(),
profile: Cryptpad.getProfileUrl(),
curvePublic: proxy.curvePublic,
netfluxId: Cryptpad.getNetwork().webChannels[0].myID,
},
priv: {
accountName: Utils.LocalStore.getAccountName(), accountName: Utils.LocalStore.getAccountName(),
origin: window.location.origin, origin: window.location.origin,
pathname: window.location.pathname, pathname: window.location.pathname,
feedbackAllowed: Utils.Feedback.state, feedbackAllowed: Utils.Feedback.state,
friends: proxy.friends || {},
settings: proxy.settings || {},
types: config.types types: config.types
} };
}); for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
sframeChan.event('EV_METADATA_UPDATE', metaObj);
}); });
}; };
Cryptpad.onDisplayNameChanged(updateMeta); Cryptpad.onMetadataChanged(updateMeta);
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
proxy.on('change', 'settings', updateMeta);
config.addCommonRpc(sframeChan); config.addCommonRpc(sframeChan);

@ -395,15 +395,6 @@ define([
var onReady = function () { var onReady = function () {
APP.$container.find('#'+CREATE_ID).remove(); APP.$container.find('#'+CREATE_ID).remove();
/*var obj = APP.lm && APP.lm.proxy;
if (!APP.readOnly) {
var pubKeys = Cryptpad.getPublicKeys();
if (pubKeys && pubKeys.curve) {
obj.curveKey = pubKeys.curve;
obj.edKey = pubKeys.ed;
}
}*/
if (!APP.initialized) { if (!APP.initialized) {
var $header = $('<div>', {id: HEADER_ID}).appendTo(APP.$rightside); var $header = $('<div>', {id: HEADER_ID}).appendTo(APP.$rightside);
addAvatar($header); addAvatar($header);

@ -36,19 +36,24 @@ define([
}; };
window.addEventListener('message', onMsg); window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad, Utils) { var getSecrets = function (Cryptpad, Utils, cb) {
var Hash = Utils.Hash; var Hash = Utils.Hash;
// 1st case: visiting someone else's profile with hash in the URL // 1st case: visiting someone else's profile with hash in the URL
if (window.location.hash) { if (window.location.hash) {
return Hash.getSecrets('profile', window.location.hash.slice(1)); return void cb(null, Hash.getSecrets('profile', window.location.hash.slice(1)));
} }
var editHash;
nThen(function (waitFor) {
// 2nd case: visiting our own existing profile // 2nd case: visiting our own existing profile
var obj = Cryptpad.getProxy(); Cryptpad.getProfileEditUrl(waitFor(function (hash) {
if (obj.profile && obj.profile.view && obj.profile.edit) { editHash = hash;
return Hash.getSecrets('profile', obj.profile.edit); }));
}).nThen(function () {
if (editHash) {
return void cb(null, Hash.getSecrets('profile', editHash));
} }
// 3rd case: profile creation (create a new random hash, store it later if needed) // 3rd case: profile creation (create a new random hash, store it later if needed)
if (!Utils.LocalStore.isLoggedIn()) { return; } if (!Utils.LocalStore.isLoggedIn()) { return void cb(); }
var hash = Hash.createRandomHash(); var hash = Hash.createRandomHash();
var secret = Hash.getSecrets('profile', hash); var secret = Hash.getSecrets('profile', hash);
Cryptpad.pinPads([secret.channel], function (e) { Cryptpad.pinPads([secret.channel], function (e) {
@ -59,11 +64,13 @@ define([
return; return;
//return void UI.log(Messages._getKey('profile_error', [e])) // TODO //return void UI.log(Messages._getKey('profile_error', [e])) // TODO
} }
obj.profile = {}; var profile = {};
obj.profile.edit = Utils.Hash.getEditHashFromKeys(secret.channel, secret.keys); profile.edit = Utils.Hash.getEditHashFromKeys(secret.channel, secret.keys);
obj.profile.view = Utils.Hash.getViewHashFromKeys(secret.channel, secret.keys); profile.view = Utils.Hash.getViewHashFromKeys(secret.channel, secret.keys);
Cryptpad.setNewProfile(profile);
});
cb(null, secret);
}); });
return secret;
}; };
var addRpc = function (sframeChan, Cryptpad, Utils) { var addRpc = function (sframeChan, Cryptpad, Utils) {
// Adding a new avatar from the profile: pin it and store it in the object // Adding a new avatar from the profile: pin it and store it in the object
@ -71,18 +78,14 @@ define([
var chanId = Utils.Hash.hrefToHexChannelId(data); var chanId = Utils.Hash.hrefToHexChannelId(data);
Cryptpad.pinPads([chanId], function (e) { Cryptpad.pinPads([chanId], function (e) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
Cryptpad.getProxy().profile.avatar = data; Cryptpad.setAvatar(data, cb);
Utils.Realtime.whenRealtimeSyncs(Cryptpad.getRealtime(), function () {
cb();
});
}); });
}); });
// Removing the avatar from the profile: unpin it // Removing the avatar from the profile: unpin it
sframeChan.on('Q_PROFILE_AVATAR_REMOVE', function (data, cb) { sframeChan.on('Q_PROFILE_AVATAR_REMOVE', function (data, cb) {
var chanId = Utils.Hash.hrefToHexChannelId(data); var chanId = Utils.Hash.hrefToHexChannelId(data);
Cryptpad.unpinPads([chanId], function (e) { Cryptpad.unpinPads([chanId], function () {
delete Cryptpad.getProxy().profile.avatar; Cryptpad.setAvatar(undefined, cb);
cb(e);
}); });
}); });
}; };

@ -43,7 +43,7 @@ define([
}); });
}); });
sframeChan.on('Q_SETTINGS_DRIVE_GET', function (d, cb) { sframeChan.on('Q_SETTINGS_DRIVE_GET', function (d, cb) {
cb(Cryptpad.getProxy()); Cryptpad.getUserObject(cb);
}); });
sframeChan.on('Q_SETTINGS_DRIVE_SET', function (data, cb) { sframeChan.on('Q_SETTINGS_DRIVE_SET', function (data, cb) {
var sjson = JSON.stringify(data); var sjson = JSON.stringify(data);
@ -57,26 +57,13 @@ define([
}); });
}); });
sframeChan.on('Q_SETTINGS_DRIVE_RESET', function (data, cb) { sframeChan.on('Q_SETTINGS_DRIVE_RESET', function (data, cb) {
var proxy = Cryptpad.getProxy(); Cryptpad.resetDrive(cb);
var realtime = Cryptpad.getRealtime();
proxy.drive = Cryptpad.getStore().getEmptyObject();
Utils.Realtime.whenRealtimeSyncs(realtime, cb);
}); });
sframeChan.on('Q_SETTINGS_LOGOUT', function (data, cb) { sframeChan.on('Q_SETTINGS_LOGOUT', function (data, cb) {
var proxy = Cryptpad.getProxy(); Cryptpad.logoutFromAll(cb);
var realtime = Cryptpad.getRealtime();
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
localStorage.setItem('loginToken', token);
proxy.loginToken = token;
Utils.Realtime.whenRealtimeSyncs(realtime, cb);
}); });
sframeChan.on('Q_SETTINGS_IMPORT_LOCAL', function (data, cb) { sframeChan.on('Q_SETTINGS_IMPORT_LOCAL', function (data, cb) {
var proxyData = Cryptpad.getStore().getProxy(); Cryptpad.mergeAnonDrive(cb);
require([
'/common/mergeDrive.js',
], function (Merge) {
Merge.anonDriveIntoUser(proxyData, Utils.LocalStore.getFSHash(), cb);
});
}); });
}; };
SFCommonO.start({ SFCommonO.start({

@ -36,12 +36,12 @@ define([
}; };
window.addEventListener('message', onMsg); window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad, Utils) { var getSecrets = function (Cryptpad, Utils, cb) {
var proxy = Cryptpad.getProxy(); Cryptpad.getTodoHash(function (hash) {
var hash = proxy.todo || Utils.Hash.createRandomHash(); var nHash = hash || Utils.Hash.createRandomHash();
if (!proxy.todo) { proxy.todo = hash; } if (!hash) { Cryptpad.setTodoHash(nHash); }
cb(null, Utils.Hash.getSecrets('todo', hash));
return Utils.Hash.getSecrets('todo', hash); });
}; };
SFCommonO.start({ SFCommonO.start({
getSecrets: getSecrets, getSecrets: getSecrets,

Loading…
Cancel
Save