Async store part 1

pull/1/head
yflory 7 years ago
parent 96cf83af34
commit 0840570fbf

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

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

@ -626,10 +626,11 @@ define([
});
};
// TODO displayName
messenger.getMyInfo = function (cb) {
cb(void 0, {
curvePublic: proxy.curvePublic,
displayName: common.getDisplayName(),
displayName: '' //common.getDisplayName(),
});
};

@ -1,17 +1,17 @@
define([
'/customize/application_config.js',
'/customize/messages.js',
'/common/common-interface.js',
], function (AppConfig, Messages, UI) {
//'/customize/application_config.js',
//'/customize/messages.js',
//'/common/common-interface.js',
], function (/*AppConfig, Messages, UI*/) {
var common = {};
common.infiniteSpinnerDetected = false;
var BAD_STATE_TIMEOUT = typeof(AppConfig.badStateTimeout) === 'number'?
AppConfig.badStateTimeout: 30000;
//common.infiniteSpinnerDetected = false;
//var BAD_STATE_TIMEOUT = typeof(AppConfig.badStateTimeout) === 'number'?
// AppConfig.badStateTimeout: 30000;
var connected = false;
var intr;
var infiniteSpinnerHandlers = [];
//var connected = false;
//var intr;
//var infiniteSpinnerHandlers = [];
/*
TODO make this not blow up when disconnected or lagging...
@ -20,7 +20,7 @@ define([
if (typeof(realtime.getAuthDoc) !== 'function') {
return void console.error('improper use of this function');
}
window.setTimeout(function () {
self.setTimeout(function () {
if (realtime.getAuthDoc() === realtime.getUserDoc()) {
return void cb();
} else {
@ -29,6 +29,7 @@ define([
}, 0);
};
/*
common.beginDetectingInfiniteSpinner = function (realtime) {
if (intr) { return; }
intr = window.setInterval(function () {
@ -52,13 +53,14 @@ define([
common.infiniteSpinnerDetected = true;
}, 2000);
};
*/
common.onInfiniteSpinner = function (f) { infiniteSpinnerHandlers.push(f); };
//common.onInfiniteSpinner = function (f) { infiniteSpinnerHandlers.push(f); };
common.setConnectionState = function (bool) {
/*common.setConnectionState = function (bool) {
if (typeof(bool) !== 'boolean') { return; }
connected = bool;
};
};*/
return common;
});

@ -73,12 +73,12 @@ define([
realtime.contentUpdate(doc);
var to = window.setTimeout(function () {
var to = self.setTimeout(function () {
cb(new Error("Timeout"));
}, 5000);
Realtime.whenRealtimeSyncs(realtime, function () {
window.clearTimeout(to);
self.clearTimeout(to);
realtime.abort();
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 fo = exp.fo = FO.init(proxy.drive, {
Cryptpad: Cryptpad,
var fo = exp.userObject = exp.fo = FO.init(proxy.drive, {
pinPads: Cryptpad.pinPads,
loggedIn: LocalStore.isLoggedIn()
});
var todo = function () {
@ -265,15 +265,15 @@ define([
proxy.on('change', [Constants.displayNameKey], function (o, n) {
if (typeof(n) !== "string") { return; }
Cryptpad.changeDisplayName(n);
Cryptpad.changeMetadata();
});
proxy.on('change', ['profile'], function () {
// Trigger userlist update when the avatar has changed
Cryptpad.changeDisplayName(proxy[Constants.displayNameKey]);
Cryptpad.changeMetadata();
});
proxy.on('change', ['friends'], function () {
// Trigger userlist update when the avatar has changed
Cryptpad.changeDisplayName(proxy[Constants.displayNameKey]);
// Trigger userlist update when the friendlist has changed
Cryptpad.changeMetadata();
});
proxy.on('change', [tokenKey], function () {
var localToken = tryParsing(localStorage.getItem(tokenKey));
@ -315,6 +315,7 @@ define([
exp.realtime = rt.realtime;
exp.proxy = rt.proxy;
exp.loggedIn = Cryptpad.isLoggedIn()
rt.proxy.on('create', function (info) {
exp.info = info;
if (!LocalStore.getUserHash()) {

@ -2,8 +2,8 @@ define([
'/common/cryptget.js',
'/common/userObject.js',
'/common/common-hash.js',
'/common/outer/local-store.js',
], function (Crypt, FO, Hash, LocalStore) {
'/common/common-realtime.js',
], function (Crypt, FO, Hash, Realtime) {
var exp = {};
var getType = function (el) {
@ -86,7 +86,7 @@ define([
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
if (!fsHash || !LocalStore.isLoggedIn()) {
if (!fsHash || !proxyData.loggedIn) {
if (typeof(cb) === "function") { return void 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) {
var proxy = proxyData.proxy;
var oldFo = FO.init(parsed.drive, {
loggedIn: LocalStore.isLoggedIn()
loggedIn: proxyData.loggedIn
});
var onMigrated = function () {
oldFo.fixFiles();
var newFo = proxyData.fo;
var newFo = proxyData.userObject;
var oldRecentPads = parsed.drive[newFo.FILES_DATA];
var newRecentPads = proxy.drive[newFo.FILES_DATA];
var oldFiles = oldFo.getFiles([newFo.FILES_DATA]);
@ -154,7 +154,9 @@ define([
proxy.FS_hashes = [];
}
proxy.FS_hashes.push(fsHash);
if (typeof(cb) === "function") { cb(); }
if (typeof(cb) === "function") {
Realtime.whenRealtimeSyncs(proxyData.realtime, cb);
}
};
oldFo.migrate(onMigrated);
return;

@ -0,0 +1,766 @@
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/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, NetConfig,
Crypto, ChainPad, Listmap) {
var Store = {};
var postMessage = function (cmd, data, cb) {};
var tryParsing = function (x) {
try { return JSON.parse(x); }
catch (e) {
console.error(e);
return null;
}
};
var storeHash;
var store = {};
var onSync = function (cb) {
Realtime.whenRealtimeSyncs(store.realtime, cb);
};
Store.get = function (key, cb) {
cb({result: 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'}); }
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); }
}
// TODO
/*if (store.proxy.friends) {
var fList = Messaging.getFriendChannelsList(common);
list = list.concat(fList);
}*/
list.push(Util.base64ToHex(userChannel));
list.sort();
return list;
};
var getCanonicalChannelList = function () {
return Util.deduplicateString(getUserChannelList()).sort();
};
//////////////////////////////////////////////////////////////////
/////////////////////// RPC //////////////////////////////////////
//////////////////////////////////////////////////////////////////
Store.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({error: e}); }
cb({synced: hash === local});
});
};
Store.resetPins = function (data, 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({error: e}); }
cb({hash: hash});
});
};
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.pads, 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.pads, 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 (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 (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'}); }
rpc.clearOwnedChannel(data.channel, cb);
};
Store.uploadComplete = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
rpc.uploadComplete(cb);
};
Store.uploadStatus = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
rpc.uploadStatus(data.size, cb);
};
Store.uploadCancel = function (data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
rpc.uploadCancel(cb);
};
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;
common.getPinLimit(function (e, limit, plan, note) {
if (e) { return void console.error(e); }
common.account.limit = limit;
localStorage.plan = common.account.plan = plan;
common.account.note = note;
cb();
});
common.arePinsSynced(function (err, yes) {
if (!yes) {
common.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, cb);
};
Store.getFileSize = function (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,
netfluxId: store.network.webChannels[0].myID
},
// "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)));
};
// Reset the drive part of the userObject (from settings)
Store.resetDrive = function (data, cb) {
store.proxy.drive = store.fo.getStructure();
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: storeObj.settings,
key: attr
};
}
if (!Array.isArray(attr)) { throw new Error("Attribute must be string or array"); }
if (attr.length === 0) { throw new Error("Attribute can't be empty"); }
var obj = storeObj.settings;
attr.forEach(function (el, i) {
if (i === attr.length-1) { return; }
if (!obj[el]) {
obj[el] = {};
}
else if (typeof obj[el] !== "object") { throw new 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);
};
/**
* Settings & pad attributes
* data
* - href (String)
* - attr (Array)
* - value (String)
*/
Store.setPadAttribute = function (data, cb) {
store.userObject.setPadAttribute(data.href, date.attr, data.value, function () {
onSync(cb);
});
};
Store.getPadAttribute = function (data, cb) {
filesOp.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);
};
var makePad = common.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}); }
store.userObject.add(id, ['template']);
onSync(cb);
});
};
// Templates
Store.getTemplates = function (data, cb) {
var templateFiles = filesOp.getFiles(['template']);
var res = [];
templateFiles.forEach(function (f) {
var data = filesOp.getFileData(f);
res.push(JSON.parse(JSON.stringify(data)));
});
return 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 = recent[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(obj.n);
}
// Add the pad if it does not exist in our drive
if (!contains) {
Store.addPad({
href: href,
title: title
}, 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);
};
// Get hashes for the share button
common.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();
};
var onReady = function (returned, cb) {
var proxy = store.proxy;
var userObject = store.userObject = UserObject.init(proxy.drive, {
pinPads: function (pads, cb) { Store.pinPads({pads: pads}, cb); },
loggedIn: store.loggedIn
});
var todo = function () {
fo.fixFiles();
Migrate(proxy, Cryptpad);
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[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', [Constants.tokenKey], function () {
postMessage("UPDATE_TOKEN", { data: 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.realtime = rt.realtime;
store.network = rt.network;
store.loggedIn = typeof(data.userHash) !== "undefined";
var returned = {};
rt.proxy.on('create', function (info) {
exp.info = info;
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, data, cb) {
setTimeout(function () {
data.query(cmd, data, cb); // TODO temporary, will be rzplaced by webworker channel
});
};
connect(data, function (ret) {
if (Object.keys(store.proxy).length === 1) {
Feedback.send("FIRST_APP_USE", true);
}
callback(ret);
});
};
Store.disconnect = function () {
if (!store.network) { return; }
store.network.disconnect();
};
return Store;
});

@ -0,0 +1,130 @@
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_COMPLETE': {
Store.uploadComplete(data, cb); break;
}
case 'UPLOAD_STATUS': {
Store.uploadStatus(data, cb); break;
}
case 'UPLOAD_CANCEL': {
Store.uploadCancel(data, cb); break;
}
case 'ARE_PINS_SYNCED': {
Store.arePinsSynced(data, cb); break;
}
case 'RESET_PINS': {
Store.resetPins(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;
}
default: {
break;
}
}
};
return Rpc;
});

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

@ -2,7 +2,7 @@ define([
'/common/common-util.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
], function (Util) {
var Nacl = window.nacl;
var Nacl = self.nacl;
var uid = Util.uid;
var signMsg = function (data, signKey) {
@ -140,7 +140,7 @@ types of messages:
var send = ctx.send = function (type, msg, cb) {
if (!ctx.connected && type !== 'COOKIE') {
return void window.setTimeout(function () {
return void self.setTimeout(function () {
cb('DISCONNECTED');
});
}
@ -185,7 +185,7 @@ types of messages:
send.unauthenticated = function (type, msg, cb) {
if (!ctx.connected) {
return void window.setTimeout(function () {
return void self.setTimeout(function () {
cb('DISCONNECTED');
});
}
@ -276,7 +276,7 @@ types of messages:
var send = ctx.send = function (type, msg, cb) {
if (!ctx.connected) {
return void window.setTimeout(function () {
return void self.setTimeout(function () {
cb('DISCONNECTED');
});
}

@ -104,10 +104,16 @@ define([
});
});
secret = cfg.getSecrets ? cfg.getSecrets(Cryptpad, Utils) : Utils.Hash.getSecrets();
if (!secret.channel) {
// New pad: create a new random channel id
secret.channel = Utils.Hash.createChannelId();
if (cfg.getSecrets) {
cfg.getSecrets(Cryptpad, Utils, waitFor(function (err, s) {
secret = s;
}));
} else {
secret = Utils.Hash.getSecrets();
if (!secret.channel) {
// New pad: create a new random channel id
secret.channel = Utils.Hash.createChannelId();
}
}
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
@ -120,54 +126,46 @@ define([
var proxy = Cryptpad.getProxy();
var updateMeta = function () {
//console.log('EV_METADATA_UPDATE');
var name;
var metaObj, isTemplate;
nThen(function (waitFor) {
Cryptpad.getLastName(waitFor(function (err, n) {
Cryptpad.getMetadata(waitFor(function (err, m) {
if (err) { console.log(err); }
metaObj = m;
}));
Cryptpad.isTemplate(window.location.href, waitFor(function (err, t) {
if (err) { console.log(err); }
name = n;
isTemplate = t;
}));
}).nThen(function (/*waitFor*/) {
var metaObj = {
doc: {
defaultTitle: defaultTitle,
type: parsed.type
},
user: {
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(),
origin: window.location.origin,
pathname: window.location.pathname,
fileHost: ApiConfig.fileHost,
readOnly: readOnly,
availableHashes: hashes,
isTemplate: Cryptpad.isTemplate(window.location.href),
feedbackAllowed: Utils.Feedback.state,
friends: proxy.friends || {},
settings: proxy.settings || {},
isPresent: parsed.hashData && parsed.hashData.present,
isEmbed: parsed.hashData && parsed.hashData.embed,
thumbnails: !((proxy.settings || {}).general || {}).disableThumbnails,
accounts: {
donateURL: Cryptpad.donateURL,
upgradeURL: Cryptpad.upgradeURL
}
metaObj.doc: {
defaultTitle: defaultTitle,
type: parsed.type
};
var additionalPriv = {
accountName: Utils.LocalStore.getAccountName(),
origin: window.location.origin,
pathname: window.location.pathname,
fileHost: ApiConfig.fileHost,
readOnly: readOnly,
availableHashes: hashes,
isTemplate: isTemplate,
feedbackAllowed: Utils.Feedback.state,
isPresent: parsed.hashData && parsed.hashData.present,
isEmbed: parsed.hashData && parsed.hashData.embed,
accounts: {
donateURL: Cryptpad.donateURL,
upgradeURL: Cryptpad.upgradeURL
}
};
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
if (cfg.addData) {
cfg.addData(metaObj.priv, Cryptpad);
}
sframeChan.event('EV_METADATA_UPDATE', metaObj);
});
};
Cryptpad.onDisplayNameChanged(updateMeta);
Cryptpad.onMetadataChanged(updateMeta);
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
proxy.on('change', 'settings', updateMeta);
@ -445,8 +443,9 @@ define([
Cryptpad.useTemplate(href, Cryptget, cb);
});
sframeChan.on('Q_TEMPLATE_EXIST', function (type, cb) {
var hasTemplate = Cryptpad.listTemplates(type).length > 0;
cb(hasTemplate);
Cryptpad.listTemplates(type, function (err, templates) {
cb(templates.length > 0);
});
});
sframeChan.on('EV_GOTO_URL', function (url) {

@ -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) {
UI.confirm(confirmMsg, cb, null, true);
});

@ -21,13 +21,13 @@ define([
module.init = function (files, config) {
var exp = {};
var Cryptpad = config.Cryptpad;
var pinPads = config.pinPads;
var loggedIn = config.loggedIn;
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 NEW_FOLDER_NAME = Messages.fm_newFolder;
var NEW_FILE_NAME = Messages.fm_newFile;
var NEW_FOLDER_NAME = Messages.fm_newFolder || 'New folder';
var NEW_FILE_NAME = Messages.fm_newFile || 'New file';
exp.ROOT = ROOT;
exp.UNSORTED = UNSORTED;
@ -488,7 +488,7 @@ define([
// FILES DATA
exp.pushData = function (data, cb) {
// TODO: can only be called from outside atm
if (!Cryptpad) { return; }
if (!pinPads) { return; }
if (typeof cb !== "function") { cb = function () {}; }
var todo = function () {
var id = Util.createRandomInteger();
@ -498,7 +498,7 @@ define([
if (!loggedIn || !AppConfig.enablePinning || config.testMode) {
return void todo();
}
Cryptpad.pinPads([Hash.hrefToHexChannelId(data.href)], function (e) {
pinPads([Hash.hrefToHexChannelId(data.href)], function (e) {
if (e) { return void cb(e); }
todo();
});

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

@ -49,36 +49,27 @@ define([
var proxy = Cryptpad.getProxy();
var updateMeta = function () {
//console.log('EV_METADATA_UPDATE');
var name;
var metaObj;
nThen(function (waitFor) {
Cryptpad.getLastName(waitFor(function (err, n) {
Cryptpad.getMetadata(waitFor(function (err, n) {
if (err) { console.log(err); }
name = n;
metaObj = n;
}));
}).nThen(function (/*waitFor*/) {
sframeChan.event('EV_METADATA_UPDATE', {
doc: {},
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(),
origin: window.location.origin,
pathname: window.location.pathname,
feedbackAllowed: Utils.Feedback.state,
friends: proxy.friends || {},
settings: proxy.settings || {},
types: config.types
}
});
metaObj.doc: {};
var additionalPriv = {
accountName: Utils.LocalStore.getAccountName(),
origin: window.location.origin,
pathname: window.location.pathname,
feedbackAllowed: Utils.Feedback.state,
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);
proxy.on('change', 'settings', updateMeta);

@ -395,15 +395,6 @@ define([
var onReady = function () {
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) {
var $header = $('<div>', {id: HEADER_ID}).appendTo(APP.$rightside);
addAvatar($header);

@ -36,34 +36,41 @@ define([
};
window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad, Utils) {
var getSecrets = function (Cryptpad, Utils, cb) {
var Hash = Utils.Hash;
// 1st case: visiting someone else's profile with hash in the URL
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)));
}
// 2nd case: visiting our own existing profile
var obj = Cryptpad.getProxy();
if (obj.profile && obj.profile.view && obj.profile.edit) {
return Hash.getSecrets('profile', obj.profile.edit);
}
// 3rd case: profile creation (create a new random hash, store it later if needed)
if (!Utils.LocalStore.isLoggedIn()) { return; }
var hash = Hash.createRandomHash();
var secret = Hash.getSecrets('profile', hash);
Cryptpad.pinPads([secret.channel], function (e) {
if (e) {
if (e === 'E_OVER_LIMIT') {
// TODO
}
return;
//return void UI.log(Messages._getKey('profile_error', [e])) // TODO
var editHash;
nThen(function (waitFor) {
// 2nd case: visiting our own existing profile
Cryptpad.getProfileEditUrl(waitFor(function (hash) {
editHash = hash;
}));
}).nThen(function () {
if (!editHash) {
return void cb(null, Hash.getSecrets('profile', editHash));
}
obj.profile = {};
obj.profile.edit = Utils.Hash.getEditHashFromKeys(secret.channel, secret.keys);
obj.profile.view = Utils.Hash.getViewHashFromKeys(secret.channel, secret.keys);
// 3rd case: profile creation (create a new random hash, store it later if needed)
if (!Utils.LocalStore.isLoggedIn()) { return void cb(); }
var hash = Hash.createRandomHash();
var secret = Hash.getSecrets('profile', hash);
Cryptpad.pinPads([secret.channel], function (e) {
if (e) {
if (e === 'E_OVER_LIMIT') {
// TODO
}
return;
//return void UI.log(Messages._getKey('profile_error', [e])) // TODO
}
var profile = {};
profile.edit = Utils.Hash.getEditHashFromKeys(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) {
// Adding a new avatar from the profile: pin it and store it in the object

@ -43,7 +43,7 @@ define([
});
});
sframeChan.on('Q_SETTINGS_DRIVE_GET', function (d, cb) {
cb(Cryptpad.getProxy());
Cryptpad.getUserObject(cb);
});
sframeChan.on('Q_SETTINGS_DRIVE_SET', function (data, cb) {
var sjson = JSON.stringify(data);
@ -57,26 +57,13 @@ define([
});
});
sframeChan.on('Q_SETTINGS_DRIVE_RESET', function (data, cb) {
var proxy = Cryptpad.getProxy();
var realtime = Cryptpad.getRealtime();
proxy.drive = Cryptpad.getStore().getEmptyObject();
Utils.Realtime.whenRealtimeSyncs(realtime, cb);
Cryptpad.resetDrive(cb);
});
sframeChan.on('Q_SETTINGS_LOGOUT', function (data, cb) {
var proxy = Cryptpad.getProxy();
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);
Cryptpad.logoutFromAll(cb);
});
sframeChan.on('Q_SETTINGS_IMPORT_LOCAL', function (data, cb) {
var proxyData = Cryptpad.getStore().getProxy();
require([
'/common/mergeDrive.js',
], function (Merge) {
Merge.anonDriveIntoUser(proxyData, Utils.LocalStore.getFSHash(), cb);
});
Cryptpad.mergeAnonDrive(cb);
});
};
SFCommonO.start({

Loading…
Cancel
Save