From 0840570fbf8b600921cd9e871e589e3671a865c8 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 30 Nov 2017 10:33:09 +0100 Subject: [PATCH 01/26] Async store part 1 --- www/auth/main.js | 15 +- www/common/common-constants.js | 1 + www/common/common-messenger.js | 3 +- www/common/common-realtime.js | 30 +- www/common/cryptget.js | 4 +- www/common/cryptpad-common.js | 956 +++++++++++------------------- www/common/fsStore.js | 13 +- www/common/mergeDrive.js | 14 +- www/common/outer/async-store.js | 766 ++++++++++++++++++++++++ www/common/outer/store-rpc.js | 130 ++++ www/common/pinpad.js | 14 +- www/common/rpc.js | 8 +- www/common/sframe-common-outer.js | 83 ++- www/common/sframe-common.js | 4 - www/common/userObject.js | 10 +- www/drive/tests.js | 2 +- www/filepicker/main.js | 39 +- www/profile/inner.js | 9 - www/profile/main.js | 51 +- www/settings/main.js | 21 +- 20 files changed, 1381 insertions(+), 792 deletions(-) create mode 100644 www/common/outer/async-store.js create mode 100644 www/common/outer/store-rpc.js diff --git a/www/auth/main.js b/www/auth/main.js index 5f1a5a333..a45751dcb 100644 --- a/www/auth/main.js +++ b/www/auth/main.js @@ -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, diff --git a/www/common/common-constants.js b/www/common/common-constants.js index 76c70d103..044319069 100644 --- a/www/common/common-constants.js +++ b/www/common/common-constants.js @@ -10,5 +10,6 @@ define(function () { displayNameKey: 'cryptpad.username', oldStorageKey: 'CryptPad_RECENTPADS', storageKey: 'filesData', + tokenKey: 'loginToken', }; }); diff --git a/www/common/common-messenger.js b/www/common/common-messenger.js index 406b27482..09a527bfb 100644 --- a/www/common/common-messenger.js +++ b/www/common/common-messenger.js @@ -626,10 +626,11 @@ define([ }); }; + // TODO displayName messenger.getMyInfo = function (cb) { cb(void 0, { curvePublic: proxy.curvePublic, - displayName: common.getDisplayName(), + displayName: '' //common.getDisplayName(), }); }; diff --git a/www/common/common-realtime.js b/www/common/common-realtime.js index 6480d7e6b..87a92064b 100644 --- a/www/common/common-realtime.js +++ b/www/common/common-realtime.js @@ -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; }); diff --git a/www/common/cryptget.js b/www/common/cryptget.js index 7a663480e..12bdd195e 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -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); }); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 181f6b7ae..d558f9e28 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -11,12 +11,13 @@ define([ '/common/common-constants.js', '/common/common-feedback.js', '/common/outer/local-store.js', + '/common/outer/store-rpc.js', '/common/pinpad.js', '/customize/application_config.js', '/bower_components/nthen/index.js', ], function ($, Config, Messages, Store, Util, Hash, - Messaging, Realtime, Language, Constants, Feedback, LocalStore, + Messaging, Realtime, Language, Constants, Feedback, LocalStore, AStore, Pinpad, AppConfig, Nthen) { /* This file exposes functionality which is specific to Cryptpad, but not to @@ -25,6 +26,18 @@ define([ Additionally, there is some basic functionality for import/export. */ + var postMessage = function (cmd, data, cb) { + setTimeout(function () { + AStore.query(cmd, data, cb); + }); + }; + var tryParsing = function (x) { + try { return JSON.parse(x); } + catch (e) { + console.error(e); + return null; + } + }; var origin = encodeURIComponent(window.location.hostname); var common = window.Cryptpad = { @@ -40,20 +53,11 @@ define([ var rpc; var anon_rpc; - var getStore = common.getStore = function () { - if (store) { return store; } - throw new Error("Store is not ready!"); - }; var getProxy = common.getProxy = function () { if (store && store.getProxy()) { return store.getProxy().proxy; } }; - common.getFO = function () { - if (store && store.getProxy()) { - return store.getProxy().fo; - } - }; var getNetwork = common.getNetwork = function () { if (store) { if (store.getProxy() && store.getProxy().info) { @@ -63,7 +67,59 @@ define([ return; }; - // REFACTOR pull language directly + // RESTRICTED + // Settings only + common.getUserObject = function (cb) { + postMessage("GET", [], function (obj) { + cb(obj); + }); + }; + common.resetDrive = function (cb) { + postMessage("RESET_DRIVE", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); + }); + }; + common.logoutFromAll = function (cb) { + var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER); + localStorage.setItem(Constants.tokenKey, token); + postMessage("SET", { + key: [Constants.tokenKey], + value: token + }, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); + }); + }; + // Settings and auth + common.getUserObject = function (cb) { + postMessage("GET", [], function (obj) { + cb(obj); + }); + }; + // Settings and ready + common.mergeAnonDrive = function (cb) { + var data = { + anonHash: LocalStore.getFSHash() + }; + postMessage("MIGRATE_ANON_DRIVE", data, cb); + }; + // Profile + common.getProfileEditUrl = function (cb) { + postMessage("GET", ['profile', 'edit'], function (obj) { + cb(obj); + }); + }; + common.setNewProfile = function (profile) { + postMessage("SET", { + key: ['profile'], + value: profile + }, function () {}); + }; + + + + // REFACTOR pull language directly? common.getLanguage = function () { return Messages._languageUsed; }; @@ -72,30 +128,11 @@ define([ }; // REAFCTOR store.getProfile should be store.get(['profile']) - common.getProfileUrl = function () { - if (store && store.getProfile()) { - return store.getProfile().view; - } - }; - common.getAvatarUrl = function () { - if (store && store.getProfile()) { - return store.getProfile().avatar; - } - }; - common.getDisplayName = function (cb) { - var name; - if (getProxy()) { - name = getProxy()[Constants.displayNameKey]; - } - name = name || ''; - if (typeof cb === "function") { cb(null, name); } - return name; - }; - - common.getUid = function () { - if (store && store.getProxy() && store.getProxy().proxy) { - return store.getProxy().proxy.uid; - } + common.getMetadata = function (cb) { + postMessage("GET_METADATA", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(null, obj); + }); }; var getRealtime = common.getRealtime = function () { @@ -105,27 +142,20 @@ define([ return; }; + // TODO not needed with async store common.hasSigningKeys = function (proxy) { return typeof(proxy) === 'object' && typeof(proxy.edPrivate) === 'string' && typeof(proxy.edPublic) === 'string'; }; + // TODO not needed with async store common.hasCurveKeys = function (proxy) { return typeof(proxy) === 'object' && typeof(proxy.curvePrivate) === 'string' && typeof(proxy.curvePublic) === 'string'; }; - common.getPublicKeys = function (proxy) { - proxy = proxy || common.getProxy(); - if (!proxy || !proxy.edPublic || !proxy.curvePublic) { return; } - return { - curve: proxy.curvePublic, - ed: proxy.edPublic, - }; - }; - var makePad = common.makePad = function (href, title) { var now = +new Date(); return { @@ -136,152 +166,145 @@ define([ }; }; + common.setDisplayName = function (value, cb) { + postMessage("SET_DISPLAY_NAME", value, cb); + }; + // STORAGE common.setPadAttribute = function (attr, value, cb, href) { href = Hash.getRelativeHref(href || window.location.href); - getStore().setPadAttribute(href, attr, value, cb); - }; - common.setDisplayName = function (value, cb) { - if (getProxy()) { - getProxy()[Constants.displayNameKey] = value; - } - if (typeof cb === "function") { Realtime.whenRealtimeSyncs(getRealtime(), cb); } - }; - common.setAttribute = function (attr, value, cb) { - getStore().setAttribute(attr, value, function (err, data) { - if (cb) { cb(err, data); } + postMessage("SET_PAD_ATTRIBUTE", { + href: href, + attr: attr, + value: value + }, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(); }); }; - - // STORAGE common.getPadAttribute = function (attr, cb) { var href = Hash.getRelativeHref(window.location.href); - getStore().getPadAttribute(href, attr, cb); + postMessage("GET_PAD_ATTRIBUTE", { + href: href, + attr: attr, + }, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); + }); + }; + common.setAttribute = function (attr, value, cb) { + postMessage("SET_ATTRIBUTE", { + attr: attr, + value: value + }, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(); + }); }; common.getAttribute = function (attr, cb) { - getStore().getAttribute(attr, function (err, data) { - cb(err, data); + postMessage("GET_ATTRIBUTE", { + attr: attr + }, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); }); }; - /* this returns a reference to your proxy. changing it will change your drive. - */ - var getFileEntry = common.getFileEntry = function (href, cb) { - if (typeof(cb) !== 'function') { return; } - var store = getStore(); - if (!store) { return void cb('NO_STORE'); } - href = href || (window.location.pathname + window.location.hash); - var id = store.getIdFromHref(href); - if (!id) { return void cb('NO_ID'); } - var entry = Util.find(getProxy(), [ - 'drive', - 'filesData', - id - ]); - cb(void 0, entry); - }; - + // Tags common.resetTags = function (href, tags, cb) { + // set pad attribute cb = cb || $.noop; if (!Array.isArray(tags)) { return void cb('INVALID_TAGS'); } - getFileEntry(href, function (e, entry) { - if (e) { return void cb(e); } - if (!entry) { cb('NO_ENTRY'); } - entry.tags = tags.slice(); - cb(); - }); + common.setPadAttribute('tags', tags.slice(), cb, href); }; - common.tagPad = function (href, tag, cb) { if (typeof(cb) !== 'function') { return void console.error('EXPECTED_CALLBACK'); } if (typeof(tag) !== 'string') { return void cb('INVALID_TAG'); } - getFileEntry(href, function (e, entry) { + common.getPadAttribute('tags', function (e, tags) { if (e) { return void cb(e); } - if (!entry) { cb('NO_ENTRY'); } - if (!entry.tags) { - entry.tags = [tag]; - } else if (entry.tags.indexOf(tag) === -1) { - entry.tags.push(tag); + var newTags; + if (!tags) { + newTags = [tag]; + } else if (tags.indexOf(tag) === -1) { + newTags = tags.slice(); + newTags.push(tag); } - cb(); - }); + common.setPadAttribute('tags', newTags, cb, href); + }, href); }; - common.untagPad = function (href, tag, cb) { if (typeof(cb) !== 'function') { return void console.error('EXPECTED_CALLBACK'); } if (typeof(tag) !== 'string') { return void cb('INVALID_TAG'); } - getFileEntry(href, function (e, entry) { + common.getPadAttribute('tags', function (e, tags) { if (e) { return void cb(e); } - if (!entry) { cb('NO_ENTRY'); } - if (!entry.tags) { return void cb(); } - var idx = entry.tags.indexOf(tag); + if (!tags) { return void cb(); } + var idx = tags.indexOf(tag); if (idx === -1) { return void cb(); } - entry.tags.splice(idx, 1); - cb(); - }); + var newTags = tags.slice(); + newTags.splice(idx, 1); + common.setPadAttribute('tags', newTags, cb, href); + }, href); }; - common.getPadTags = function (href, cb) { if (typeof(cb) !== 'function') { return; } - getFileEntry(href, function (e, entry) { - if (entry) { - return void cb(void 0, entry.tags? - JSON.parse(JSON.stringify(entry.tags)): []); - } - return cb('NO_ENTRY'); - }); + common.getPadAttribute('tags', function (e, tags) { + if (e) { return void cb(e); } + cb(void 0, tags ? tags.slice() : []); + }, href); }; - common.listAllTags = function (cb) { - var all = []; - var proxy = getProxy(); - var files = Util.find(proxy, ['drive', 'filesData']); - - if (typeof(files) !== 'object') { return cb('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); - } - }); + postMessage("LIST_ALL_TAGS", null, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(void 0, obj); }); - cb(void 0, all); }; // STORAGE - TEMPLATES - var listTemplates = common.listTemplates = function (type) { - var allTemplates = getStore().listTemplates(); - if (!type) { return allTemplates; } - - var templates = allTemplates.filter(function (f) { - var parsed = Hash.parsePadUrl(f.href); - return parsed.type === type; + common.listTemplates = function (type, cb) { + postMessage("GET_TEMPLATES", null, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + if (!Array.isArray(obj)) { return void cb ('NOT_AN_ARRAY'); } + if (!type) { return void cb(obj); } + + var templates = obj.filter(function (f) { + var parsed = Hash.parsePadUrl(f.href); + return parsed.type === type; + }); + cb(templates); }); - return templates; }; - common.addTemplate = function (data) { - getStore().pushData(data, function (e, id) { - if (e) { return void console.error("Error while adding a template:", e); } // TODO LIMIT - getStore().addPad(id, ['template']); + + common.saveAsTemplate = function (Cryptput, data, cb) { + var p = Hash.parsePadUrl(window.location.href); + if (!p.type) { return; } + var hash = Hash.createRandomHash(); + var href = '/' + p.type + '/#' + hash; + Cryptput(hash, data.toSave, function (e) { + if (e) { throw new Error(e); } + postMessage("ADD_PAD", { + href: href, + title: data.title + }, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(); + }); }); }; - common.isTemplate = function (href) { + common.isTemplate = function (href, cb) { var rhref = Hash.getRelativeHref(href); - var templates = listTemplates(); - return templates.some(function (t) { - return t.href === rhref; + common.listTemplates(null, function (templates) { + cb(void 0, templates.some(function (t) { + return t.href === rhref; + })); }); }; - // Secure iframes common.useTemplate = function (href, Crypt, cb) { var parsed = Hash.parsePadUrl(href); if(!parsed) { throw new Error("Cannot get template hash"); } @@ -292,363 +315,115 @@ define([ }); }; - // STORAGE - /* fetch and migrate your pad history from the store */ - var getRecentPads = common.getRecentPads = function (cb) { - getStore().getDrive('filesData', function (err, recentPads) { - if (typeof(recentPads) === "object") { - cb(void 0, recentPads); - return; - } - cb(void 0, {}); - }); - }; - - // STORAGE: Display Name - common.getLastName = common.getDisplayName; - /* function (cb) { - common.getDisplayName(function (err, userName) { - cb(err, userName); - }); - };*/ - var _onDisplayNameChanged = []; - common.onDisplayNameChanged = function (h) { - if (typeof(h) !== "function") { return; } - if (_onDisplayNameChanged.indexOf(h) !== -1) { return; } - _onDisplayNameChanged.push(h); - }; - common.changeDisplayName = function (newName, isLocal) { - _onDisplayNameChanged.forEach(function (h) { - h(newName, isLocal); - }); - }; - - // STORAGE - common.forgetPad = function (href, cb) { - if (typeof(getStore().forgetPad) === "function") { - getStore().forgetPad(Hash.getRelativeHref(href), cb); - return; - } - cb ("store.forgetPad is not a function"); + // Forget button + common.moveToTrash = function (cb, href) { + href = href || window.location.href; + postMessage("MOVE_TO_TRASH", { href: href }, cb); }; - common.setPadTitle = function (name, padHref, cb) { - var href = typeof padHref === "string" ? padHref : window.location.href; + // When opening a new pad or renaming it, store the new title + common.setPadTitle = function (title, padHref, cb) { + var href = padHref || window.location.href; var parsed = Hash.parsePadUrl(href); if (!parsed.hash) { return; } href = parsed.getUrl({present: parsed.present}); - //href = Hash.getRelativeHref(href); - // getRecentPads return the array from the drive, not a copy - // We don't have to call "set..." at the end, everything is stored with listmap - getRecentPads(function (err, recent) { - if (err) { - cb(err); - return; - } - - var updateWeaker = []; - var contains; - Object.keys(recent).forEach(function (id) { - var pad = recent[id]; - var p = Hash.parsePadUrl(pad.href); - - if (p.type !== parsed.type) { return pad; } - - var shouldUpdate = p.hash.replace(/\/$/, '') === parsed.hash.replace(/\/$/, ''); - - // Version 1 : we have up to 4 differents hash for 1 pad, keep the strongest : - // Edit > Edit (present) > View > View (present) - var pHash = p.hashData; - var parsedHash = parsed.hashData; - - if (!pHash) { return; } // We may have a corrupted pad in our storage, abort here in that case - - if (!shouldUpdate && pHash.version === 1 && parsedHash.version === 1 && pHash.channel === parsedHash.channel) { - if (pHash.mode === 'view' && parsedHash.mode === 'edit') { shouldUpdate = true; } - else if (pHash.mode === parsedHash.mode && pHash.present) { shouldUpdate = true; } - else { - // Editing a "weaker" version of a stored hash : update the date and do not push the current hash - pad.atime = +new Date(); - contains = true; - return pad; - } - } - - if (shouldUpdate) { - contains = true; - // update the atime - pad.atime = +new Date(); - - // set the name - pad.title = name; - - // If we now have a stronger version of a stored href, replace the weaker one by the strong one - if (pad && pad.href && href !== pad.href) { - updateWeaker.push({ - o: pad.href, - n: href - }); - } - pad.href = href; - } - return pad; - }); - - if (updateWeaker.length > 0) { - updateWeaker.forEach(function (obj) { - // If we have a stronger url, and if all the occurences of the weaker were - // in the trash, add remove them from the trash and add the stronger in root - getStore().restoreHref(obj.n); - }); - } - if (!contains && href) { - var data = makePad(href, name); - getStore().pushData(data, function (e, id) { - if (e) { - return void cb(e); - } - getStore().addPad(id, common.initialPath); - cb(err, recent); - }); - return; - } - cb(err, recent); - }); - }; - /* - * Buttons - */ - common.renamePad = function (title, href, callback) { if (title === null) { return; } + if (title.trim() === "") { title = Hash.getDefaultName(parsed); } - if (title.trim() === "") { - var parsed = Hash.parsePadUrl(href || window.location.href); - title = Hash.getDefaultName(parsed); - } - - common.setPadTitle(title, href, function (err) { - if (err) { + postMessage("SET_PAD_TITLE", { + href: href, + title: title + }, function (obj) { + if (obj && obj.error) { console.log("unable to set pad title"); - console.error(err); - return; + return void cb(obj.error); } - callback(null, title); + cb(); }); }; // Needed for the secure filepicker app common.getSecureFilesList = function (query, cb) { - var store = common.getStore(); - if (!store) { return void cb("Store is not ready"); } - var proxy = store.getProxy(); - var fo = proxy.fo; - 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; - }; - fo.getFiles(where).forEach(function (id) { - var data = fo.getFileData(id); - var parsed = Hash.parsePadUrl(data.href); - if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) - && hashes.indexOf(parsed.hash) === -1) { - if (isFiltered(parsed.type, data)) { return; } - hashes.push(parsed.hash); - list[id] = data; - } + postMessage("GET_SECURE_FILES_LIST", query, function (list) { + cb(void 0, list); }); - cb (null, list); - }; - - var getUserChannelList = common.getUserChannelList = function () { - var store = common.getStore(); - var proxy = store.getProxy(); - var fo = proxy.fo; - - // start with your userHash... - var userHash = localStorage && localStorage[Constants.userHashKey]; - if (!userHash) { return null; } - - var userParsedHash = Hash.parseTypeHash('drive', userHash); - var userChannel = userParsedHash && userParsedHash.channel; - if (!userChannel) { return null; } - - var list = fo.getFiles([fo.FILES_DATA]).map(function (id) { - return Hash.hrefToHexChannelId(fo.getFileData(id).href); - }) - .filter(function (x) { return x; }); - - // Get the avatar - var profile = store.getProfile(); - 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 (getProxy().friends) { - var fList = Messaging.getFriendChannelsList(common); - list = list.concat(fList); - } - - list.push(Util.base64ToHex(userChannel)); - list.sort(); - - return list; - }; - - var getCanonicalChannelList = common.getCanonicalChannelList = function () { - return Util.deduplicateString(getUserChannelList()).sort(); - }; - - var pinsReady = common.pinsReady = function () { - if (!LocalStore.isLoggedIn()) { - return false; - } - if (!PINNING_ENABLED) { - console.error('[PINNING_DISABLED]'); - return false; - } - if (!rpc) { - console.error('RPC_NOT_READY'); - return false; - } - return true; }; common.arePinsSynced = function (cb) { - if (!pinsReady()) { return void cb ('RPC_NOT_READY'); } - - var list = getCanonicalChannelList(); - var local = Hash.hashChannelList(list); - rpc.getServerHash(function (e, hash) { - if (e) { return void cb(e); } - cb(void 0, hash === local); + postMessage("ARE_PINS_SYNCED", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); }); }; common.resetPins = function (cb) { - if (!pinsReady()) { return void cb ('RPC_NOT_READY'); } - - var list = getCanonicalChannelList(); - rpc.reset(list, function (e, hash) { - if (e) { return void cb(e); } - cb(void 0, hash); + postMessage("RESET_PINS", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); }); }; common.pinPads = function (pads, cb) { - if (!pinsReady()) { return void cb ('RPC_NOT_READY'); } - if (typeof(cb) !== 'function') { - console.error('expected a callback'); - } - - rpc.pin(pads, function (e, hash) { - if (e) { return void cb(e); } - cb(void 0, hash); + postMessage("PIN_PADS", {pads: pads}, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); }); }; common.unpinPads = function (pads, cb) { - if (!pinsReady()) { return void cb ('RPC_NOT_READY'); } - - rpc.unpin(pads, function (e, hash) { - if (e) { return void cb(e); } - cb(void 0, hash); + postMessage("UNPIN_PADS", {pads: pads}, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); }); }; common.getPinnedUsage = function (cb) { - if (!pinsReady()) { return void cb('RPC_NOT_READY'); } - - rpc.getFileListSize(function (err, bytes) { - if (typeof(bytes) === 'number') { - common.account.usage = bytes; - } - cb(err, bytes); + postMessage("GET_PINNED_USAGE", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); }); }; // SFRAME: talk to anon_rpc from the iframe common.anonRpcMsg = function (msg, data, cb) { if (!msg) { return; } - if (!anon_rpc) { return void cb('ANON_RPC_NOT_READY'); } - anon_rpc.send(msg, data, cb); + postMessage("ANON_RPC_MESSAGE", { + msg: msg, + data: data + }, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); + }); }; common.getFileSize = function (href, cb) { - if (!anon_rpc) { return void cb('ANON_RPC_NOT_READY'); } - //if (!pinsReady()) { return void cb('RPC_NOT_READY'); } - var channelId = Hash.hrefToHexChannelId(href); - anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) { - if (e) { return void cb(e); } - if (response && response.length && typeof(response[0]) === 'number') { - return void cb(void 0, response[0]); - } else { - cb('INVALID_RESPONSE'); - } + postMessage("GET_FILE_SIZE", {href: href}, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(undefined, obj.size); }); }; + // TODO not used anymore? common.getMultipleFileSize = function (files, cb) { - if (!anon_rpc) { return void cb('ANON_RPC_NOT_READY'); } - if (!Array.isArray(files)) { - return void setTimeout(function () { cb('INVALID_FILE_LIST'); }); - } - - anon_rpc.send('GET_MULTIPLE_FILE_SIZE', files, function (e, res) { - if (e) { return cb(e); } - if (res && res.length && typeof(res[0]) === 'object') { - cb(void 0, res[0]); - } else { - cb('UNEXPECTED_RESPONSE'); - } + postMessage("GET_MULTIPLE_FILE_SIZE", {files:files}, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(undefined, obj.size); }); }; common.updatePinLimit = function (cb) { - if (!pinsReady()) { return void cb('RPC_NOT_READY'); } - rpc.updatePinLimits(function (e, limit, plan, note) { - if (e) { return cb(e); } - common.account.limit = limit; - common.account.plan = plan; - common.account.note = note; - cb(e, limit, plan, note); + postMessage("UPDATE_PIN_LIMIT", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(undefined, obj.limit, obj.plan, obj.note); }); }; common.getPinLimit = function (cb) { - if (!pinsReady()) { return void cb('RPC_NOT_READY'); } - - var account = common.account; - - var ALWAYS_REVALIDATE = true; - if (ALWAYS_REVALIDATE || typeof(account.limit) !== 'number' || - typeof(account.plan) !== 'string' || - typeof(account.note) !== 'string') { - return void rpc.getLimit(function (e, limit, plan, note) { - if (e) { return cb(e); } - common.account.limit = limit; - common.account.plan = plan; - common.account.note = note; - cb(void 0, limit, plan, note); - }); - } - - cb(void 0, account.limit, account.plan, account.note); + postMessage("GET_PIN_LIMIT", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(undefined, obj.limit, obj.plan, obj.note); + }); }; common.isOverPinLimit = function (cb) { @@ -671,87 +446,55 @@ define([ }; common.clearOwnedChannel = function (channel, cb) { - if (!pinsReady()) { return void cb('RPC_NOT_READY'); } - rpc.clearOwnedChannel(channel, cb); + postMessage("CLEAR_OWNED_CHANNEL", {channel: channel}, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); + }); }; common.uploadComplete = function (cb) { - if (!pinsReady()) { return void cb('RPC_NOT_READY'); } - rpc.uploadComplete(cb); + postMessage("UPLOAD_COMPLETE", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); + }); }; common.uploadStatus = function (size, cb) { - if (!pinsReady()) { return void cb('RPC_NOT_READY'); } - rpc.uploadStatus(size, cb); + postMessage("UPLOAD_STATUS", {size: size}, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); + }); }; common.uploadCancel = function (cb) { - if (!pinsReady()) { return void cb('RPC_NOT_READY'); } - rpc.uploadCancel(cb); - }; - - // Forget button - // TODO REFACTOR only used in sframe-common-outer - common.moveToTrash = function (cb, href) { - href = href || window.location.href; - common.forgetPad(href, function (err) { - if (err) { - console.log("unable to forget pad"); - console.error(err); - cb(err, null); - return; - } - var n = getNetwork(); - var r = getRealtime(); - if (n && r) { - Realtime.whenRealtimeSyncs(r, function () { - n.disconnect(); - cb(); - }); - } else { - cb(); - } + postMessage("UPLOAD_CANCEL", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); }); }; - // TODO REFACTOR only used in sframe-common-outer - common.saveAsTemplate = function (Cryptput, data, cb) { - var p = Hash.parsePadUrl(window.location.href); - if (!p.type) { return; } - var hash = Hash.createRandomHash(); - var href = '/' + p.type + '/#' + hash; - Cryptput(hash, data.toSave, function (e) { - if (e) { throw new Error(e); } - common.addTemplate(makePad(href, data.title)); - Realtime.whenRealtimeSyncs(getRealtime(), function () { - cb(); - }); - }); - }; + // HERE common.getShareHashes = function (secret, cb) { if (!window.location.hash) { var hashes = Hash.getHashes(secret.channel, secret); return void cb(null, hashes); } - common.getRecentPads(function (err, recent) { - var parsed = Hash.parsePadUrl(window.location.href); - if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); } - if (parsed.type === 'file') { secret.channel = Util.base64ToHex(secret.channel); } - var hashes = Hash.getHashes(secret.channel, secret); - - if (!hashes.editHash && !hashes.viewHash && parsed.hashData && !parsed.hashData.mode) { - // It means we're using an old hash - hashes.editHash = window.location.hash.slice(1); - } - - // If we have a stronger version in drive, add it and add a redirect button - var stronger = recent && Hash.findStronger(null, recent); - if (stronger) { - var parsed2 = Hash.parsePadUrl(stronger); - hashes.editHash = parsed2.hash; - } + var parsed = Hash.parsePadUrl(window.location.href); + if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); } + if (parsed.type === 'file') { secret.channel = Util.base64ToHex(secret.channel); } + var hashes = Hash.getHashes(secret.channel, secret); + + if (!hashes.editHash && !hashes.viewHash && parsed.hashData && !parsed.hashData.mode) { + // It means we're using an old hash + hashes.editHash = window.location.hash.slice(1); + return void cb(null, hashes); + } + postMessage("GET_STRONGER_HASH", { + href: href + }, function (hash) { + if (hash) { hashes.editHash = hash; } cb(null, hashes); }); }; @@ -777,6 +520,44 @@ define([ localStorage[CRYPTPAD_VERSION] = ver; }; + var _onMetadataChanged = []; + common.onMetadataChanged = function (h) { + if (typeof(h) !== "function") { return; } + if (_onMetadataChanged.indexOf(h) !== -1) { return; } + _onMetadataChanged.push(h); + }; + common.changeMetadata = function () { + _onMetadataChanged.forEach(function (h) { h(); }); + }; + + var requestLogin = function () { + // log out so that you don't go into an endless loop... + LocalStore.logout(); + + // redirect them to log in, and come back when they're done. + sessionStorage.redirectTo = window.location.href; + window.location.href = '/login/'; + }; + + var onMessage = function (cmd, data, cb) { + cb = cb || function () {}; + switch (cmd) { + case 'REQUEST_LOGIN': { + requestLogin(); + break; + } + case 'UPDATE_METADATA': { + common.changeMetadata(); + break; + } + case 'UPDATE_TOKEN': { + var localToken = tryParsing(localStorage.getItem(Constants.tokenKey)); + if (localToken !== data.token) { requestLogin(); } + break; + } + } + }; + common.ready = (function () { var env = {}; var initialized = false; @@ -786,18 +567,13 @@ define([ return void setTimeout(function () { f(void 0, env); }); } + // TODO if (sessionStorage[Constants.newPadPathKey]) { common.initialPath = sessionStorage[Constants.newPadPathKey]; delete sessionStorage[Constants.newPadPathKey]; } - var proxy; - var network; var provideFeedback = function () { - if (Object.keys(proxy).length === 1) { - Feedback.send("FIRST_APP_USE", true); - } - if (typeof(window.Proxy) === 'undefined') { Feedback.send("NO_PROXIES"); } @@ -817,36 +593,42 @@ define([ Feedback.reportScreenDimensions(); Feedback.reportLanguage(); }; - var initFeedback = function () { + var initFeedback = function (feedback) { // Initialize feedback - try { - var entry = Util.find(getProxy(), [ - 'settings', - 'general', - 'allowUserFeedback' - ]); - Feedback.init(entry); - } catch (e) { - console.error(e); - Feedback.init(false); - } + Feedback.init(feedback); provideFeedback(); }; Nthen(function (waitFor) { - Store.ready(waitFor(function (err, storeObj) { - store = common.store = env.store = storeObj; - Messaging.addDirectMessageHandler(common); - proxy = getProxy(); - network = getNetwork(); - network.on('disconnect', function () { - Realtime.setConnectionState(false); - }); - network.on('reconnect', function () { - Realtime.setConnectionState(true); - }); - initFeedback(); - }), common); + var cfg = { + query: onMessage, // TODO temporary, will be replaced by a webworker channel + userHash: LocalStore.getUserHash(), + anonHash: LocalStore.getFSHash(), + localToken: localStorage.getItem(Constants.tokenKey) + }; + AStore.query("CONNECT", cfg, waitFor(function (data) { + if (data.error) { throw new Error(data.error); } + + if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); } + + if (cfg.userHash && sessionStorage) { + // copy User_hash into sessionStorage because cross-domain iframes + // on safari replaces localStorage with sessionStorage or something + sessionStorage.setItem(Constants.userHashKey, cfg.userHash); + } + + if (cfg.userHash) { + var localToken = tryParsing(localStorage.getItem(Constants.tokenKey)); + if (localToken === null) { + // if that number hasn't been set to localStorage, do so. + localStorage.setItem(Constants.tokenKey, data[Constants.tokenKey]); + } + } + + // TODO ww + //Messaging.addDirectMessageHandler(common); + initFeedback(data.feedback); + })); }).nThen(function (waitFor) { $(waitFor()); }).nThen(function (waitFor) { @@ -876,114 +658,40 @@ define([ document.location.reload(); } else if (o && !n) { LocalStore.logout(); - if (getNetwork()) { - getNetwork().disconnect(); - } + postMessage("DISCONNECT"); } }); if (PINNING_ENABLED && LocalStore.isLoggedIn()) { console.log("logged in. pads will be pinned"); - var w0 = waitFor(); - Pinpad.create(network, proxy, function (e, call) { - if (e) { - console.error(e); - return w0(); - } - + postMessage("INIT_RPC", null, waitFor(function () { console.log('RPC handshake complete'); - rpc = common.rpc = env.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; - w0(); - }); - - common.arePinsSynced(function (err, yes) { - if (!yes) { - common.resetPins(function (err) { - if (err) { - console.error("Pin Reset Error"); - return console.error(err); - } - console.log('RESET DONE'); - }); - } - }); - }); + })); } else if (PINNING_ENABLED) { console.log('not logged in. pads will not be pinned'); } else { console.log('pinning disabled'); } - var w1 = waitFor(); - require([ - '/common/rpc.js', - ], function (Rpc) { - Rpc.createAnonymous(network, function (e, call) { - if (e) { - console.error(e); - return void w1(); - } - anon_rpc = common.anon_rpc = env.anon_rpc = call; - w1(); - }); - }); - - // Everything's ready, continue... - if($('#pad-iframe').length) { - var w2 = waitFor(); - var $iframe = $('#pad-iframe'); - var iframe = $iframe[0]; - var iframeDoc = iframe.contentDocument || iframe.contentWindow.document; - if (iframeDoc.readyState === 'complete') { - return void w2(); - } - $iframe.load(w2); //cb); - } + postMessage("INIT_ANON_RPC", null, waitFor(function () { + console.log('Anonymous RPC ready'); + })); }).nThen(function (waitFor) { if (sessionStorage.createReadme) { - var w = waitFor(); - require(['/common/cryptget.js'], function (Crypt) { - var hash = Hash.createRandomHash(); - Crypt.put(hash, Messages.driveReadme, function (e) { - if (e) { - console.error("Error while creating the default pad:", e); - return void w(); - } - var href = '/pad/#' + hash; - var data = { - href: href, - title: Messages.driveReadmeTitle, - atime: +new Date(), - ctime: +new Date() - }; - common.getFO().pushData(data, function (e, id) { - if (e) { - console.error("Error while creating the default pad:", e); - return void w(); - } - common.getFO().add(id); - w(); - }); - }); + var data = { + driveReadme: Messages.driveReadme, + driveReadmeTitle: Messages.driveReadmeTitle, + }; + postMessage("CREATE_README", data, waitFor(function (e) { + if (e && e.error) { return void console.error(e.error); } delete sessionStorage.createReadme; - }); + })); } }).nThen(function (waitFor) { if (sessionStorage.migrateAnonDrive) { - var w = waitFor(); - require(['/common/mergeDrive.js'], function (Merge) { - var hash = LocalStore.getFSHash(); - Merge.anonDriveIntoUser(getStore().getProxy(), hash, function () { - delete sessionStorage.migrateAnonDrive; - w(); - }); - }); + common.mergeAnonDrive(waitFor(function() { + delete sessionStorage.migrateAnonDrive; + })); } }).nThen(function () { updateLocalVersion(); diff --git a/www/common/fsStore.js b/www/common/fsStore.js index 6ef2f6d51..3c502a0ee 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -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()) { diff --git a/www/common/mergeDrive.js b/www/common/mergeDrive.js index d61a1db18..ba831ab56 100644 --- a/www/common/mergeDrive.js +++ b/www/common/mergeDrive.js @@ -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; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js new file mode 100644 index 000000000..46e8ba2a9 --- /dev/null +++ b/www/common/outer/async-store.js @@ -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; +}); diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js new file mode 100644 index 000000000..5a49f31c4 --- /dev/null +++ b/www/common/outer/store-rpc.js @@ -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; +}); + diff --git a/www/common/pinpad.js b/www/common/pinpad.js index ebece2626..07f55b5a2 100644 --- a/www/common/pinpad.js +++ b/www/common/pinpad.js @@ -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'); }); } diff --git a/www/common/rpc.js b/www/common/rpc.js index 9f04d7663..929be4795 100644 --- a/www/common/rpc.js +++ b/www/common/rpc.js @@ -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'); }); } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 3e6c7c84c..c9eeb009e 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -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) { diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index f50ff71da..1d21acb92 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -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); }); diff --git a/www/common/userObject.js b/www/common/userObject.js index 58e45e8ee..88c354b5b 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -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(); }); diff --git a/www/drive/tests.js b/www/drive/tests.js index 23ed2172f..222d1b905 100644 --- a/www/drive/tests.js +++ b/www/drive/tests.js @@ -70,7 +70,7 @@ define([ module.test = function (assert) { var config = { - Cryptpad: Cryptpad, + pinPads: Cryptpad.pinPads, workgroup: false, testMode: true, loggedIn: false diff --git a/www/filepicker/main.js b/www/filepicker/main.js index d628b401c..738e40773 100644 --- a/www/filepicker/main.js +++ b/www/filepicker/main.js @@ -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); diff --git a/www/profile/inner.js b/www/profile/inner.js index 9b48e2ca2..2728dc15e 100644 --- a/www/profile/inner.js +++ b/www/profile/inner.js @@ -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 = $('
', {id: HEADER_ID}).appendTo(APP.$rightside); addAvatar($header); diff --git a/www/profile/main.js b/www/profile/main.js index 195dbdc6b..bdee640bf 100644 --- a/www/profile/main.js +++ b/www/profile/main.js @@ -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 diff --git a/www/settings/main.js b/www/settings/main.js index 958dcda45..7075ccc8c 100644 --- a/www/settings/main.js +++ b/www/settings/main.js @@ -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({ From b3688db20220cf664a6779da7b0d39e93737e721 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 30 Nov 2017 15:01:17 +0100 Subject: [PATCH 02/26] Use async store --- www/common/common-messaging.js | 66 +++++------ www/common/common-realtime.js | 49 +------- www/common/cryptget.js | 4 +- www/common/cryptpad-common.js | 137 ++++++++++------------ www/common/fsStore.js | 2 +- www/common/outer/async-store.js | 185 +++++++++++++++++------------- www/common/outer/store-rpc.js | 11 +- www/common/pinpad.js | 14 +-- www/common/rpc.js | 8 +- www/common/sframe-common-outer.js | 31 +++-- www/common/sframe-common.js | 1 + www/drive/main.js | 14 +-- www/filepicker/main.js | 4 +- www/profile/main.js | 10 +- www/todo/main.js | 12 +- 15 files changed, 251 insertions(+), 297 deletions(-) diff --git a/www/common/common-messaging.js b/www/common/common-messaging.js index da5ac004a..13d132219 100644 --- a/www/common/common-messaging.js +++ b/www/common/common-messaging.js @@ -1,15 +1,12 @@ define([ - 'jquery', '/bower_components/chainpad-crypto/crypto.js', - '/common/curve.js', '/common/common-hash.js', '/common/common-util.js', '/common/common-constants.js', '/customize/messages.js', - '/bower_components/marked/marked.min.js', '/common/common-realtime.js', -], function ($, Crypto, Curve, Hash, Util, Constants, Messages, Marked, Realtime) { +], function (Crypto, Hash, Util, Constants, Messages, Realtime) { var Msg = { inputs: [], }; @@ -51,9 +48,8 @@ define([ }); }; - Msg.getFriendChannelsList = function (common) { + Msg.getFriendChannelsList = function (proxy) { var list = []; - var proxy = common.getProxy(); eachFriend(proxy.friends, function (friend) { list.push(friend.channel); }); @@ -61,7 +57,7 @@ define([ }; // TODO make this internal to the messenger - var channels = Msg.channels = window.channels = {}; + var channels = Msg.channels = {}; Msg.getLatestMessages = function () { Object.keys(channels).forEach(function (id) { @@ -74,8 +70,8 @@ define([ // Invitation // FIXME there are too many functions with this name - var addToFriendList = Msg.addToFriendList = function (common, data, cb) { - var proxy = common.getProxy(); + var addToFriendList = Msg.addToFriendList = function (cfg, data, cb) { + var proxy = cfg.proxy; var friends = getFriendList(proxy); var pubKey = data.curvePublic; // todo validata data @@ -83,19 +79,19 @@ define([ friends[pubKey] = data; - Realtime.whenRealtimeSyncs(common.getRealtime(), function () { + Realtime.whenRealtimeSyncs(cfg.realtime, function () { cb(); - common.pinPads([data.channel], function (e) { - if (e) { console.error(e); } + cfg.pinPads([data.channel], function (res) { + if (res.error) { console.error(res.error); } }); }); - common.changeDisplayName(proxy[Constants.displayNameKey]); + cfg.updateMetadata(); }; /* Used to accept friend requests within apps other than /contacts/ */ - Msg.addDirectMessageHandler = function (common) { - var network = common.getNetwork(); - var proxy = common.getProxy(); + Msg.addDirectMessageHandler = function (cfg) { + var network = cfg.network; + var proxy = cfg.proxy; if (!network) { return void console.error('Network not ready'); } network.on('message', function (message, sender) { var msg; @@ -138,8 +134,7 @@ define([ var confirmMsg = Messages._getKey('contacts_request', [ Util.fixHTML(msgData.displayName) ]); - common.onFriendRequest(confirmMsg, todo); - //UI.confirm(confirmMsg, todo, null, true); + cfg.friendRequest(confirmMsg, todo); return; } if (msg[0] === "FRIEND_REQ_OK") { @@ -147,14 +142,14 @@ define([ if (idx !== -1) { pendingRequests.splice(idx, 1); } // FIXME clarify this function's name - addToFriendList(common, msgData, function (err) { + addToFriendList(cfg, msgData, function (err) { if (err) { - return void common.onFriendComplete({ + return void cfg.friendComplete({ logText: Messages.contacts_addError, netfluxId: sender }); } - common.onFriendComplete({ + cfg.friendComplete({ logText: Messages.contacts_added, netfluxId: sender }); @@ -167,24 +162,24 @@ define([ if (msg[0] === "FRIEND_REQ_NOK") { var i = pendingRequests.indexOf(sender); if (i !== -1) { pendingRequests.splice(i, 1); } - common.onFriendComplete({ + cfg.friendComplete({ logText: Messages.contacts_rejected, netfluxId: sender }); - common.changeDisplayName(proxy[Constants.displayNameKey]); + cfg.updateMetadata(); return; } if (msg[0] === "FRIEND_REQ_ACK") { var data = pending[sender]; if (!data) { return; } - addToFriendList(common, data, function (err) { + addToFriendList(cfg, data, function (err) { if (err) { - return void common.onFriendComplete({ + return void cfg.friendComplete({ logText: Messages.contacts_addError, netfluxId: sender }); } - common.onFriendComplete({ + cfg.friendComplete({ logText: Messages.contacts_added, netfluxId: sender }); @@ -198,17 +193,14 @@ define([ }); }; - Msg.getPending = function () { - return pendingRequests; - }; - - Msg.inviteFromUserlist = function (common, netfluxId) { - var network = common.getNetwork(); - var parsed = Hash.parsePadUrl(window.location.href); + Msg.inviteFromUserlist = function (cfg, data, cb) { + var network = cfg.network; + var netfluxId = data.netfluxId; + var parsed = Hash.parsePadUrl(data.href); if (!parsed.hashData) { return; } // Message var chan = parsed.hashData.channel; - var myData = createData(common.getProxy()); + var myData = createData(cfg.proxy); var msg = ["FRIEND_REQ", chan, myData]; // Encryption var keyStr = parsed.hashData.key; @@ -218,12 +210,10 @@ define([ // Send encrypted message if (pendingRequests.indexOf(netfluxId) === -1) { pendingRequests.push(netfluxId); - var proxy = common.getProxy(); - // this redraws the userlist after a change has occurred - // TODO rename this function to reflect its purpose - common.changeDisplayName(proxy[Constants.displayNameKey]); + cfg.updateMetadata(); // redraws the userlist in pad } network.sendto(netfluxId, msgStr); + cb(); }; return Msg; diff --git a/www/common/common-realtime.js b/www/common/common-realtime.js index 87a92064b..21c0728e2 100644 --- a/www/common/common-realtime.js +++ b/www/common/common-realtime.js @@ -1,18 +1,6 @@ -define([ - //'/customize/application_config.js', - //'/customize/messages.js', - //'/common/common-interface.js', -], function (/*AppConfig, Messages, UI*/) { +define([], function () { 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... */ @@ -20,7 +8,7 @@ define([ if (typeof(realtime.getAuthDoc) !== 'function') { return void console.error('improper use of this function'); } - self.setTimeout(function () { + setTimeout(function () { if (realtime.getAuthDoc() === realtime.getUserDoc()) { return void cb(); } else { @@ -29,38 +17,5 @@ define([ }, 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; }); diff --git a/www/common/cryptget.js b/www/common/cryptget.js index 12bdd195e..686c69884 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -73,12 +73,12 @@ define([ realtime.contentUpdate(doc); - var to = self.setTimeout(function () { + var to = setTimeout(function () { cb(new Error("Timeout")); }, 5000); Realtime.whenRealtimeSyncs(realtime, function () { - self.clearTimeout(to); + clearTimeout(to); realtime.abort(); finish(Session, void 0); }); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index d558f9e28..f1b0eff31 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2,7 +2,6 @@ define([ 'jquery', '/api/config', '/customize/messages.js', - '/common/fsStore.js', '/common/common-util.js', '/common/common-hash.js', '/common/common-messaging.js', @@ -16,7 +15,7 @@ define([ '/common/pinpad.js', '/customize/application_config.js', '/bower_components/nthen/index.js', -], function ($, Config, Messages, Store, Util, Hash, +], function ($, Config, Messages, Util, Hash, Messaging, Realtime, Language, Constants, Feedback, LocalStore, AStore, Pinpad, AppConfig, Nthen) { @@ -49,24 +48,6 @@ define([ var PINNING_ENABLED = AppConfig.enablePinning; - var store; - var rpc; - var anon_rpc; - - var getProxy = common.getProxy = function () { - if (store && store.getProxy()) { - return store.getProxy().proxy; - } - }; - var getNetwork = common.getNetwork = function () { - if (store) { - if (store.getProxy() && store.getProxy().info) { - return store.getProxy().info.network; - } - } - return; - }; - // RESTRICTED // Settings only common.getUserObject = function (cb) { @@ -116,7 +97,27 @@ define([ value: profile }, function () {}); }; - + common.setAvatar = function (data, cb) { + var postData = { + key: ['profile', 'avatar'] + }; + // If we don't have "data", it means we want to remove the avatar and we should not have a + // "postData.value", even set to undefined (JSON.stringify transforms undefined to null) + if (data) { postData.value = data; } + postMessage("SET", postData, cb); + }; + // Todo + common.getTodoHash = function (cb) { + postMessage("GET", ['todo'], function (obj) { + cb(obj); + }); + }; + common.setTodoHash = function (hash) { + postMessage("SET", { + key: ['todo'], + value: hash + }, function () {}); + }; // REFACTOR pull language directly? @@ -127,7 +128,6 @@ define([ Language.setLanguage(l, null, cb); }; - // REAFCTOR store.getProfile should be store.get(['profile']) common.getMetadata = function (cb) { postMessage("GET_METADATA", null, function (obj) { if (obj.error) { return void cb(obj.error); } @@ -135,37 +135,6 @@ define([ }); }; - var getRealtime = common.getRealtime = function () { - if (store && store.getProxy() && store.getProxy().info) { - return store.getProxy().info.realtime; - } - return; - }; - - // TODO not needed with async store - common.hasSigningKeys = function (proxy) { - return typeof(proxy) === 'object' && - typeof(proxy.edPrivate) === 'string' && - typeof(proxy.edPublic) === 'string'; - }; - - // TODO not needed with async store - common.hasCurveKeys = function (proxy) { - return typeof(proxy) === 'object' && - typeof(proxy.curvePrivate) === 'string' && - typeof(proxy.curvePublic) === 'string'; - }; - - 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)), - }; - }; - common.setDisplayName = function (value, cb) { postMessage("SET_DISPLAY_NAME", value, cb); }; @@ -214,7 +183,7 @@ define([ // Tags common.resetTags = function (href, tags, cb) { // set pad attribute - cb = cb || $.noop; + cb = cb || function () {}; if (!Array.isArray(tags)) { return void cb('INVALID_TAGS'); } common.setPadAttribute('tags', tags.slice(), cb, href); }; @@ -350,29 +319,15 @@ define([ }); }; - common.arePinsSynced = function (cb) { - postMessage("ARE_PINS_SYNCED", null, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(); - }); - }; - - common.resetPins = function (cb) { - postMessage("RESET_PINS", null, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(); - }); - }; - common.pinPads = function (pads, cb) { - postMessage("PIN_PADS", {pads: pads}, function (obj) { + postMessage("PIN_PADS", pads, function (obj) { if (obj.error) { return void cb(obj.error); } cb(); }); }; common.unpinPads = function (pads, cb) { - postMessage("UNPIN_PADS", {pads: pads}, function (obj) { + postMessage("UNPIN_PADS", pads, function (obj) { if (obj.error) { return void cb(obj.error); } cb(); }); @@ -392,14 +347,14 @@ define([ msg: msg, data: data }, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(); + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); }); }; common.getFileSize = function (href, cb) { postMessage("GET_FILE_SIZE", {href: href}, function (obj) { - if (obj.error) { return void cb(obj.error); } + if (obj && obj.error) { return void cb(obj.error); } cb(undefined, obj.size); }); }; @@ -473,17 +428,29 @@ define([ }); }; + // Messaging + common.inviteFromUserlist = function (netfluxId, cb) { + postMessage("INVITE_FROM_USERLIST", { + netfluxId: netfluxId, + href: window.location.href + }, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(); + }); + }; + // HERE common.getShareHashes = function (secret, cb) { + var hashes; if (!window.location.hash) { - var hashes = Hash.getHashes(secret.channel, secret); + hashes = Hash.getHashes(secret.channel, secret); return void cb(null, hashes); } var parsed = Hash.parsePadUrl(window.location.href); if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); } if (parsed.type === 'file') { secret.channel = Util.base64ToHex(secret.channel); } - var hashes = Hash.getHashes(secret.channel, secret); + hashes = Hash.getHashes(secret.channel, secret); if (!hashes.editHash && !hashes.viewHash && parsed.hashData && !parsed.hashData.mode) { // It means we're using an old hash @@ -492,7 +459,7 @@ define([ } postMessage("GET_STRONGER_HASH", { - href: href + href: window.location.href }, function (hash) { if (hash) { hashes.editHash = hash; } cb(null, hashes); @@ -555,6 +522,16 @@ define([ if (localToken !== data.token) { requestLogin(); } break; } + case 'Q_FRIEND_REQUEST': { + if (!common.onFriendRequest) { break; } + common.onFriendRequest(data, cb); + break; + } + case 'EV_FRIEND_COMPLETE': { + if (!common.onFriendComplete) { break; } + common.onFriendComplete(data); + break; + } } }; @@ -664,8 +641,10 @@ define([ if (PINNING_ENABLED && LocalStore.isLoggedIn()) { console.log("logged in. pads will be pinned"); - postMessage("INIT_RPC", null, waitFor(function () { + postMessage("INIT_RPC", null, waitFor(function (obj) { console.log('RPC handshake complete'); + if (obj.error) { return; } + localStorage.plan = obj.plan; })); } else if (PINNING_ENABLED) { console.log('not logged in. pads will not be pinned'); @@ -703,9 +682,9 @@ define([ }()); // MAGIC that happens implicitly - $(function () { + /*$(function () { Language.applyTranslation(); - }); + });*/ return common; }); diff --git a/www/common/fsStore.js b/www/common/fsStore.js index 3c502a0ee..27ed42d34 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -315,7 +315,7 @@ define([ exp.realtime = rt.realtime; exp.proxy = rt.proxy; - exp.loggedIn = Cryptpad.isLoggedIn() + exp.loggedIn = Cryptpad.isLoggedIn(); rt.proxy.on('create', function (info) { exp.info = info; if (!LocalStore.getUserHash()) { diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 46e8ba2a9..116e0cdd7 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -6,24 +6,17 @@ define([ '/common/common-constants.js', '/common/common-feedback.js', '/common/common-realtime.js', + '/common/common-messaging.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, +], function (UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, 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 postMessage = function () {}; var storeHash; @@ -43,7 +36,11 @@ define([ 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; + if (typeof data.value === "undefined") { + delete obj[key]; + } else { + obj[key] = data.value; + } onSync(cb); }; @@ -82,11 +79,10 @@ define([ if (avatarChan) { list.push(avatarChan); } } - // TODO - /*if (store.proxy.friends) { - var fList = Messaging.getFriendChannelsList(common); + if (store.proxy.friends) { + var fList = Messaging.getFriendChannelsList(store.proxy); list = list.concat(fList); - }*/ + } list.push(Util.base64ToHex(userChannel)); list.sort(); @@ -102,34 +98,13 @@ define([ /////////////////////// 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) { + store.rpc.pin(data, function (e, hash) { if (e) { return void cb({error: e}); } cb({hash: hash}); }); @@ -138,7 +113,7 @@ define([ Store.unpinPads = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - store.rpc.unpin(data.pads, function (e, hash) { + store.rpc.unpin(data, function (e, hash) { if (e) { return void cb({error: e}); } cb({hash: hash}); }); @@ -158,7 +133,7 @@ define([ }; // Update for all users from accounts and return current user limits - Store.updatePinLimit = function (cb) { + 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}); } @@ -169,7 +144,7 @@ define([ }); }; // Get current user limits - Store.getPinLimit = function (cb) { + Store.getPinLimit = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } var ALWAYS_REVALIDATE = true; @@ -189,42 +164,63 @@ define([ Store.clearOwnedChannel = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - rpc.clearOwnedChannel(data.channel, cb); + store.rpc.clearOwnedChannel(data.channel, cb); }; Store.uploadComplete = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - rpc.uploadComplete(cb); + store.rpc.uploadComplete(cb); }; Store.uploadStatus = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - rpc.uploadStatus(data.size, cb); + store.rpc.uploadStatus(data.size, cb); }; Store.uploadCancel = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - rpc.uploadCancel(cb); + store.rpc.uploadCancel(cb); + }; + + 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.initRpc = function (data, cb) { - require(['/common/pinpad.js', function (Pinpad) { + 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(); + 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); }); - common.arePinsSynced(function (err, yes) { + arePinsSynced(function (err, yes) { if (!yes) { - common.resetPins(function (err) { + resetPins(function (err) { if (err) { return console.error(err); } console.log('RESET DONE'); }); @@ -239,10 +235,14 @@ define([ ////////////////////////////////////////////////////////////////// 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.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); @@ -367,19 +367,19 @@ define([ if (typeof attr === "string") { console.error('DEPRECATED: use setAttribute with an array, not a string'); return { - obj: storeObj.settings, + obj: store.proxy.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; + 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") { throw new Error("Wrong attribute"); } + else if (typeof obj[el] !== "object") { return void console.error("Wrong attribute"); } obj = obj[el]; }); return { @@ -402,12 +402,12 @@ define([ * - value (String) */ Store.setPadAttribute = function (data, cb) { - store.userObject.setPadAttribute(data.href, date.attr, data.value, function () { + store.userObject.setPadAttribute(data.href, data.attr, data.value, function () { onSync(cb); }); }; Store.getPadAttribute = function (data, cb) { - filesOp.getPadAttribute(data.href, data.attr, function (err, val) { + store.userObject.getPadAttribute(data.href, data.attr, function (err, val) { if (err) { return void cb({error: err}); } cb(val); }); @@ -443,7 +443,7 @@ define([ cb(all); }; - var makePad = common.makePad = function (href, title) { + var makePad = function (href, title) { var now = +new Date(); return { href: href, @@ -465,13 +465,13 @@ define([ // Templates Store.getTemplates = function (data, cb) { - var templateFiles = filesOp.getFiles(['template']); + var templateFiles = store.userObject.getFiles(['template']); var res = []; templateFiles.forEach(function (f) { - var data = filesOp.getFileData(f); + var data = store.userObject.getFileData(f); res.push(JSON.parse(JSON.stringify(data))); }); - return res; + cb(res); }; // Pads @@ -496,7 +496,7 @@ define([ // 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]; + var pad = allPads[id]; if (!pad.href) { continue; } var p2 = Hash.parsePadUrl(pad.href); @@ -539,7 +539,7 @@ define([ 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); + store.userObject.restoreHref(href); } // Add the pad if it does not exist in our drive @@ -585,7 +585,7 @@ define([ }; // Get hashes for the share button - common.getStrongerHash = function (data, cb) { + 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 @@ -597,6 +597,29 @@ define([ cb(); }; + // Messaging + var getMessagingCfg = function () { + console.log(store, store.network); + 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); + }; var onReady = function (returned, cb) { var proxy = store.proxy; @@ -605,9 +628,9 @@ define([ loggedIn: store.loggedIn }); var todo = function () { - fo.fixFiles(); + userObject.fixFiles(); - Migrate(proxy, Cryptpad); + Migrate(proxy); var requestLogin = function () { postMessage("REQUEST_LOGIN"); @@ -624,7 +647,7 @@ define([ } returned[Constants.tokenKey] = proxy[Constants.tokenKey]; - if (store.data.localToken && store.data.localToken !== proxy[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(); @@ -665,6 +688,9 @@ define([ // 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", { data: proxy[Constants.tokenKey] }); }); @@ -692,14 +718,13 @@ define([ classic: true, }; var rt = Listmap.create(listmapConfig); - store.proxy = rt.proxy, - store.realtime = rt.realtime; - store.network = rt.network; + store.proxy = rt.proxy; store.loggedIn = typeof(data.userHash) !== "undefined"; var returned = {}; rt.proxy.on('create', function (info) { - exp.info = info; + store.realtime = info.realtime; + store.network = info.network; if (!data.userHash) { returned.anonHash = Hash.getEditHashFromKeys(info.channel, secret.keys); } @@ -743,18 +768,24 @@ define([ }); } initialized = true; - postMessage = function (cmd, data, cb) { + postMessage = function (cmd, d, cb) { setTimeout(function () { - data.query(cmd, data, cb); // TODO temporary, will be rzplaced by webworker channel + data.query(cmd, d, cb); // TODO temporary, will be rzplaced 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); + + }); }; diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 5a49f31c4..7479b3025 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -40,12 +40,6 @@ define([ 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; } @@ -117,6 +111,11 @@ define([ case 'GET_STRONGER_HASH': { Store.getStrongerHash(data, cb); break; } + // Messaging + case 'INVITE_FROM_USERLIST': { + Store.inviteFromUserlist(data, cb); break; + } + default: { break; diff --git a/www/common/pinpad.js b/www/common/pinpad.js index 07f55b5a2..47eb97892 100644 --- a/www/common/pinpad.js +++ b/www/common/pinpad.js @@ -3,13 +3,13 @@ define([ ], function (Rpc) { var create = function (network, proxy, cb) { if (!network) { - self.setTimeout(function () { + setTimeout(function () { cb('INVALID_NETWORK'); }); return; } if (!proxy) { - self.setTimeout(function () { + setTimeout(function () { cb('INVALID_PROXY'); }); return; @@ -19,7 +19,7 @@ define([ var edPublic = proxy.edPublic; if (!(edPrivate && edPublic)) { - self.setTimeout(function () { + 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)) { - self.setTimeout(function () { + 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)) { - self.setTimeout(function () { + 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)) { - self.setTimeout(function () { + 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 self.setTimeout(function () { + return void setTimeout(function () { cb('INVALID_SIZE'); }); } diff --git a/www/common/rpc.js b/www/common/rpc.js index 929be4795..e86615111 100644 --- a/www/common/rpc.js +++ b/www/common/rpc.js @@ -2,7 +2,7 @@ define([ '/common/common-util.js', '/bower_components/tweetnacl/nacl-fast.min.js', ], function (Util) { - var Nacl = self.nacl; + var Nacl = window.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 self.setTimeout(function () { + return void setTimeout(function () { cb('DISCONNECTED'); }); } @@ -185,7 +185,7 @@ types of messages: send.unauthenticated = function (type, msg, cb) { if (!ctx.connected) { - return void self.setTimeout(function () { + return void setTimeout(function () { cb('DISCONNECTED'); }); } @@ -276,7 +276,7 @@ types of messages: var send = ctx.send = function (type, msg, cb) { if (!ctx.connected) { - return void self.setTimeout(function () { + return void setTimeout(function () { cb('DISCONNECTED'); }); } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index c9eeb009e..a32cecf3f 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -9,6 +9,7 @@ define([ common.start = function (cfg) { cfg = cfg || {}; var realtime = !cfg.noRealtime; + var network; var secret; var hashes; var CpNfOuter; @@ -18,7 +19,7 @@ define([ var SFrameChannel; var sframeChan; var FilePicker; - var Messenger; + //var Messenger; var Messaging; var Notifier; var Utils = {}; @@ -32,7 +33,7 @@ define([ '/common/cryptget.js', '/common/sframe-channel.js', '/filepicker/main.js', - '/common/common-messenger.js', + //'/common/common-messenger.js', '/common/common-messaging.js', '/common/common-notifier.js', '/common/common-hash.js', @@ -41,16 +42,18 @@ define([ '/common/common-constants.js', '/common/common-feedback.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, - _FilePicker, _Messenger, _Messaging, _Notifier, _Hash, _Util, _Realtime, - _Constants, _Feedback, _LocalStore) { + _FilePicker, /*_Messenger,*/ _Messaging, _Notifier, _Hash, _Util, _Realtime, + _Constants, _Feedback, _LocalStore, NetConfig, Netflux) { CpNfOuter = _CpNfOuter; Cryptpad = _Cryptpad; Crypto = _Crypto; Cryptget = _Cryptget; SFrameChannel = _SFrameChannel; FilePicker = _FilePicker; - Messenger = _Messenger; + //Messenger = _Messenger; Messaging = _Messaging; Notifier = _Notifier; Utils.Hash = _Hash; @@ -85,6 +88,12 @@ define([ sframeChan = sfc; }), false, { cache: cache, localStore: localStore, language: Cryptpad.getLanguage() }); Cryptpad.ready(waitFor()); + + if (!cfg.newNetwork) { + Netflux.connect(NetConfig.getWebsocketURL()).then(waitFor(function (nw) { + network = nw; + })); + } })); }).nThen(function (waitFor) { $('#sbox-iframe').focus(); @@ -123,7 +132,6 @@ define([ var parsed = Utils.Hash.parsePadUrl(window.location.href); if (!parsed.type) { throw new Error(); } var defaultTitle = Utils.Hash.getDefaultName(parsed); - var proxy = Cryptpad.getProxy(); var updateMeta = function () { //console.log('EV_METADATA_UPDATE'); var metaObj, isTemplate; @@ -137,7 +145,7 @@ define([ isTemplate = t; })); }).nThen(function (/*waitFor*/) { - metaObj.doc: { + metaObj.doc = { defaultTitle: defaultTitle, type: parsed.type }; @@ -167,7 +175,6 @@ define([ }; Cryptpad.onMetadataChanged(updateMeta); sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); - proxy.on('change', 'settings', updateMeta); Utils.LocalStore.onLogout(function () { sframeChan.event('EV_LOGOUT'); @@ -285,7 +292,6 @@ define([ }; sframeChan.on('Q_GET_FULL_HISTORY', function (data, cb) { - var network = Cryptpad.getNetwork(); var hkn = network.historyKeeper; var crypto = Crypto.createEncryptor(secret.keys); // Get the history messages and send them to the iframe @@ -493,7 +499,8 @@ define([ } if (cfg.messaging) { - var messenger = Messenger.messenger(Cryptpad); + // TODO make messenger work with async store + /*var messenger = Messenger.messenger(Cryptpad); sframeChan.on('Q_CONTACTS_GET_FRIEND_LIST', function (data, cb) { messenger.getFriendList(function (e, keys) { @@ -604,7 +611,7 @@ define([ sframeChan.event('EV_CONTACTS_UNFRIEND', { curvePublic: curvePublic, }); - }); + });*/ } sframeChan.ready(); @@ -628,7 +635,7 @@ define([ CpNfOuter.start({ sframeChan: sframeChan, channel: secret.channel, - network: cfg.newNetwork || Cryptpad.getNetwork(), + network: cfg.newNetwork || network, validateKey: secret.keys.validateKey || undefined, readOnly: readOnly, crypto: Crypto.createEncryptor(secret.keys), diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 1d21acb92..ee2589727 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -122,6 +122,7 @@ define([ funcs.getFileSize = function (href, cb) { var channelId = Hash.hrefToHexChannelId(href); funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) { + console.log(data); if (!data) { return void cb("No response"); } if (data.error) { return void cb(data.error); } if (data.response && data.response.length && typeof(data.response[0]) === 'number') { diff --git a/www/drive/main.js b/www/drive/main.js index 1e3ff3589..01fc98554 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -5,9 +5,7 @@ define([ 'jquery', '/common/requireconfig.js', '/common/sframe-common-outer.js', - '/common/outer/network-config.js', - '/bower_components/netflux-websocket/netflux-client.js', -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO, NetConfig, Netflux) { +], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); // Loaded in load #2 @@ -38,10 +36,10 @@ define([ }; window.addEventListener('message', onMsg); }).nThen(function (/*waitFor*/) { - var getSecrets = function (Cryptpad, Utils) { + var getSecrets = function (Cryptpad, Utils, cb) { var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() || Utils.LocalStore.getFSHash(); - return Utils.Hash.getSecrets('drive', hash); + cb(null, Utils.Hash.getSecrets('drive', hash)); }; var addRpc = function (sframeChan, Cryptpad, Utils) { sframeChan.on('EV_BURN_ANON_DRIVE', function () { @@ -51,13 +49,13 @@ define([ window.location.reload(); }); }; - Netflux.connect(NetConfig.getWebsocketURL()).then(function (network) { + //Netflux.connect(NetConfig.getWebsocketURL()).then(function (network) { SFCommonO.start({ getSecrets: getSecrets, - newNetwork: network, + //newNetwork: network, noHash: true, addRpc: addRpc }); - }, function (err) { console.error(err); }); + //}, function (err) { console.error(err); }); }); }); diff --git a/www/filepicker/main.js b/www/filepicker/main.js index 738e40773..8b7ab2b60 100644 --- a/www/filepicker/main.js +++ b/www/filepicker/main.js @@ -46,7 +46,6 @@ define([ sframeChan = sfc; })); }).nThen(function () { - var proxy = Cryptpad.getProxy(); var updateMeta = function () { //console.log('EV_METADATA_UPDATE'); var metaObj; @@ -56,7 +55,7 @@ define([ metaObj = n; })); }).nThen(function (/*waitFor*/) { - metaObj.doc: {}; + metaObj.doc = {}; var additionalPriv = { accountName: Utils.LocalStore.getAccountName(), origin: window.location.origin, @@ -71,7 +70,6 @@ define([ }; Cryptpad.onMetadataChanged(updateMeta); sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); - proxy.on('change', 'settings', updateMeta); config.addCommonRpc(sframeChan); diff --git a/www/profile/main.js b/www/profile/main.js index bdee640bf..a77829242 100644 --- a/www/profile/main.js +++ b/www/profile/main.js @@ -78,18 +78,14 @@ define([ var chanId = Utils.Hash.hrefToHexChannelId(data); Cryptpad.pinPads([chanId], function (e) { if (e) { return void cb(e); } - Cryptpad.getProxy().profile.avatar = data; - Utils.Realtime.whenRealtimeSyncs(Cryptpad.getRealtime(), function () { - cb(); - }); + Cryptpad.setAvatar(data, cb); }); }); // Removing the avatar from the profile: unpin it sframeChan.on('Q_PROFILE_AVATAR_REMOVE', function (data, cb) { var chanId = Utils.Hash.hrefToHexChannelId(data); - Cryptpad.unpinPads([chanId], function (e) { - delete Cryptpad.getProxy().profile.avatar; - cb(e); + Cryptpad.unpinPads([chanId], function () { + Cryptpad.setAvatar(undefined, cb); }); }); }; diff --git a/www/todo/main.js b/www/todo/main.js index 3f6d1cd5f..9caeb16ef 100644 --- a/www/todo/main.js +++ b/www/todo/main.js @@ -36,12 +36,12 @@ define([ }; window.addEventListener('message', onMsg); }).nThen(function (/*waitFor*/) { - var getSecrets = function (Cryptpad, Utils) { - var proxy = Cryptpad.getProxy(); - var hash = proxy.todo || Utils.Hash.createRandomHash(); - if (!proxy.todo) { proxy.todo = hash; } - - return Utils.Hash.getSecrets('todo', hash); + var getSecrets = function (Cryptpad, Utils, cb) { + Cryptpad.getTodoHash(function (hash) { + var nHash = hash || Utils.Hash.createRandomHash(); + if (!hash) { Cryptpad.setTodoHash(nHash); } + cb(null, Utils.Hash.getSecrets('todo', hash)); + }); }; SFCommonO.start({ getSecrets: getSecrets, From 5c3237e0934ca6d77acc9653407cee949b261d39 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 30 Nov 2017 17:21:58 +0100 Subject: [PATCH 03/26] Fix issues with the async store --- www/common/common-ui-elements.js | 11 +++++----- www/common/cryptpad-common.js | 36 ++++++++++++++----------------- www/common/metadata-manager.js | 22 +++++++++++++++++-- www/common/outer/async-store.js | 10 ++++----- www/common/sframe-common-outer.js | 3 ++- www/common/sframe-common.js | 5 +++-- www/common/userObject.js | 5 ++--- www/file/inner.js | 2 -- 8 files changed, 53 insertions(+), 41 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 57392a5cd..19d4e03bf 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -571,12 +571,7 @@ define([ // getPinnedUsage updates common.account.usage, and other values // so we can just use those and only check for errors var $container = $('', {'class':'cp-limit-container'}); - var todo; - var updateUsage = Util.notAgainForAnother(function () { - common.getPinUsage(todo); - }, LIMIT_REFRESH_RATE); - - todo = function (err, data) { + var todo = function (err, data) { if (err) { return void console.error(err); } var usage = data.usage; @@ -645,6 +640,10 @@ define([ $limit.append($usage).append($text); }; + var updateUsage = Util.notAgainForAnother(function () { + common.getPinUsage(todo); + }, LIMIT_REFRESH_RATE); + setInterval(function () { updateUsage(); }, LIMIT_REFRESH_RATE * 3); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index f1b0eff31..14728be3a 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1,5 +1,4 @@ define([ - 'jquery', '/api/config', '/customize/messages.js', '/common/common-util.js', @@ -15,7 +14,7 @@ define([ '/common/pinpad.js', '/customize/application_config.js', '/bower_components/nthen/index.js', -], function ($, Config, Messages, Util, Hash, +], function (Config, Messages, Util, Hash, Messaging, Realtime, Language, Constants, Feedback, LocalStore, AStore, Pinpad, AppConfig, Nthen) { @@ -238,13 +237,13 @@ define([ postMessage("GET_TEMPLATES", null, function (obj) { if (obj && obj.error) { return void cb(obj.error); } if (!Array.isArray(obj)) { return void cb ('NOT_AN_ARRAY'); } - if (!type) { return void cb(obj); } + if (!type) { return void cb(null, obj); } var templates = obj.filter(function (f) { var parsed = Hash.parsePadUrl(f.href); return parsed.type === type; }); - cb(templates); + cb(null, templates); }); }; @@ -257,7 +256,8 @@ define([ if (e) { throw new Error(e); } postMessage("ADD_PAD", { href: href, - title: data.title + title: data.title, + path: ['template'] }, function (obj) { if (obj && obj.error) { return void cb(obj.error); } cb(); @@ -267,7 +267,7 @@ define([ common.isTemplate = function (href, cb) { var rhref = Hash.getRelativeHref(href); - common.listTemplates(null, function (templates) { + common.listTemplates(null, function (err, templates) { cb(void 0, templates.some(function (t) { return t.href === rhref; })); @@ -321,22 +321,22 @@ define([ common.pinPads = function (pads, cb) { postMessage("PIN_PADS", pads, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(); + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj.hash); }); }; common.unpinPads = function (pads, cb) { postMessage("UNPIN_PADS", pads, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(); + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj.hash); }); }; common.getPinnedUsage = function (cb) { postMessage("GET_PINNED_USAGE", null, function (obj) { if (obj.error) { return void cb(obj.error); } - cb(); + cb(null, obj.bytes); }); }; @@ -544,12 +544,6 @@ define([ return void setTimeout(function () { f(void 0, env); }); } - // TODO - if (sessionStorage[Constants.newPadPathKey]) { - common.initialPath = sessionStorage[Constants.newPadPathKey]; - delete sessionStorage[Constants.newPadPathKey]; - } - var provideFeedback = function () { if (typeof(window.Proxy) === 'undefined') { Feedback.send("NO_PROXIES"); @@ -581,8 +575,12 @@ define([ query: onMessage, // TODO temporary, will be replaced by a webworker channel userHash: LocalStore.getUserHash(), anonHash: LocalStore.getFSHash(), - localToken: localStorage.getItem(Constants.tokenKey) + localToken: tryParsing(localStorage.getItem(Constants.tokenKey)) }; + if (sessionStorage[Constants.newPadPathKey]) { + cfg.initialPath = sessionStorage[Constants.newPadPathKey]; + delete sessionStorage[Constants.newPadPathKey]; + } AStore.query("CONNECT", cfg, waitFor(function (data) { if (data.error) { throw new Error(data.error); } @@ -606,8 +604,6 @@ define([ //Messaging.addDirectMessageHandler(common); initFeedback(data.feedback); })); - }).nThen(function (waitFor) { - $(waitFor()); }).nThen(function (waitFor) { // Load the new pad when the hash has changed var oldHref = document.location.href; diff --git a/www/common/metadata-manager.js b/www/common/metadata-manager.js index 0e375b736..bf99e4b84 100644 --- a/www/common/metadata-manager.js +++ b/www/common/metadata-manager.js @@ -59,7 +59,6 @@ define(['json.sortify'], function (Sortify) { } if (metadataObj.title !== rememberedTitle) { - console.log("Title update\n" + metadataObj.title + '\n'); rememberedTitle = 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) { meta = ev; if (ev.priv) { priv = ev.priv; } + if (netfluxId) { + meta.user.netfluxId = netfluxId; + } + if (!isReady) { + isReady = true; + readyHandlers.forEach(function (f) { f(); }); + } change(true); }); sframeChan.on('EV_RT_CONNECT', function (ev) { - meta.user.netfluxId = ev.myID; + netfluxId = ev.myID; members = ev.members; + if (!meta.user) { return; } + meta.user.netfluxId = netfluxId; change(true); }); sframeChan.on('EV_RT_JOIN', function (ev) { members.push(ev); + if (!meta.user) { return; } change(false); }); sframeChan.on('EV_RT_LEAVE', function (ev) { var idx = members.indexOf(ev); if (idx === -1) { console.log('Error: ' + ev + ' not in members'); return; } members.splice(idx, 1); + if (!meta.user) { return; } change(false); }); sframeChan.on('EV_RT_DISCONNECT', function () { members = []; + if (!meta.user) { return; } change(true); }); @@ -140,6 +154,10 @@ define(['json.sortify'], function (Sortify) { }, getNetfluxId : function () { return meta.user.netfluxId; + }, + onReady: function (f) { + if (isReady) { return void f(); } + readyHandlers.push(f); } }); }; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 116e0cdd7..1a5086200 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -300,7 +300,6 @@ define([ 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: { @@ -458,7 +457,8 @@ define([ 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']); + var path = data.path || ['root']; + store.userObject.add(id, path); onSync(cb); }); }; @@ -546,7 +546,8 @@ define([ if (!contains) { Store.addPad({ href: href, - title: title + title: title, + path: store.data && store.data.initialPath }, cb); return; } @@ -599,7 +600,6 @@ define([ // Messaging var getMessagingCfg = function () { - console.log(store, store.network); return { proxy: store.proxy, realtime: store.realtime, @@ -624,7 +624,7 @@ define([ 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); }, + pinPads: Store.pinPads, loggedIn: store.loggedIn }); var todo = function () { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index a32cecf3f..ccdf1814d 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -170,6 +170,7 @@ define([ if (cfg.addData) { cfg.addData(metaObj.priv, Cryptpad); } + sframeChan.event('EV_METADATA_UPDATE', metaObj); }); }; @@ -228,7 +229,7 @@ define([ sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) { currentTitle = newTitle; setDocumentTitle(); - Cryptpad.renamePad(newTitle, undefined, function (err) { + Cryptpad.setPadTitle(newTitle, undefined, function (err) { cb(err); }); }); diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index ee2589727..01ae4c38c 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -122,7 +122,6 @@ define([ funcs.getFileSize = function (href, cb) { var channelId = Hash.hrefToHexChannelId(href); funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) { - console.log(data); if (!data) { return void cb("No response"); } if (data.error) { return void cb(data.error); } if (data.response && data.response.length && typeof(data.response[0]) === 'number') { @@ -329,7 +328,7 @@ define([ nThen(function (waitFor) { SFrameChannel.create(window.parent, waitFor(function (sfc) { ctx.sframeChan = sfc; }), true); // CpNfInner.start() should be here.... - }).nThen(function () { + }).nThen(function (waitFor) { localForage.clear(); ctx.metadataMgr = MetadataMgr.create(ctx.sframeChan); @@ -390,6 +389,8 @@ define([ } catch (e) { Feedback.init(false); } }); + ctx.metadataMgr.onReady(waitFor()); + }).nThen(function () { ctx.sframeChan.ready(); cb(funcs); }); diff --git a/www/common/userObject.js b/www/common/userObject.js index 88c354b5b..35d5690b9 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -487,7 +487,6 @@ define([ // FILES DATA exp.pushData = function (data, cb) { - // TODO: can only be called from outside atm if (!pinPads) { return; } if (typeof cb !== "function") { cb = function () {}; } var todo = function () { @@ -498,8 +497,8 @@ define([ if (!loggedIn || !AppConfig.enablePinning || config.testMode) { return void todo(); } - pinPads([Hash.hrefToHexChannelId(data.href)], function (e) { - if (e) { return void cb(e); } + pinPads([Hash.hrefToHexChannelId(data.href)], function (obj) { + if (obj && obj.error) { return void cb(obj.error); } todo(); }); }; diff --git a/www/file/inner.js b/www/file/inner.js index 09a302b0a..0bc7a2000 100644 --- a/www/file/inner.js +++ b/www/file/inner.js @@ -120,7 +120,6 @@ define([ decrypted.callback(); } - console.log(decrypted); $dlview.show(); $dlform.hide(); var $dlButton = $dlview.find('media-tag button'); @@ -174,7 +173,6 @@ define([ var progress = e.originalEvent; var p = progress.percent +'%'; $progress.width(p); - console.log(progress.percent); }); /** From 664625a6ef488a40547887d5d0133cbda37ec3fa Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 30 Nov 2017 18:22:26 +0100 Subject: [PATCH 04/26] Fix more async store issues --- www/common/cryptpad-common.js | 27 +++++++++++++++---------- www/common/outer/async-store.js | 33 +++++++++++++++++++++++++------ www/common/outer/store-rpc.js | 3 +++ www/common/outer/upload.js | 2 +- www/common/sframe-common-outer.js | 9 +++++++-- www/debug/inner.js | 11 +---------- www/profile/main.js | 2 +- 7 files changed, 57 insertions(+), 30 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 14728be3a..45c401175 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -67,7 +67,7 @@ define([ key: [Constants.tokenKey], value: token }, function (obj) { - if (obj.error) { return void cb(obj.error); } + if (obj && obj.error) { return void cb(obj.error); } cb(); }); }; @@ -402,29 +402,36 @@ define([ common.clearOwnedChannel = function (channel, cb) { postMessage("CLEAR_OWNED_CHANNEL", {channel: channel}, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(); + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); + }); + }; + + common.uploadChunk = function (data, cb) { + postMessage("UPLOAD_CHUNK", {chunk: data}, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); }); }; common.uploadComplete = function (cb) { postMessage("UPLOAD_COMPLETE", null, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(); + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); }); }; common.uploadStatus = function (size, cb) { postMessage("UPLOAD_STATUS", {size: size}, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(); + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); }); }; common.uploadCancel = function (cb) { postMessage("UPLOAD_CANCEL", null, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(); + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); }); }; @@ -434,7 +441,7 @@ define([ netfluxId: netfluxId, href: window.location.href }, function (obj) { - if (obj.error) { return void cb(obj.error); } + if (obj && obj.error) { return void cb(obj.error); } cb(); }); }; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 1a5086200..bf7dae60e 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -29,7 +29,7 @@ define([ Store.get = function (key, cb) { - cb({result: Util.find(store.proxy, key)}); + cb(Util.find(store.proxy, key)); }; Store.set = function (data, cb) { var path = data.key.slice(); @@ -164,22 +164,34 @@ define([ Store.clearOwnedChannel = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - store.rpc.clearOwnedChannel(data.channel, cb); + store.rpc.clearOwnedChannel(data.channel, function (err, res) { + if (err) { return void cb({error:err}); } + cb(res); + }); }; Store.uploadComplete = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - store.rpc.uploadComplete(cb); + 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, cb); + 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(cb); + store.rpc.uploadCancel(function (err, res) { + if (err) { return void cb({error:err}); } + cb(res); + }); }; var arePinsSynced = function (cb) { @@ -203,6 +215,15 @@ define([ }); }; + 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) { @@ -692,7 +713,7 @@ define([ postMessage("UPDATE_METADATA"); }); proxy.on('change', [Constants.tokenKey], function () { - postMessage("UPDATE_TOKEN", { data: proxy[Constants.tokenKey] }); + postMessage("UPDATE_TOKEN", { token: proxy[Constants.tokenKey] }); }); }; userObject.migrate(todo); diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 7479b3025..0c0c71221 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -31,6 +31,9 @@ define([ 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; } diff --git a/www/common/outer/upload.js b/www/common/outer/upload.js index 4e42d94da..381c9bc4d 100644 --- a/www/common/outer/upload.js +++ b/www/common/outer/upload.js @@ -20,7 +20,7 @@ define([ var sendChunk = function (box, cb) { var enc = Nacl.util.encodeBase64(box); - common.rpc.send.unauthenticated('UPLOAD', enc, function (e, msg) { + common.uploadChunk(enc, function (e, msg) { cb(e, msg); }); }; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index ccdf1814d..9d245959a 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -114,8 +114,13 @@ define([ }); 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(); @@ -123,8 +128,8 @@ define([ // New pad: create a new random channel id 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 () { var readOnly = secret.keys && !secret.keys.editKeyStr; @@ -247,7 +252,7 @@ define([ cb('ERROR'); return; } - Cryptpad.changeDisplayName(newName, true); + Cryptpad.changeMetadata(); cb(); }); }); diff --git a/www/debug/inner.js b/www/debug/inner.js index f45670b41..bb4e1ecf2 100644 --- a/www/debug/inner.js +++ b/www/debug/inner.js @@ -1,10 +1,8 @@ define([ 'jquery', '/bower_components/chainpad-crypto/crypto.js', - '/bower_components/textpatcher/TextPatcher.js', '/common/toolbar3.js', 'json.sortify', - '/bower_components/chainpad-json-validator/json-ot.js', '/common/common-util.js', '/bower_components/nthen/index.js', '/common/sframe-common.js', @@ -22,10 +20,8 @@ define([ ], function ( $, Crypto, - TextPatcher, Toolbar, JSONSortify, - JsonOT, Util, nThen, SFCommon, @@ -61,7 +57,6 @@ define([ var config = APP.config = { readOnly: readOnly, - transformFunction: JsonOT.validate, // cryptpad debug logging (default is 1) // logLevel: 0, validateContent: function (content) { @@ -123,11 +118,7 @@ define([ config.onReady = function (info) { if (APP.realtime !== info.realtime) { - var realtime = APP.realtime = info.realtime; - APP.patchText = TextPatcher.create({ - realtime: realtime, - //logging: true - }); + APP.realtime = info.realtime; } var userDoc = APP.realtime.getUserDoc(); diff --git a/www/profile/main.js b/www/profile/main.js index a77829242..e325cdf6d 100644 --- a/www/profile/main.js +++ b/www/profile/main.js @@ -49,7 +49,7 @@ define([ editHash = hash; })); }).nThen(function () { - if (!editHash) { + if (editHash) { return void cb(null, Hash.getSecrets('profile', editHash)); } // 3rd case: profile creation (create a new random hash, store it later if needed) From 443eeaa407f3b011ffb2965e01baf2abeab8b90a Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 30 Nov 2017 19:17:59 +0100 Subject: [PATCH 05/26] fancy 404 page --- customize.dist/404.html | 16 ++++++ customize.dist/four-oh-four.js | 58 ++++++++++++++++++++ customize.dist/src/less2/404.less | 40 ++++++++++++++ customize.dist/src/less2/pages/page-404.less | 17 ++++++ 4 files changed, 131 insertions(+) create mode 100644 customize.dist/404.html create mode 100644 customize.dist/four-oh-four.js create mode 100644 customize.dist/src/less2/404.less create mode 100644 customize.dist/src/less2/pages/page-404.less diff --git a/customize.dist/404.html b/customize.dist/404.html new file mode 100644 index 000000000..9298a5377 --- /dev/null +++ b/customize.dist/404.html @@ -0,0 +1,16 @@ + + + + + CryptPad: Zero Knowledge, Collaborative Real Time Editing + + + + + + + diff --git a/customize.dist/four-oh-four.js b/customize.dist/four-oh-four.js new file mode 100644 index 000000000..2f7b6212a --- /dev/null +++ b/customize.dist/four-oh-four.js @@ -0,0 +1,58 @@ +define([ + '/common/hyperscript.js', + + 'less!/customize/src/less2/pages/page-404.less', +], function (h) { + var scramble = h('h2#cp-scramble', "We couldn't find the page you were looking for"); + var title = h('h1#title', "404"); + var content = h('div#cp-main', [ + title, + scramble + ]); + document.body.appendChild(content); + + var die = function (n) { return Math.floor(Math.random() * n); }; + var randomChar = function () { + return String.fromCharCode(die(94) + 34); + }; + var mutate = function (S, i, c) { + var A = S.split(""); + A[i] = c; + return A.join(""); + }; + + var take = function (A) { + var n = die(A.length); + var choice = A[n]; + A.splice(n, 1); + return choice; + }; + + var makeDecryptor = function (el, t, difficulty, cb) { + var Orig = el.innerText; + var options = []; + el.innerText = el.innerText.split("").map(function (c, i) { + Orig[i] = c; + options.push(i); + return randomChar(); + }).join(""); + + return function f () { + if (die(difficulty) === 0) { + var choice = take(options); + el.innerText = mutate(el.innerText, choice, Orig.charAt(choice)); + } else { // make a superficial change + el.innerText = mutate(el.innerText, + options[die(options.length)], + randomChar()); + } + setTimeout(options.length > 0? f: cb, t); + }; + }; + + makeDecryptor(title, 70, 17, function () { })(); + makeDecryptor(scramble, 10, 8, function () { + console.log('done'); + })(); +}); + diff --git a/customize.dist/src/less2/404.less b/customize.dist/src/less2/404.less new file mode 100644 index 000000000..ca1c3503e --- /dev/null +++ b/customize.dist/src/less2/404.less @@ -0,0 +1,40 @@ +@import (once) './include/font.less'; +.font_neuropolitical(); +.font_open-sans(); + +body.cp-page-index { @import "./pages/page-index.less"; } +body.cp-page-contact { @import "./pages/page-contact.less"; } +body.cp-page-login { @import "./pages/page-login.less"; } +body.cp-page-register { @import "./pages/page-register.less"; } +body.cp-page-what-is-cryptpad { @import "./pages/page-what-is-cryptpad.less"; } +body.cp-page-about { @import "./pages/page-about.less"; } +body.cp-page-privacy { @import "./pages/page-privacy.less"; } +body.cp-page-terms { @import "./pages/page-terms.less"; } + +// Set the HTML style for the apps which shouldn't have a body scrollbar +html.cp-app-noscroll { + @import "./include/app-noscroll.less"; + .app-noscroll_main(); +} +// Set the HTML style for printing slides +html.cp-app-print { + @import "./include/app-print.less"; + .app-print_main(); +} + +body.cp-readonly .cp-hidden-if-readonly { display:none !important; } + +body.cp-app-drive { @import "../../../drive/app-drive.less"; } +body.cp-app-pad { @import "../../../pad/app-pad.less"; } +body.cp-app-code { @import "../../../code/app-code.less"; } +body.cp-app-slide { @import "../../../slide/app-slide.less"; } +body.cp-app-file { @import "../../../file/app-file.less"; } +body.cp-app-filepicker { @import "../../../filepicker/app-filepicker.less"; } +body.cp-app-contacts { @import "../../../contacts/app-contacts.less"; } +body.cp-app-poll { @import "../../../poll/app-poll.less"; } +body.cp-app-whiteboard { @import "../../../whiteboard/app-whiteboard.less"; } +body.cp-app-todo { @import "../../../todo/app-todo.less"; } +body.cp-app-profile { @import "../../../profile/app-profile.less"; } +body.cp-app-settings { @import "../../../settings/app-settings.less"; } +body.cp-app-debug { @import "../../../debug/app-debug.less"; } + diff --git a/customize.dist/src/less2/pages/page-404.less b/customize.dist/src/less2/pages/page-404.less new file mode 100644 index 000000000..7bac6d4bd --- /dev/null +++ b/customize.dist/src/less2/pages/page-404.less @@ -0,0 +1,17 @@ +//@import (once) "../include/infopages.less"; +@import (once) "../include/colortheme.less"; + +html, body { + margin: 0; + padding: 0; + #cp-main { + height: 100vh; + width: 100%; + padding-top: 20%; + text-align: center; + #cp-scramble { + font-family: monospace !important; + } + } +} + From b5516ddcd058958fb4d446a7118236ca140dbfa0 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 30 Nov 2017 19:19:21 +0100 Subject: [PATCH 06/26] send 404 page from server --- server.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/server.js b/server.js index baf9b2e99..53e7bb9ad 100644 --- a/server.js +++ b/server.js @@ -156,6 +156,22 @@ app.get('/api/config', function(req, res){ ].join(';\n')); }); +var four04_path = Path.resolve(__dirname + '/customize.dist/404.html'); +var custom_four04_path = Path.resolve(__dirname + '/customize/404.html'); + +var send404 = function (res, path) { + if (!path && path !== four04_path) { path = four04_path; } + Fs.exists(path, function (exists) { + if (exists) { return Fs.createReadStream(path).pipe(res); } + send404(res); + }); +}; + +app.use(function (req, res, next) { + res.status(404); + send404(res, custom_four04_path); +}); + var httpServer = httpsOpts ? Https.createServer(httpsOpts, app) : Http.createServer(app); httpServer.listen(config.httpPort,config.httpAddress,function(){ From bd85f1b003fbc2ef206f357ba5cb293dc4e56834 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 1 Dec 2017 10:16:48 +0100 Subject: [PATCH 07/26] Fix file upload and remove unnecessary dependencies --- www/common/cryptpad-common.js | 7 ++++--- www/common/outer/async-store.js | 2 +- www/common/outer/upload.js | 3 +-- www/common/sframe-common-outer.js | 2 +- www/common/userObject.js | 17 ++++++++--------- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 45c401175..dc8a37025 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -129,7 +129,7 @@ define([ common.getMetadata = function (cb) { postMessage("GET_METADATA", null, function (obj) { - if (obj.error) { return void cb(obj.error); } + if (obj && obj.error) { return void cb(obj.error); } cb(null, obj); }); }; @@ -291,7 +291,7 @@ define([ }; // When opening a new pad or renaming it, store the new title - common.setPadTitle = function (title, padHref, cb) { + common.setPadTitle = function (title, padHref, path, cb) { var href = padHref || window.location.href; var parsed = Hash.parsePadUrl(href); if (!parsed.hash) { return; } @@ -302,7 +302,8 @@ define([ postMessage("SET_PAD_TITLE", { href: href, - title: title + title: title, + path: path }, function (obj) { if (obj && obj.error) { console.log("unable to set pad title"); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index bf7dae60e..b67153830 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -568,7 +568,7 @@ define([ Store.addPad({ href: href, title: title, - path: store.data && store.data.initialPath + path: data.path || (store.data && store.data.initialPath) }, cb); return; } diff --git a/www/common/outer/upload.js b/www/common/outer/upload.js index 381c9bc4d..cce3e6b3c 100644 --- a/www/common/outer/upload.js +++ b/www/common/outer/upload.js @@ -58,8 +58,7 @@ define([ if (noStore) { return void onComplete(href); } - common.initialPath = path; - common.renamePad(title || "", href, function (err) { + common.setPadTitle(title || "", href, path, function (err) { if (err) { return void console.error(err); } onComplete(href); common.setPadAttribute('fileType', metadata.type, null, href); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 9d245959a..76510078b 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -234,7 +234,7 @@ define([ sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) { currentTitle = newTitle; setDocumentTitle(); - Cryptpad.setPadTitle(newTitle, undefined, function (err) { + Cryptpad.setPadTitle(newTitle, undefined, undefined, function (err) { cb(err); }); }); diff --git a/www/common/userObject.js b/www/common/userObject.js index 35d5690b9..d2beb93a9 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -1,12 +1,11 @@ define([ - 'jquery', '/customize/application_config.js', '/common/common-util.js', '/common/common-hash.js', '/common/common-realtime.js', '/common/common-constants.js', '/customize/messages.js' -], function ($, AppConfig, Util, Hash, Realtime, Constants, Messages) { +], function (AppConfig, Util, Hash, Realtime, Constants, Messages) { var module = {}; var ROOT = module.ROOT = "root"; @@ -101,7 +100,7 @@ define([ }; for (var f in element) { if (trashRoot) { - if ($.isArray(element[f])) { + if (Array.isArray(element[f])) { element[f].forEach(addSubfolder); } } else { @@ -119,7 +118,7 @@ define([ }; for (var f in element) { if (trashRoot) { - if ($.isArray(element[f])) { + if (Array.isArray(element[f])) { element[f].forEach(addFile); } } else { @@ -148,14 +147,14 @@ define([ return data.filename || data.title || NEW_FILE_NAME; }; exp.getPadAttribute = function (href, attr, cb) { - cb = cb || $.noop; + cb = cb || function () {}; var id = exp.getIdFromHref(href); if (!id) { return void cb(null, undefined); } var data = getFileData(id); cb(null, clone(data[attr])); }; exp.setPadAttribute = function (href, attr, value, cb) { - cb = cb || $.noop; + cb = cb || function () {}; var id = exp.getIdFromHref(href); if (!id) { return void cb("E_INVAL_HREF"); } if (!attr || !attr.trim()) { return void cb("E_INVAL_ATTR"); } @@ -167,7 +166,7 @@ define([ // PATHS 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; } var result = true; var i = a.length - 1; @@ -265,7 +264,7 @@ define([ } }; for (var e in root) { - if (!$.isArray(root[e])) { + if (!Array.isArray(root[e])) { error("Trash contains a non-array element"); return; } @@ -967,7 +966,7 @@ define([ var addToClean = function (obj, idx, el) { if (typeof(obj) !== "object") { 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") { // We have an old file (href) which is not in filesData: add it var id = Util.createRandomInteger(); From bcdc0d810ad622940218b775c61ea7d3ba2344e3 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 1 Dec 2017 11:51:04 +0100 Subject: [PATCH 08/26] make 404 page translateable, point to home page or drive --- customize.dist/four-oh-four.js | 32 +++++++++++++++++--- customize.dist/src/less2/pages/page-404.less | 32 ++++++++++++++++---- customize.dist/translations/messages.js | 8 +++-- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/customize.dist/four-oh-four.js b/customize.dist/four-oh-four.js index 2f7b6212a..571663a32 100644 --- a/customize.dist/four-oh-four.js +++ b/customize.dist/four-oh-four.js @@ -1,13 +1,33 @@ define([ + '/api/config', '/common/hyperscript.js', + '/common/outer/local-store.js', + '/customize/messages.js', 'less!/customize/src/less2/pages/page-404.less', -], function (h) { - var scramble = h('h2#cp-scramble', "We couldn't find the page you were looking for"); - var title = h('h1#title', "404"); +], function (Config, h, LocalStore, Messages) { + var urlArgs = Config.requireConf.urlArgs; + var img = h('img#cp-logo', { + src: '/customize/cryptpad-new-logo-colors-logoonly.png?' + urlArgs + }); + + var brand = h('h1#cp-brand', 'CryptPad'); + var message = h('h2#cp-scramble', Messages.four04_pageNotFound); + var title = h('h2#cp-title', "404"); + + var loggedIn = LocalStore.isLoggedIn(); + var link = h('a#cp-link', { + href: loggedIn? '/drive/': '/', + }, loggedIn? Messages.four04_goToDrive: Messages.four04_goToHome); + var content = h('div#cp-main', [ + img, + brand, + //h('h1#cp-brand', 'CryptPad'), title, - scramble + message, + //scramble, + link, ]); document.body.appendChild(content); @@ -50,8 +70,10 @@ define([ }; }; + makeDecryptor(brand, 90, 4, function () { })(); makeDecryptor(title, 70, 17, function () { })(); - makeDecryptor(scramble, 10, 8, function () { + makeDecryptor(link, 20, 12, function () {})(); + makeDecryptor(scramble, 30, 8, function () { console.log('done'); })(); }); diff --git a/customize.dist/src/less2/pages/page-404.less b/customize.dist/src/less2/pages/page-404.less index 7bac6d4bd..6c67732fd 100644 --- a/customize.dist/src/less2/pages/page-404.less +++ b/customize.dist/src/less2/pages/page-404.less @@ -1,16 +1,36 @@ -//@import (once) "../include/infopages.less"; @import (once) "../include/colortheme.less"; +@import (once) "../include/font.less"; +.font_neuropolitical(); +.font_open-sans(); html, body { - margin: 0; - padding: 0; + margin: 0px; + padding: 0px; #cp-main { + height: 100vh; + margin: 0px; width: 100%; - padding-top: 20%; + padding-top: 5%; text-align: center; - #cp-scramble { - font-family: monospace !important; + #cp-logo { + display: block; + max-width: 15%; + margin: auto; + } + #cp-brand { + font-family: neuropolitical; + font-size: 40px; + } + #cp-title { + font-size: 30px; + } + #cp-scramble, #cp-link { + font-size: 20px; + } + #cp-title, #cp-scramble, #cp-link { + //font-family: 'Open Sans'; + font-family: monospace; } } } diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 511629de8..c37420611 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -688,10 +688,14 @@ define(function () { out.tos_logs = "Metadata provided by your browser to the server may be logged for the purpose of maintaining the service."; out.tos_3rdparties = "We do not provide individualized data to third parties unless required to by law."; + out.four04_pageNotFound = "We couldn't find the page you were looking for."; + out.four04_goToHome = "Go to the home page."; + out.four04_goToDrive = "Go to my CryptDrive."; + // BottomBar.html - out.bottom_france = 'Made with love in France'; - out.bottom_support = 'An XWiki SAS Labs Project with the support of OpenPaaS-ng'; + //out.bottom_france = 'Made with love in France'; + //out.bottom_support = 'An XWiki SAS Labs Project with the support of OpenPaaS-ng'; // Header.html From c80c97d7db9be26150635b13be85a6e900d0e0b0 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 1 Dec 2017 13:23:23 +0100 Subject: [PATCH 09/26] lint compliance --- customize.dist/four-oh-four.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/customize.dist/four-oh-four.js b/customize.dist/four-oh-four.js index 571663a32..64503409e 100644 --- a/customize.dist/four-oh-four.js +++ b/customize.dist/four-oh-four.js @@ -23,10 +23,8 @@ define([ var content = h('div#cp-main', [ img, brand, - //h('h1#cp-brand', 'CryptPad'), title, message, - //scramble, link, ]); document.body.appendChild(content); @@ -73,7 +71,7 @@ define([ makeDecryptor(brand, 90, 4, function () { })(); makeDecryptor(title, 70, 17, function () { })(); makeDecryptor(link, 20, 12, function () {})(); - makeDecryptor(scramble, 30, 8, function () { + makeDecryptor(message, 12, 8, function () { console.log('done'); })(); }); From 5acbaef28fd42c63bca44423262f15211c537429 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 1 Dec 2017 14:03:13 +0100 Subject: [PATCH 10/26] tweak animation parameters for faster 'decryption' --- customize.dist/four-oh-four.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/customize.dist/four-oh-four.js b/customize.dist/four-oh-four.js index 64503409e..91752b190 100644 --- a/customize.dist/four-oh-four.js +++ b/customize.dist/four-oh-four.js @@ -69,9 +69,9 @@ define([ }; makeDecryptor(brand, 90, 4, function () { })(); - makeDecryptor(title, 70, 17, function () { })(); - makeDecryptor(link, 20, 12, function () {})(); - makeDecryptor(message, 12, 8, function () { + makeDecryptor(title, 50, 14, function () { })(); + makeDecryptor(link, 20, 10, function () {})(); + makeDecryptor(message, 12, 5, function () { console.log('done'); })(); }); From 4903bd0757fd36816e6c8d380577a356d44d6834 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 1 Dec 2017 14:49:21 +0100 Subject: [PATCH 11/26] Enable messenger in async store --- customize.dist/messages.js | 14 +- www/common/common-messenger.js | 23 +-- www/common/common-util.js | 37 ++++ www/common/cryptpad-common.js | 302 ++++++++++++++++++------------ www/common/outer/async-store.js | 196 ++++++++++++++----- www/common/outer/store-rpc.js | 29 ++- www/common/sframe-common-outer.js | 114 +++-------- 7 files changed, 437 insertions(+), 278 deletions(-) diff --git a/customize.dist/messages.js b/customize.dist/messages.js index 7cb43762d..7e84dd01d 100644 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -12,7 +12,7 @@ var map = { var messages = {}; 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 getLanguage = messages._getLanguage = function () { if (window.cryptpadLanguage) { return window.cryptpadLanguage; } @@ -24,19 +24,17 @@ var getLanguage = messages._getLanguage = function () { }; 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'); } -define(req, function($, Default, Language) { +define(req, function(Util, Default, Language) { map.en = 'English'; var defaultLanguage = 'en'; - if (!Language || language === defaultLanguage || !map[language]) { - messages = $.extend(true, messages, Default); - } - else { + Util.extend(messages, Default); + if (Language && language !== defaultLanguage) { // Add the translated keys to the returned object - messages = $.extend(true, messages, Default, Language); + Util.extend(messages, Language); } messages._languages = map; diff --git a/www/common/common-messenger.js b/www/common/common-messenger.js index 09a527bfb..9efe72586 100644 --- a/www/common/common-messenger.js +++ b/www/common/common-messenger.js @@ -5,7 +5,8 @@ define([ '/common/common-hash.js', '/common/common-util.js', '/common/common-realtime.js', -], function ($, Crypto, Curve, Hash, Util, Realtime) { + '/common/common-constants.js', +], function ($, Crypto, Curve, Hash, Util, Realtime, Constants) { 'use strict'; var Msg = { inputs: [], @@ -28,7 +29,7 @@ define([ var createData = Msg.createData = function (proxy, hash) { return { channel: hash || Hash.createChannelId(), - displayName: proxy['cryptpad.username'], + displayName: proxy[Constants.displayNameKey], profile: proxy.profile && proxy.profile.view, edPublic: proxy.edPublic, curvePublic: proxy.curvePublic, @@ -56,7 +57,7 @@ define([ }); }; - Msg.messenger = function (common) { + Msg.messenger = function (store) { var messenger = { handlers: { message: [], @@ -89,9 +90,9 @@ define([ var joining = {}; // declare common variables - var network = common.getNetwork(); - var proxy = common.getProxy(); - var realtime = common.getRealtime(); + var network = store.network; + var proxy = store.proxy; + var realtime = store.realtime; Msg.hk = network.historyKeeper; var friends = getFriendList(proxy); @@ -626,18 +627,10 @@ define([ }); }; - // TODO displayName messenger.getMyInfo = function (cb) { cb(void 0, { curvePublic: proxy.curvePublic, - displayName: '' //common.getDisplayName(), - }); - }; - - messenger.clearOwnedChannel = function (channel, cb) { - common.clearOwnedChannel(channel, function (e) { - if (e) { return void cb(e); } - cb(); + displayName: proxy[Constants.displayNameKey] }); }; diff --git a/www/common/common-util.js b/www/common/common-util.js index 759730663..030536751 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -209,6 +209,43 @@ define([], function () { 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; }); }(self)); diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index dc8a37025..ee04ee19b 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -47,6 +47,15 @@ define([ var PINNING_ENABLED = AppConfig.enablePinning; + // COMMON + common.getLanguage = function () { + return Messages._languageUsed; + }; + common.setLanguage = function (l, cb) { + Language.setLanguage(l, null, cb); + }; + + // RESTRICTED // Settings only common.getUserObject = function (cb) { @@ -119,14 +128,125 @@ define([ }; - // REFACTOR pull language directly? - common.getLanguage = function () { - return Messages._languageUsed; + // RPC + common.pinPads = function (pads, cb) { + postMessage("PIN_PADS", pads, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj.hash); + }); }; - common.setLanguage = function (l, cb) { - Language.setLanguage(l, null, cb); + + common.unpinPads = function (pads, cb) { + postMessage("UNPIN_PADS", pads, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj.hash); + }); + }; + + common.getPinnedUsage = function (cb) { + postMessage("GET_PINNED_USAGE", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(null, obj.bytes); + }); + }; + + common.updatePinLimit = function (cb) { + postMessage("UPDATE_PIN_LIMIT", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(undefined, obj.limit, obj.plan, obj.note); + }); + }; + + common.getPinLimit = function (cb) { + postMessage("GET_PIN_LIMIT", null, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(undefined, obj.limit, obj.plan, obj.note); + }); }; + common.isOverPinLimit = function (cb) { + if (!LocalStore.isLoggedIn()) { return void cb(null, false); } + var usage; + var andThen = function (e, limit, plan) { + if (e) { return void cb(e); } + var data = {usage: usage, limit: limit, plan: plan}; + if (usage > limit) { + return void cb (null, true, data); + } + return void cb (null, false, data); + }; + var todo = function (e, used) { + if (e) { return void cb(e); } + usage = used; + common.getPinLimit(andThen); + }; + common.getPinnedUsage(todo); + }; + + common.clearOwnedChannel = function (channel, cb) { + postMessage("CLEAR_OWNED_CHANNEL", channel, cb); + }; + + common.uploadComplete = function (cb) { + postMessage("UPLOAD_COMPLETE", null, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); + }); + }; + + common.uploadStatus = function (size, cb) { + postMessage("UPLOAD_STATUS", {size: size}, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); + }); + }; + + common.uploadCancel = function (cb) { + postMessage("UPLOAD_CANCEL", null, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); + }); + }; + + common.uploadChunk = function (data, cb) { + postMessage("UPLOAD_CHUNK", {chunk: data}, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); + }); + }; + + // ANON RPC + + // SFRAME: talk to anon_rpc from the iframe + common.anonRpcMsg = function (msg, data, cb) { + if (!msg) { return; } + postMessage("ANON_RPC_MESSAGE", { + msg: msg, + data: data + }, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); + }); + }; + + common.getFileSize = function (href, cb) { + postMessage("GET_FILE_SIZE", {href: href}, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(undefined, obj.size); + }); + }; + + common.getMultipleFileSize = function (files, cb) { + postMessage("GET_MULTIPLE_FILE_SIZE", {files:files}, function (obj) { + if (obj.error) { return void cb(obj.error); } + cb(undefined, obj.size); + }); + }; + + // Store + + + common.getMetadata = function (cb) { postMessage("GET_METADATA", null, function (obj) { if (obj && obj.error) { return void cb(obj.error); } @@ -138,7 +258,6 @@ define([ postMessage("SET_DISPLAY_NAME", value, cb); }; - // STORAGE common.setPadAttribute = function (attr, value, cb, href) { href = Hash.getRelativeHref(href || window.location.href); postMessage("SET_PAD_ATTRIBUTE", { @@ -178,7 +297,6 @@ define([ }); }; - // Tags common.resetTags = function (href, tags, cb) { // set pad attribute @@ -320,133 +438,52 @@ define([ }); }; - common.pinPads = function (pads, cb) { - postMessage("PIN_PADS", pads, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(null, obj.hash); - }); - }; - - common.unpinPads = function (pads, cb) { - postMessage("UNPIN_PADS", pads, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(null, obj.hash); - }); - }; - - common.getPinnedUsage = function (cb) { - postMessage("GET_PINNED_USAGE", null, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(null, obj.bytes); - }); - }; - - // SFRAME: talk to anon_rpc from the iframe - common.anonRpcMsg = function (msg, data, cb) { - if (!msg) { return; } - postMessage("ANON_RPC_MESSAGE", { - msg: msg, - data: data + // Messaging (manage friends from the userlist) + common.inviteFromUserlist = function (netfluxId, cb) { + postMessage("INVITE_FROM_USERLIST", { + netfluxId: netfluxId, + href: window.location.href }, function (obj) { if (obj && obj.error) { return void cb(obj.error); } - cb(null, obj); + cb(); }); }; - common.getFileSize = function (href, cb) { - postMessage("GET_FILE_SIZE", {href: href}, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(undefined, obj.size); - }); + // Messenger + var messenger = common.messenger = {}; + messenger.getFriendList = function (cb) { + postMessage("CONTACTS_GET_FRIEND_LIST", null, cb); }; - - // TODO not used anymore? - common.getMultipleFileSize = function (files, cb) { - postMessage("GET_MULTIPLE_FILE_SIZE", {files:files}, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(undefined, obj.size); - }); + messenger.getMyInfo = function (cb) { + postMessage("CONTACTS_GET_MY_INFO", null, cb); }; - - common.updatePinLimit = function (cb) { - postMessage("UPDATE_PIN_LIMIT", null, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(undefined, obj.limit, obj.plan, obj.note); - }); + messenger.getFriendInfo = function (curvePublic, cb) { + postMessage("CONTACTS_GET_FRIEND_INFO", curvePublic, cb); }; - - common.getPinLimit = function (cb) { - postMessage("GET_PIN_LIMIT", null, function (obj) { - if (obj.error) { return void cb(obj.error); } - cb(undefined, obj.limit, obj.plan, obj.note); - }); + messenger.removeFriend = function (curvePublic, cb) { + postMessage("CONTACTS_REMOVE_FRIEND", curvePublic, cb); }; - - common.isOverPinLimit = function (cb) { - if (!LocalStore.isLoggedIn()) { return void cb(null, false); } - var usage; - var andThen = function (e, limit, plan) { - if (e) { return void cb(e); } - var data = {usage: usage, limit: limit, plan: plan}; - if (usage > limit) { - return void cb (null, true, data); - } - return void cb (null, false, data); - }; - var todo = function (e, used) { - if (e) { return void cb(e); } - usage = used; - common.getPinLimit(andThen); - }; - common.getPinnedUsage(todo); + messenger.openFriendChannel = function (curvePublic, cb) { + postMessage("CONTACTS_OPEN_FRIEND_CHANNEL", curvePublic, cb); }; - - common.clearOwnedChannel = function (channel, cb) { - postMessage("CLEAR_OWNED_CHANNEL", {channel: channel}, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(null, obj); - }); + messenger.getFriendStatus = function (curvePublic, cb) { + postMessage("CONTACTS_GET_FRIEND_STATUS", curvePublic, cb); }; - - common.uploadChunk = function (data, cb) { - postMessage("UPLOAD_CHUNK", {chunk: data}, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(null, obj); - }); + messenger.getMoreHistory = function (data, cb) { + postMessage("CONTACTS_GET_MORE_HISTORY", data, cb); }; - - common.uploadComplete = function (cb) { - postMessage("UPLOAD_COMPLETE", null, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(null, obj); - }); + messenger.sendMessage = function (data, cb) { + postMessage("CONTACTS_SEND_MESSAGE", data, cb); }; - - common.uploadStatus = function (size, cb) { - postMessage("UPLOAD_STATUS", {size: size}, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(null, obj); - }); - }; - - common.uploadCancel = function (cb) { - postMessage("UPLOAD_CANCEL", null, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(null, obj); - }); - }; - - // Messaging - common.inviteFromUserlist = function (netfluxId, cb) { - postMessage("INVITE_FROM_USERLIST", { - netfluxId: netfluxId, - href: window.location.href - }, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(); - }); + messenger.setChannelHead = function (data, cb) { + postMessage("CONTACTS_SET_CHANNEL_HEAD", data, cb); }; - + messenger.onMessageEvent = Util.mkEvent(); + messenger.onJoinEvent = Util.mkEvent(); + messenger.onLeaveEvent = Util.mkEvent(); + messenger.onUpdateEvent = Util.mkEvent(); + messenger.onFriendEvent = Util.mkEvent(); + messenger.onUnfriendEvent = Util.mkEvent(); // HERE common.getShareHashes = function (secret, cb) { @@ -540,6 +577,25 @@ define([ common.onFriendComplete(data); break; } + // Messenger + case 'CONTACTS_MESSAGE': { + common.messenger.onMessageEvent.fire(data); break; + } + case 'CONTACTS_JOIN': { + common.messenger.onJoinEvent.fire(data); break; + } + case 'CONTACTS_LEAVE': { + common.messenger.onLeaveEvent.fire(data); break; + } + case 'CONTACTS_UPDATE': { + common.messenger.onUpdateEvent.fire(data); break; + } + case 'CONTACTS_FRIEND': { + common.messenger.onFriendEvent.fire(data); break; + } + case 'CONTACTS_UNFRIEND': { + common.messenger.onUnfriendEvent.fire(data); break; + } } }; @@ -547,7 +603,7 @@ define([ var env = {}; var initialized = false; - return function (f) { + return function (f, rdyCfg) { if (initialized) { return void setTimeout(function () { f(void 0, env); }); } @@ -583,7 +639,9 @@ define([ query: onMessage, // TODO temporary, will be replaced by a webworker channel userHash: LocalStore.getUserHash(), anonHash: LocalStore.getFSHash(), - localToken: tryParsing(localStorage.getItem(Constants.tokenKey)) + localToken: tryParsing(localStorage.getItem(Constants.tokenKey)), + language: common.getLanguage(), + messenger: rdyCfg.messenger }; if (sessionStorage[Constants.newPadPathKey]) { cfg.initialPath = sessionStorage[Constants.newPadPathKey]; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index b67153830..fe94d1a38 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -7,12 +7,14 @@ define([ '/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, NetConfig, +], function (UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger, + NetConfig, Crypto, ChainPad, Listmap) { var Store = {}; @@ -164,9 +166,8 @@ define([ Store.clearOwnedChannel = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - store.rpc.clearOwnedChannel(data.channel, function (err, res) { - if (err) { return void cb({error:err}); } - cb(res); + store.rpc.clearOwnedChannel(data, function (err) { + cb({error:err}); }); }; @@ -333,10 +334,25 @@ define([ 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); + 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); + }); }; /** @@ -414,6 +430,12 @@ define([ 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 @@ -463,27 +485,6 @@ define([ cb(all); }; - 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); - }); - }; - // Templates Store.getTemplates = function (data, cb) { var templateFiles = store.userObject.getFiles(['template']); @@ -606,20 +607,7 @@ define([ cb(list); }; - // 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(); - }; - - // Messaging + // Messaging (manage friends from the userlist) var getMessagingCfg = function () { return { proxy: store.proxy, @@ -642,6 +630,91 @@ define([ 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, { @@ -791,7 +864,7 @@ define([ initialized = true; postMessage = function (cmd, d, cb) { setTimeout(function () { - data.query(cmd, d, cb); // TODO temporary, will be rzplaced by webworker channel + data.query(cmd, d, cb); // TODO temporary, will be replaced by webworker channel }); }; @@ -806,7 +879,40 @@ define([ 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, + }); + }); + } }); }; diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 0c0c71221..c0b435079 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -118,7 +118,34 @@ define([ 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; diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 76510078b..c9c6c9526 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -33,7 +33,6 @@ define([ '/common/cryptget.js', '/common/sframe-channel.js', '/filepicker/main.js', - //'/common/common-messenger.js', '/common/common-messaging.js', '/common/common-notifier.js', '/common/common-hash.js', @@ -45,7 +44,7 @@ define([ '/common/outer/network-config.js', '/bower_components/netflux-websocket/netflux-client.js', ], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel, - _FilePicker, /*_Messenger,*/ _Messaging, _Notifier, _Hash, _Util, _Realtime, + _FilePicker, _Messaging, _Notifier, _Hash, _Util, _Realtime, _Constants, _Feedback, _LocalStore, NetConfig, Netflux) { CpNfOuter = _CpNfOuter; Cryptpad = _Cryptpad; @@ -53,7 +52,6 @@ define([ Cryptget = _Cryptget; SFrameChannel = _SFrameChannel; FilePicker = _FilePicker; - //Messenger = _Messenger; Messaging = _Messaging; Notifier = _Notifier; Utils.Hash = _Hash; @@ -87,7 +85,9 @@ define([ SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) { sframeChan = sfc; }), 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) { @@ -505,119 +505,59 @@ define([ } if (cfg.messaging) { - // TODO make messenger work with async store - /*var messenger = Messenger.messenger(Cryptpad); - sframeChan.on('Q_CONTACTS_GET_FRIEND_LIST', function (data, cb) { - messenger.getFriendList(function (e, keys) { - cb({ - error: e, - data: keys, - }); - }); + Cryptpad.messenger.getFriendList(cb); }); sframeChan.on('Q_CONTACTS_GET_MY_INFO', function (data, cb) { - messenger.getMyInfo(function (e, info) { - cb({ - error: e, - data: info, - }); - }); + Cryptpad.messenger.getMyInfo(cb); }); sframeChan.on('Q_CONTACTS_GET_FRIEND_INFO', function (curvePublic, cb) { - messenger.getFriendInfo(curvePublic, function (e, info) { - cb({ - error: e, - data: info, - }); - }); + Cryptpad.messenger.getFriendInfo(curvePublic, cb); }); sframeChan.on('Q_CONTACTS_REMOVE_FRIEND', function (curvePublic, cb) { - messenger.removeFriend(curvePublic, function (e, info) { - cb({ - error: e, - data: info, - }); - }); + Cryptpad.messenger.removeFriend(curvePublic, cb); }); sframeChan.on('Q_CONTACTS_OPEN_FRIEND_CHANNEL', function (curvePublic, cb) { - messenger.openFriendChannel(curvePublic, function (e) { - cb({ error: e, }); - }); + Cryptpad.messenger.openFriendChannel(curvePublic, cb); }); sframeChan.on('Q_CONTACTS_GET_STATUS', function (curvePublic, cb) { - messenger.getStatus(curvePublic, function (e, online) { - cb({ - error: e, - data: online, - }); - }); + Cryptpad.messenger.getFriendStatus(curvePublic, cb); }); sframeChan.on('Q_CONTACTS_GET_MORE_HISTORY', function (opt, cb) { - messenger.getMoreHistory(opt.curvePublic, opt.sig, opt.count, function (e, history) { - cb({ - error: e, - data: history, - }); - }); + Cryptpad.messenger.getMoreHistory(opt, cb); }); sframeChan.on('Q_CONTACTS_SEND_MESSAGE', function (opt, cb) { - messenger.sendMessage(opt.curvePublic, opt.content, function (e) { - cb({ - error: e, - }); - }); + Cryptpad.messenger.sendMessage(opt, cb); }); sframeChan.on('Q_CONTACTS_SET_CHANNEL_HEAD', function (opt, cb) { - messenger.setChannelHead(opt.curvePublic, opt.sig, function (e) { - cb({ - error: e - }); - }); + Cryptpad.messenger.setChannelHead(opt, cb); }); sframeChan.on('Q_CONTACTS_CLEAR_OWNED_CHANNEL', function (channel, cb) { - messenger.clearOwnedChannel(channel, function (e) { - cb({ - error: e, - }); - }); + Cryptpad.clearOwnedChannel(channel, cb); }); - messenger.on('message', function (message) { - sframeChan.event('EV_CONTACTS_MESSAGE', message); + Cryptpad.messenger.onMessageEvent.reg(function (data) { + sframeChan.event('EV_CONTACTS_MESSAGE', data); }); - messenger.on('join', function (curvePublic, channel) { - sframeChan.event('EV_CONTACTS_JOIN', { - curvePublic: curvePublic, - channel: channel, - }); + Cryptpad.messenger.onJoinEvent.reg(function (data) { + sframeChan.event('EV_CONTACTS_JOIN', data); }); - messenger.on('leave', function (curvePublic, channel) { - sframeChan.event('EV_CONTACTS_LEAVE', { - curvePublic: curvePublic, - channel: channel, - }); + Cryptpad.messenger.onLeaveEvent.reg(function (data) { + sframeChan.event('EV_CONTACTS_LEAVE', data); }); - messenger.on('update', function (info, curvePublic) { - sframeChan.event('EV_CONTACTS_UPDATE', { - curvePublic: curvePublic, - info: info, - }); + Cryptpad.messenger.onUpdateEvent.reg(function (data) { + sframeChan.event('EV_CONTACTS_UPDATE', data); }); - messenger.on('friend', function (curvePublic) { - sframeChan.event('EV_CONTACTS_FRIEND', { - curvePublic: curvePublic, - }); + Cryptpad.messenger.onFriendEvent.reg(function (data) { + sframeChan.event('EV_CONTACTS_FRIEND', data); + }); + Cryptpad.messenger.onUnfriendEvent.reg(function (data) { + sframeChan.event('EV_CONTACTS_UNFRIEND', data); }); - messenger.on('unfriend', function (curvePublic) { - sframeChan.event('EV_CONTACTS_UNFRIEND', { - curvePublic: curvePublic, - }); - });*/ } sframeChan.ready(); From c6b8bbea59a3fc795ffd0df01397abce91fc43f8 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 1 Dec 2017 14:56:34 +0100 Subject: [PATCH 12/26] Fix tests --- www/common/userObject.js | 2 +- www/drive/tests.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/www/common/userObject.js b/www/common/userObject.js index d2beb93a9..344677ea8 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -486,7 +486,6 @@ define([ // FILES DATA exp.pushData = function (data, cb) { - if (!pinPads) { return; } if (typeof cb !== "function") { cb = function () {}; } var todo = function () { var id = Util.createRandomInteger(); @@ -496,6 +495,7 @@ define([ if (!loggedIn || !AppConfig.enablePinning || config.testMode) { return void todo(); } + if (!pinPads) { return; } pinPads([Hash.hrefToHexChannelId(data.href)], function (obj) { if (obj && obj.error) { return void cb(obj.error); } todo(); diff --git a/www/drive/tests.js b/www/drive/tests.js index 222d1b905..7132fd8a7 100644 --- a/www/drive/tests.js +++ b/www/drive/tests.js @@ -325,7 +325,12 @@ define([ var fo = FO.init(files, config); fo.fixFiles(); - var data = Cryptpad.makePad(href5, 'Title5'); + var data = { + href: href5, + title: 'Title5', + atime: +new Date(), + ctime: +new Date() + }; var res; var id5; // pushData is synchronous in test mode (no pinning) From c59c6072f3e2ac2c8a238c594979c47d960f0493 Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 1 Dec 2017 16:05:20 +0100 Subject: [PATCH 13/26] Remove jquery from outer --- www/common/common-language.js | 1 - www/common/common-messenger.js | 5 +- www/common/cryptpad-common.js | 12 +- www/common/dom-ready.js | 10 + www/common/fsStore.js | 360 -------------------------------- www/common/outer/async-store.js | 2 +- www/common/sframe-app-outer.js | 10 +- www/contacts/main.js | 8 +- www/drive/main.js | 8 +- www/file/main.js | 8 +- www/poll/main.js | 8 +- www/profile/main.js | 8 +- www/settings/main.js | 8 +- www/todo/main.js | 8 +- www/whiteboard/main.js | 8 +- 15 files changed, 54 insertions(+), 410 deletions(-) create mode 100644 www/common/dom-ready.js delete mode 100644 www/common/fsStore.js diff --git a/www/common/common-language.js b/www/common/common-language.js index 1af06df33..8b27a64bf 100644 --- a/www/common/common-language.js +++ b/www/common/common-language.js @@ -10,7 +10,6 @@ define([ // Add handler to the language selector Msg.setLanguage = function (l, sframeChan, cb) { - console.log(sframeChan); if (sframeChan) { // We're in the sandbox sframeChan.query("Q_LANGUAGE_SET", l, cb); diff --git a/www/common/common-messenger.js b/www/common/common-messenger.js index 9efe72586..cd0a5626d 100644 --- a/www/common/common-messenger.js +++ b/www/common/common-messenger.js @@ -1,12 +1,11 @@ define([ - 'jquery', '/bower_components/chainpad-crypto/crypto.js', '/common/curve.js', '/common/common-hash.js', '/common/common-util.js', '/common/common-realtime.js', '/common/common-constants.js', -], function ($, Crypto, Curve, Hash, Util, Realtime, Constants) { +], function (Crypto, Curve, Hash, Util, Realtime, Constants) { 'use strict'; var Msg = { inputs: [], @@ -485,7 +484,7 @@ define([ }; var msg = ['GET_HISTORY', chan.id, cfg]; network.sendto(network.historyKeeper, JSON.stringify(msg)) - .then($.noop, function (err) { + .then(function () {}, function (err) { throw new Error(err); }); }; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index ee04ee19b..aeadac105 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -5,7 +5,6 @@ define([ '/common/common-hash.js', '/common/common-messaging.js', '/common/common-realtime.js', - '/common/common-language.js', '/common/common-constants.js', '/common/common-feedback.js', '/common/outer/local-store.js', @@ -15,7 +14,7 @@ define([ '/customize/application_config.js', '/bower_components/nthen/index.js', ], function (Config, Messages, Util, Hash, - Messaging, Realtime, Language, Constants, Feedback, LocalStore, AStore, + Messaging, Realtime, Constants, Feedback, LocalStore, AStore, Pinpad, AppConfig, Nthen) { /* This file exposes functionality which is specific to Cryptpad, but not to @@ -52,7 +51,9 @@ define([ return Messages._languageUsed; }; common.setLanguage = function (l, cb) { - Language.setLanguage(l, null, cb); + var LS_LANG = "CRYPTPAD_LANG"; + localStorage.setItem(LS_LANG, l); + cb(); }; @@ -743,10 +744,5 @@ define([ }()); - // MAGIC that happens implicitly - /*$(function () { - Language.applyTranslation(); - });*/ - return common; }); diff --git a/www/common/dom-ready.js b/www/common/dom-ready.js new file mode 100644 index 000000000..69a484493 --- /dev/null +++ b/www/common/dom-ready.js @@ -0,0 +1,10 @@ +define(function () { + return { + onReady: function (cb) { + if (document.readyState === 'complete') { return void cb(); } + document.onreadystatechange = function () { + if (document.readyState === 'complete') { cb(); } + }; + } + }; +}); diff --git a/www/common/fsStore.js b/www/common/fsStore.js deleted file mode 100644 index 27ed42d34..000000000 --- a/www/common/fsStore.js +++ /dev/null @@ -1,360 +0,0 @@ -define([ - 'jquery', - '/bower_components/chainpad-listmap/chainpad-listmap.js', - '/bower_components/chainpad-crypto/crypto.js?v=0.1.5', - '/common/userObject.js', - '/common/common-interface.js', - '/common/common-hash.js', - '/common/common-util.js', - '/common/common-constants.js', - '/common/migrate-user-object.js', - '/bower_components/chainpad/chainpad.dist.js', - '/common/outer/network-config.js', - '/common/outer/local-store.js', -], function ($, Listmap, Crypto, FO, UI, Hash, Util, Constants, Migrate, ChainPad, NetConfig, - LocalStore) { - /* - This module uses localStorage, which is synchronous, but exposes an - asyncronous API. This is so that we can substitute other storage - methods. - - To override these methods, create another file at: - /customize/storage.js - */ - - var Store = {}; - var store; - - var initStore = function (filesOp, storeObj, exp) { - var ret = {}; - - var safeSet = function (key, val) { - storeObj[key] = val; - }; - - // Store uses nodebacks... - ret.set = function (key, val, cb) { - safeSet(key, val); - cb(); - }; - - // implement in alternative store - ret.setBatch = function (map, cb) { - Object.keys(map).forEach(function (key) { - safeSet(key, map[key]); - }); - cb(void 0, map); - }; - - ret.setDrive = function (key, val, cb) { - storeObj.drive[key] = val; - cb(); - }; - - var safeGet = function (key) { - return storeObj[key]; - }; - - ret.get = function (key, cb) { - cb(void 0, safeGet(key)); - }; - - // implement in alternative store - ret.getBatch = function (keys, cb) { - var res = {}; - keys.forEach(function (key) { - res[key] = safeGet(key); - }); - cb(void 0, res); - }; - - 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] - }; - }; - ret.setAttribute = function (attr, value, cb) { - try { - var object = getAttributeObject(attr); - object.obj[object.key] = value; - } catch (e) { return void cb(e); } - cb(); - }; - ret.getAttribute = function (attr, cb) { - var object; - try { - object = getAttributeObject(attr); - } catch (e) { return void cb(e); } - cb(null, object.obj[object.key]); - }; - ret.setPadAttribute = filesOp.setPadAttribute; - ret.getPadAttribute = filesOp.getPadAttribute; - ret.getIdFromHref = filesOp.getIdFromHref; - - ret.getDrive = function (key, cb) { - cb(void 0, storeObj.drive[key]); - }; - - var safeRemove = function (key) { - delete storeObj[key]; - }; - - ret.remove = function (key, cb) { - safeRemove(key); - cb(); - }; - - // implement in alternative store - ret.removeBatch = function (keys, cb) { - keys.forEach(function (key) { - safeRemove(key); - }); - cb(); - }; - - ret.keys = function (cb) { - cb(void 0, Object.keys(storeObj)); - }; - - ret.removeData = filesOp.removeData; - ret.pushData = filesOp.pushData; - ret.addPad = filesOp.add; - - ret.forgetPad = function (href, cb) { - filesOp.forget(href); - cb(); - }; - - ret.listTemplates = function () { - 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; - }; - - ret.getProxy = function () { - return exp; - }; - - ret.getLoginName = function () { - return storeObj.login_name; - }; - - ret.repairDrive = function () { - filesOp.fixFiles(); - }; - - ret.getEmptyObject = function () { - return filesOp.getStructure(); - }; - - ret.replace = filesOp.replace; - - ret.restoreHref = filesOp.restoreHref; - - ret.changeHandlers = []; - - ret.change = function () {}; - - ret.getProfile = function () { - return storeObj.profile; - }; - - return ret; - }; - - var tryParsing = function (x) { - try { return JSON.parse(x); } - catch (e) { - console.error(e); - return null; - } - }; - - var onReady = function (f, proxy, Cryptpad, exp) { - var fo = exp.userObject = exp.fo = FO.init(proxy.drive, { - pinPads: Cryptpad.pinPads, - loggedIn: LocalStore.isLoggedIn() - }); - var todo = function () { - fo.fixFiles(); - - Migrate(proxy, Cryptpad); - - store = initStore(fo, proxy, exp); - if (typeof(f) === 'function') { - f(void 0, store); - } - //storeObj = proxy; - - var requestLogin = function () { - // log out so that you don't go into an endless loop... - LocalStore.logout(); - - // redirect them to log in, and come back when they're done. - sessionStorage.redirectTo = window.location.href; - window.location.href = '/login/'; - }; - - var tokenKey = 'loginToken'; - if (LocalStore.isLoggedIn()) { - /* 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[tokenKey] = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER); - } - - // copy User_hash into sessionStorage because cross-domain iframes - // on safari replaces localStorage with sessionStorage or something - if (sessionStorage) { sessionStorage.setItem('User_hash', localStorage.getItem('User_hash')); } - - var localToken = tryParsing(localStorage.getItem(tokenKey)); - if (localToken === null) { - // if that number hasn't been set to localStorage, do so. - localStorage.setItem(tokenKey, proxy.loginToken); - } else if (localToken !== proxy[tokenKey]) { - // if it has been, and 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; - } - - 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 (LocalStore.isLoggedIn() && (!Cryptpad.hasSigningKeys(proxy) || - !Cryptpad.hasCurveKeys(proxy))) { - return void requestLogin(); - } - - proxy.on('change', [Constants.displayNameKey], function (o, n) { - if (typeof(n) !== "string") { return; } - Cryptpad.changeMetadata(); - }); - proxy.on('change', ['profile'], function () { - // Trigger userlist update when the avatar has changed - Cryptpad.changeMetadata(); - }); - proxy.on('change', ['friends'], function () { - // Trigger userlist update when the friendlist has changed - Cryptpad.changeMetadata(); - }); - proxy.on('change', [tokenKey], function () { - var localToken = tryParsing(localStorage.getItem(tokenKey)); - if (localToken !== proxy[tokenKey]) { - return void requestLogin(); - } - }); - }; - fo.migrate(todo); - }; - - var initialized = false; - - var init = function (f, Cryptpad) { - if (!Cryptpad || initialized) { return; } - initialized = true; - - var hash = LocalStore.getUserHash() || LocalStore.getFSHash() || Hash.createRandomHash(); - 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 exp = {}; - - var rt = window.rt = Listmap.create(listmapConfig); - - exp.realtime = rt.realtime; - exp.proxy = rt.proxy; - exp.loggedIn = Cryptpad.isLoggedIn(); - rt.proxy.on('create', function (info) { - exp.info = info; - if (!LocalStore.getUserHash()) { - LocalStore.setFSHash(Hash.getEditHashFromKeys(info.channel, secret.keys)); - } - }).on('ready', function () { - if (store) { 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] = []; - onReady(f, rt.proxy, Cryptpad, exp); - return; - } - // Drive already exist: return the existing drive, don't load data from legacy store - onReady(f, rt.proxy, Cryptpad, exp); - }) - .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(); - UI.alert(Cryptpad.Messages.fs_migration, null, true); - } - }); - }; - - Store.ready = function (f, Cryptpad) { - if (store) { // Store.ready probably called twice, store already ready - if (typeof(f) === 'function') { - f(void 0, store); - } - } else { - init(f, Cryptpad); - } - }; - - return Store; -}); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index fe94d1a38..7415843b1 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -328,7 +328,7 @@ define([ edPublic: store.proxy.edPublic, friends: store.proxy.friends, settings: store.proxy.settings, - thumbnails: !((store.proxy.settings || {}).general || {}).disableThumbnails + thumbnails: !Util.find(store.proxy, ['settings', 'general', 'disableThumbnails']) } }; cb(JSON.parse(JSON.stringify(metadata))); diff --git a/www/common/sframe-app-outer.js b/www/common/sframe-app-outer.js index 3c675a4c3..d0dcb631c 100644 --- a/www/common/sframe-app-outer.js +++ b/www/common/sframe-app-outer.js @@ -2,14 +2,14 @@ define([ '/bower_components/nthen/index.js', '/api/config', - 'jquery', + '/common/dom-ready.js', '/common/requireconfig.js', '/common/sframe-common-outer.js' -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { +], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); nThen(function (waitFor) { - $(waitFor()); + DomReady.onReady(waitFor()); }).nThen(function (waitFor) { var req = { cfg: requireConfig, @@ -18,7 +18,7 @@ define([ }; window.rc = requireConfig; window.apiconf = ApiConfig; - $('#sbox-iframe').attr('src', + document.getElementById('sbox-iframe').setAttribute('src', ApiConfig.httpSafeOrigin + window.location.pathname + 'inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); @@ -37,4 +37,4 @@ define([ }).nThen(function (/*waitFor*/) { SFCommonO.start(); }); -}); \ No newline at end of file +}); diff --git a/www/contacts/main.js b/www/contacts/main.js index f99e7d472..38d6c5e71 100644 --- a/www/contacts/main.js +++ b/www/contacts/main.js @@ -2,15 +2,15 @@ define([ '/bower_components/nthen/index.js', '/api/config', - 'jquery', + '/common/dom-ready.js', '/common/requireconfig.js', '/common/sframe-common-outer.js' -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { +], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); // Loaded in load #2 nThen(function (waitFor) { - $(waitFor()); + DomReady.onReady(waitFor()); }).nThen(function (waitFor) { var req = { cfg: requireConfig, @@ -19,7 +19,7 @@ define([ }; window.rc = requireConfig; window.apiconf = ApiConfig; - $('#sbox-iframe').attr('src', + document.getElementById('sbox-iframe').setAttribute('src', ApiConfig.httpSafeOrigin + '/contacts/inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); diff --git a/www/drive/main.js b/www/drive/main.js index 01fc98554..de668fc4d 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -2,15 +2,15 @@ define([ '/bower_components/nthen/index.js', '/api/config', - 'jquery', + '/common/dom-ready.js', '/common/requireconfig.js', '/common/sframe-common-outer.js', -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { +], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); // Loaded in load #2 nThen(function (waitFor) { - $(waitFor()); + DomReady.onReady(waitFor()); }).nThen(function (waitFor) { var req = { cfg: requireConfig, @@ -19,7 +19,7 @@ define([ }; window.rc = requireConfig; window.apiconf = ApiConfig; - $('#sbox-iframe').attr('src', + document.getElementById('sbox-iframe').setAttribute('src', ApiConfig.httpSafeOrigin + '/drive/inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); diff --git a/www/file/main.js b/www/file/main.js index 0af6558ef..e59957299 100644 --- a/www/file/main.js +++ b/www/file/main.js @@ -2,15 +2,15 @@ define([ '/bower_components/nthen/index.js', '/api/config', - 'jquery', + '/common/dom-ready.js', '/common/requireconfig.js', '/common/sframe-common-outer.js' -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { +], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); // Loaded in load #2 nThen(function (waitFor) { - $(waitFor()); + DomReady.onReady(waitFor()); }).nThen(function (waitFor) { var req = { cfg: requireConfig, @@ -19,7 +19,7 @@ define([ }; window.rc = requireConfig; window.apiconf = ApiConfig; - $('#sbox-iframe').attr('src', + document.getElementById('sbox-iframe').setAttribute('src', ApiConfig.httpSafeOrigin + '/file/inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); diff --git a/www/poll/main.js b/www/poll/main.js index 04c9b8f2f..737038ead 100644 --- a/www/poll/main.js +++ b/www/poll/main.js @@ -2,15 +2,15 @@ define([ '/bower_components/nthen/index.js', '/api/config', - 'jquery', + '/common/dom-ready.js', '/common/requireconfig.js', '/common/sframe-common-outer.js', -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { +], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); // Loaded in load #2 nThen(function (waitFor) { - $(waitFor()); + DomReady.onReady(waitFor()); }).nThen(function (waitFor) { var req = { cfg: requireConfig, @@ -19,7 +19,7 @@ define([ }; window.rc = requireConfig; window.apiconf = ApiConfig; - $('#sbox-iframe').attr('src', + document.getElementById('sbox-iframe').setAttribute('src', ApiConfig.httpSafeOrigin + '/poll/inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); diff --git a/www/profile/main.js b/www/profile/main.js index e325cdf6d..22823d0d9 100644 --- a/www/profile/main.js +++ b/www/profile/main.js @@ -2,15 +2,15 @@ define([ '/bower_components/nthen/index.js', '/api/config', - 'jquery', + '/common/dom-ready.js', '/common/requireconfig.js', '/common/sframe-common-outer.js', -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { +], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); // Loaded in load #2 nThen(function (waitFor) { - $(waitFor()); + DomReady.onReady(waitFor()); }).nThen(function (waitFor) { var req = { cfg: requireConfig, @@ -19,7 +19,7 @@ define([ }; window.rc = requireConfig; window.apiconf = ApiConfig; - $('#sbox-iframe').attr('src', + document.getElementById('sbox-iframe').setAttribute('src', ApiConfig.httpSafeOrigin + '/profile/inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); diff --git a/www/settings/main.js b/www/settings/main.js index 7075ccc8c..42f501075 100644 --- a/www/settings/main.js +++ b/www/settings/main.js @@ -2,15 +2,15 @@ define([ '/bower_components/nthen/index.js', '/api/config', - 'jquery', + '/common/dom-ready.js', '/common/requireconfig.js', '/common/sframe-common-outer.js' -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { +], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); // Loaded in load #2 nThen(function (waitFor) { - $(waitFor()); + DomReady.onReady(waitFor()); }).nThen(function (waitFor) { var req = { cfg: requireConfig, @@ -19,7 +19,7 @@ define([ }; window.rc = requireConfig; window.apiconf = ApiConfig; - $('#sbox-iframe').attr('src', + document.getElementById('sbox-iframe').setAttribute('src', ApiConfig.httpSafeOrigin + '/settings/inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); diff --git a/www/todo/main.js b/www/todo/main.js index 9caeb16ef..2898434fd 100644 --- a/www/todo/main.js +++ b/www/todo/main.js @@ -2,15 +2,15 @@ define([ '/bower_components/nthen/index.js', '/api/config', - 'jquery', + '/common/dom-ready.js', '/common/requireconfig.js', '/common/sframe-common-outer.js' -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { +], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); // Loaded in load #2 nThen(function (waitFor) { - $(waitFor()); + DomReady.onReady(waitFor()); }).nThen(function (waitFor) { var req = { cfg: requireConfig, @@ -19,7 +19,7 @@ define([ }; window.rc = requireConfig; window.apiconf = ApiConfig; - $('#sbox-iframe').attr('src', + document.getElementById('sbox-iframe').setAttribute('src', ApiConfig.httpSafeOrigin + '/todo/inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); diff --git a/www/whiteboard/main.js b/www/whiteboard/main.js index 4c8a3887c..ce1f14d9c 100644 --- a/www/whiteboard/main.js +++ b/www/whiteboard/main.js @@ -2,15 +2,15 @@ define([ '/bower_components/nthen/index.js', '/api/config', - 'jquery', + '/common/dom-ready.js', '/common/requireconfig.js', '/common/sframe-common-outer.js' -], function (nThen, ApiConfig, $, RequireConfig, SFCommonO) { +], function (nThen, ApiConfig, DomReady, RequireConfig, SFCommonO) { var requireConfig = RequireConfig(); // Loaded in load #2 nThen(function (waitFor) { - $(waitFor()); + DomReady.onReady(waitFor()); }).nThen(function (waitFor) { var req = { cfg: requireConfig, @@ -19,7 +19,7 @@ define([ }; window.rc = requireConfig; window.apiconf = ApiConfig; - $('#sbox-iframe').attr('src', + document.getElementById('sbox-iframe').setAttribute('src', ApiConfig.httpSafeOrigin + '/whiteboard/inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); From afcc888a8b88771e0e76db89c67150221385305c Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 4 Dec 2017 10:16:17 +0100 Subject: [PATCH 14/26] make flat dom quiet again --- www/common/flat-dom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/flat-dom.js b/www/common/flat-dom.js index 51c3d07b3..25b51d786 100644 --- a/www/common/flat-dom.js +++ b/www/common/flat-dom.js @@ -33,7 +33,7 @@ define([], function () { data.map[id] = el.textContent; return id; } - if (!el || !el.attributes) { return void console.error(el); } + if (!el || !el.attributes) { return; } id = uid(); data.map[id] = [ el.tagName, From d3d9e2e7715f7cbdf9182a7676d663e1947f01cd Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 4 Dec 2017 11:10:25 +0100 Subject: [PATCH 15/26] WIP: refactoring rpc code --- www/assert/main.js | 49 +++++++++++++++++++++++- www/common/wire.js | 94 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 www/common/wire.js diff --git a/www/assert/main.js b/www/assert/main.js index 968200dc9..56cc29116 100644 --- a/www/assert/main.js +++ b/www/assert/main.js @@ -6,8 +6,9 @@ define([ '/common/test.js', '/common/common-hash.js', '/common/common-thumbnail.js', + '/common/wire.js', '/common/flat-dom.js', -], function ($, Hyperjson, Sortify, Drive, Test, Hash, Thumb, Flat) { +], function ($, Hyperjson, Sortify, Drive, Test, Hash, Thumb, Wire, Flat) { window.Hyperjson = Hyperjson; window.Sortify = Sortify; @@ -30,7 +31,7 @@ define([ ASSERTS.forEach(function (f, index) { f(function (err) { - console.log("test " + index); + //console.log("test " + index); done(err, index); }, index); }); @@ -235,6 +236,50 @@ define([ return cb(true); }, "version 2 hash failed to parse correctly"); + assert(function (cb) { + Wire.create({ + constructor: function (cb) { + var service = function (type, data, cb) { + switch (type) { + case "HEY_BUDDY": + return cb(void 0, "SALUT!"); + default: + cb("ERROR"); + } + }; + + var respond; + cb(void 0, { + send: function (raw, cb) { + try { + var parsed = JSON.parse(raw); + var txid = parsed.txid; + var message = parsed.message; + setTimeout(function () { + service(message.command, message.content, function (e, result) { + respond(JSON.stringify({ + txid: txid, + error: e, + content: result, + })); + }); + }); + } catch (e) { console.error("PEWPEW"); } + }, + receive: function (f) { + respond = f; + }, + }); + }, + }, function (e, rpc) { + if (e) { return cb(false); } + rpc.send('HEY_BUDDY', null, function (e, out) { + if (e) { return void cb(false); } + if (out === 'SALUT!') { cb(true); } + }); + }); + }, "Test rpc factory"); + /* assert(function (cb) { var getBlob = function (url, cb) { diff --git a/www/common/wire.js b/www/common/wire.js new file mode 100644 index 000000000..e0a3530b2 --- /dev/null +++ b/www/common/wire.js @@ -0,0 +1,94 @@ +define([ + +], function () { + var Wire = {}; + + /* MISSION: write a generic RPC framework + +Requirements + +* some transmission methods can be interrupted + * handle disconnects and reconnects +* handle callbacks +* configurable timeout +* Service should expose 'addClient' method + * and handle broadcast + + +* + + */ + + var uid = function () { + return Number(Math.floor(Math.random () * + Number.MAX_SAFE_INTEGER)).toString(32); + }; + +/* +opt = { + send: function () { + + }, + receive: function () { + + }, + constructor: function (cb) { + cb(void 0 , { + send: function (content, cb) { + + }, + receive: function () { + + } + }); + }, +}; +*/ + + Wire.create = function (opt, cb) { + var ctx = {}; + var pending = ctx.pending = {}; + ctx.connected = false; + + var rpc = {}; + + opt.constructor(function (e, service) { + if (e) { return setTimeout(function () { cb(e); }); } + + rpc.send = function (type, data, cb) { + var txid = uid(); + if (typeof(cb) !== 'function') { + throw new Error('expected callback'); + } + + ctx.pending[txid] = function (err, response) { + cb(err, response); + }; + + service.send(JSON.stringify({ + txid: txid, + message: { + command: type, + content: data, + }, + })); + }; + + service.receive(function (raw) { + try { + var data = JSON.parse(raw); + var txid = data.txid; + if (!txid) { throw new Error('NO_TXID'); } + var cb = pending[txid]; + if (data.error) { return void cb(data.error); } + cb(void 0, data.content); + } catch (e) { console.error("UNHANDLED_MESSAGE", data); } + }); + + cb(void 0, rpc); + }); + }; + + + return Wire; +}); From cd2920826776b46660a51a4b399dc4c4d4bd636c Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 4 Dec 2017 11:46:15 +0100 Subject: [PATCH 16/26] fix toolbar's incorrect gotoDrive title --- www/common/toolbar3.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index b06d358ba..5bc74b44f 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -747,7 +747,7 @@ define([ var origin = privateData.origin; var pathname = privateData.pathname; var href = inDrive.test(pathname) ? origin+'/index.html' : origin+'/drive/'; - var buttonTitle = inDrive ? Messages.header_homeTitle : Messages.header_logoTitle; + var buttonTitle = inDrive.test(pathname) ? Messages.header_homeTitle : Messages.header_logoTitle; var $aTag = $('', { href: href, From 0b54a76211225b24d9b848d29ecbf6e24bf41329 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 4 Dec 2017 13:58:33 +0100 Subject: [PATCH 17/26] lint compliance --- www/assert/main.js | 13 +- www/common/common-messenger2.js | 754 ++++++++++++++++++++++++++++++++ www/common/wire.js | 2 +- 3 files changed, 764 insertions(+), 5 deletions(-) create mode 100644 www/common/common-messenger2.js diff --git a/www/assert/main.js b/www/assert/main.js index 56cc29116..e88e13fd1 100644 --- a/www/assert/main.js +++ b/www/assert/main.js @@ -5,10 +5,11 @@ define([ '/drive/tests.js', '/common/test.js', '/common/common-hash.js', + '/common/common-util.js', '/common/common-thumbnail.js', '/common/wire.js', '/common/flat-dom.js', -], function ($, Hyperjson, Sortify, Drive, Test, Hash, Thumb, Wire, Flat) { +], function ($, Hyperjson, Sortify, Drive, Test, Hash, Util, Thumb, Wire, Flat) { window.Hyperjson = Hyperjson; window.Sortify = Sortify; @@ -248,9 +249,13 @@ define([ } }; - var respond; + + var evt = Util.mkEvent(); + var respond = function (e, out) { + evt.fire(e, out); + }; cb(void 0, { - send: function (raw, cb) { + send: function (raw /*, cb */) { try { var parsed = JSON.parse(raw); var txid = parsed.txid; @@ -267,7 +272,7 @@ define([ } catch (e) { console.error("PEWPEW"); } }, receive: function (f) { - respond = f; + evt.reg(f); }, }); }, diff --git a/www/common/common-messenger2.js b/www/common/common-messenger2.js new file mode 100644 index 000000000..be9a68972 --- /dev/null +++ b/www/common/common-messenger2.js @@ -0,0 +1,754 @@ +define([ + 'jquery', + '/bower_components/chainpad-crypto/crypto.js', + '/common/curve.js', + '/common/common-hash.js', + '/bower_components/chainpad-listmap/chainpad-listmap.js', +], function ($, Crypto, Curve, Hash, Listmap) { + 'use strict'; + var Msg = { + inputs: [], + }; + + var Types = { + message: 'MSG', + update: 'UPDATE', + unfriend: 'UNFRIEND', + mapId: 'MAP_ID', + mapIdAck: 'MAP_ID_ACK' + }; + + var clone = function (o) { + return JSON.parse(JSON.stringify(o)); + }; + + // TODO + // - mute a channel (hide notifications or don't open it?) + var createData = Msg.createData = function (proxy, hash) { + return { + channel: hash || Hash.createChannelId(), + displayName: proxy['cryptpad.username'], + profile: proxy.profile && proxy.profile.view, + edPublic: proxy.edPublic, + curvePublic: proxy.curvePublic, + avatar: proxy.profile && proxy.profile.avatar + }; + }; + + var getFriend = function (proxy, pubkey) { + if (pubkey === proxy.curvePublic) { + var data = createData(proxy); + delete data.channel; + return data; + } + return proxy.friends ? proxy.friends[pubkey] : undefined; + }; + + var getFriendList = Msg.getFriendList = function (proxy) { + if (!proxy.friends) { proxy.friends = {}; } + return proxy.friends; + }; + + var eachFriend = function (friends, cb) { + Object.keys(friends).forEach(function (id) { + if (id === 'me') { return; } + cb(friends[id], id, friends); + }); + }; + + Msg.getFriendChannelsList = function (proxy) { + var list = []; + eachFriend(proxy, function (friend) { + list.push(friend.channel); + }); + return list; + }; + + var msgAlreadyKnown = function (channel, sig) { + return channel.messages.some(function (message) { + return message[0] === sig; + }); + }; + + Msg.messenger = function (common) { + var messenger = { + handlers: { + message: [], + join: [], + leave: [], + update: [], + friend: [], + unfriend: [], + }, + range_requests: {}, + }; + + var eachHandler = function (type, g) { + messenger.handlers[type].forEach(g); + }; + + messenger.on = function (type, f) { + var stack = messenger.handlers[type]; + if (!Array.isArray(stack)) { + return void console.error('unsupported message type'); + } + if (typeof(f) !== 'function') { + return void console.error('expected function'); + } + stack.push(f); + }; + + var channels = messenger.channels = {}; + + var joining = {}; + + // declare common variables + var network = common.getNetwork(); + var proxy = common.getProxy(); + var realtime = common.getRealtime(); + Msg.hk = network.historyKeeper; + var friends = getFriendList(proxy); + + var getChannel = function (curvePublic) { + var friend = friends[curvePublic]; + if (!friend) { return; } + var chanId = friend.channel; + if (!chanId) { return; } + return channels[chanId]; + }; + + var initRangeRequest = function (txid, curvePublic, sig, cb) { + messenger.range_requests[txid] = { + messages: [], + cb: cb, + curvePublic: curvePublic, + sig: sig, + }; + }; + + var getRangeRequest = function (txid) { + return messenger.range_requests[txid]; + }; + + var deleteRangeRequest = function (txid) { + delete messenger.range_requests[txid]; + }; + + messenger.getMoreHistory = function (curvePublic, hash, count, cb) { + if (typeof(cb) !== 'function') { return; } + + if (typeof(hash) !== 'string') { + // FIXME hash is not necessarily defined. + // What does this mean? + console.error("not sure what to do here"); + return; + } + + var chan = getChannel(curvePublic); + if (typeof(chan) === 'undefined') { + console.error("chan is undefined. we're going to have a problem here"); + return; + } + + var txid = common.uid(); + initRangeRequest(txid, curvePublic, hash, cb); + var msg = [ 'GET_HISTORY_RANGE', chan.id, { + from: hash, + count: count, + txid: txid, + } + ]; + + network.sendto(network.historyKeeper, JSON.stringify(msg)).then(function () { + }, function (err) { + throw new Error(err); + }); + }; + + var getCurveForChannel = function (id) { + var channel = channels[id]; + if (!channel) { return; } + return channel.curve; + }; + + messenger.getChannelHead = function (curvePublic, cb) { + var friend = friends[curvePublic]; + if (!friend) { return void cb('NO_SUCH_FRIEND'); } + cb(void 0, friend.lastKnownHash); + }; + + messenger.setChannelHead = function (curvePublic, hash, cb) { + var friend = friends[curvePublic]; + if (!friend) { return void cb('NO_SUCH_FRIEND'); } + friend.lastKnownHash = hash; + cb(); + }; + + // Id message allows us to map a netfluxId with a public curve key + var onIdMessage = function (msg, sender) { + var channel; + var isId = Object.keys(channels).some(function (chanId) { + if (channels[chanId].userList.indexOf(sender) !== -1) { + channel = channels[chanId]; + return true; + } + }); + + if (!isId) { return; } + + var decryptedMsg = channel.encryptor.decrypt(msg); + + if (decryptedMsg === null) { + return void console.error("Failed to decrypt message"); + } + + if (!decryptedMsg) { + console.error('decrypted message was falsey but not null'); + return; + } + + var parsed; + try { + parsed = JSON.parse(decryptedMsg); + } catch (e) { + console.error(decryptedMsg); + return; + } + if (parsed[0] !== Types.mapId && parsed[0] !== Types.mapIdAck) { return; } + + // check that the responding peer's encrypted netflux id matches + // the sender field. This is to prevent replay attacks. + if (parsed[2] !== sender || !parsed[1]) { return; } + channel.mapId[sender] = parsed[1]; + eachHandler('join', function (f) { + f(parsed[1], channel.id); + }); + + if (parsed[0] !== Types.mapId) { return; } // Don't send your key if it's already an ACK + // Answer with your own key + var rMsg = [Types.mapIdAck, proxy.curvePublic, channel.wc.myID]; + var rMsgStr = JSON.stringify(rMsg); + var cryptMsg = channel.encryptor.encrypt(rMsgStr); + network.sendto(sender, cryptMsg); + }; + + var orderMessages = function (curvePublic, new_messages /*, sig */) { + var channel = getChannel(curvePublic); + var messages = channel.messages; + + // TODO improve performance, guarantee correct ordering + new_messages.reverse().forEach(function (msg) { + messages.unshift(msg); + }); + }; + + var removeFromFriendList = function (curvePublic, cb) { + if (!proxy.friends) { return; } + var friends = proxy.friends; + delete friends[curvePublic]; + common.whenRealtimeSyncs(realtime, cb); + }; + + var pushMsg = function (channel, cryptMsg) { + var msg = channel.encryptor.decrypt(cryptMsg); + var sig = cryptMsg.slice(0, 64); + if (msgAlreadyKnown(channel, sig)) { return; } + + var parsedMsg = JSON.parse(msg); + var curvePublic; + if (parsedMsg[0] === Types.message) { + // TODO validate messages here + var res = { + type: parsedMsg[0], + sig: sig, + author: parsedMsg[1], + time: parsedMsg[2], + text: parsedMsg[3], + // this makes debugging a whole lot easier + curve: getCurveForChannel(channel.id), + }; + + channel.messages.push(res); + eachHandler('message', function (f) { + f(res); + }); + + return true; + } + if (parsedMsg[0] === Types.update) { + if (parsedMsg[1] === proxy.curvePublic) { return; } + curvePublic = parsedMsg[1]; + var newdata = parsedMsg[3]; + var data = getFriend(proxy, parsedMsg[1]); + var types = []; + Object.keys(newdata).forEach(function (k) { + if (data[k] !== newdata[k]) { + types.push(k); + data[k] = newdata[k]; + } + }); + + eachHandler('update', function (f) { + f(clone(newdata), curvePublic); + }); + return; + } + if (parsedMsg[0] === Types.unfriend) { + curvePublic = parsedMsg[1]; + delete friends[curvePublic]; + + removeFromFriendList(parsedMsg[1], function () { + channel.wc.leave(Types.unfriend); + eachHandler('unfriend', function (f) { + f(curvePublic); + }); + }); + return; + } + }; + + /* Broadcast a display name, profile, or avatar change to all contacts + */ + + // TODO send event... + messenger.updateMyData = function () { + var friends = getFriendList(proxy); + var mySyncData = friends.me; + var myData = createData(proxy); + if (!mySyncData || mySyncData.displayName !== myData.displayName + || mySyncData.profile !== myData.profile + || mySyncData.avatar !== myData.avatar) { + delete myData.channel; + Object.keys(channels).forEach(function (chan) { + var channel = channels[chan]; + + if (!channel) { + return void console.error('NO_SUCH_CHANNEL'); + } + + + var msg = [Types.update, myData.curvePublic, +new Date(), myData]; + var msgStr = JSON.stringify(msg); + var cryptMsg = channel.encryptor.encrypt(msgStr); + channel.wc.bcast(cryptMsg).then(function () { + // TODO send event + //channel.refresh(); + }, function (err) { + console.error(err); + }); + }); + eachHandler('update', function (f) { + f(myData, myData.curvePublic); + }); + friends.me = myData; + } + }; + + var onChannelReady = function (chanId) { + var cb = joining[chanId]; + if (typeof(cb) !== 'function') { + return void console.error('channel ready without callback'); + } + delete joining[chanId]; + return cb(); + }; + + var onDirectMessage = function (common, msg, sender) { + if (sender !== Msg.hk) { return void onIdMessage(msg, sender); } + var parsed = JSON.parse(msg); + + if (/HISTORY_RANGE/.test(parsed[0])) { + //console.log(parsed); + var txid = parsed[1]; + var req = getRangeRequest(txid); + var type = parsed[0]; + if (!req) { + return void console.error("received response to unknown request"); + } + + if (type === 'HISTORY_RANGE') { + req.messages.push(parsed[2]); + } else if (type === 'HISTORY_RANGE_END') { + // process all the messages (decrypt) + var curvePublic = req.curvePublic; + var channel = getChannel(curvePublic); + + var decrypted = req.messages.map(function (msg) { + if (msg[2] !== 'MSG') { return; } + try { + return { + d: JSON.parse(channel.encryptor.decrypt(msg[4])), + sig: msg[4].slice(0, 64), + }; + } catch (e) { + console.log('failed to decrypt'); + return null; + } + }).filter(function (decrypted) { + return decrypted; + }).map(function (O) { + return { + type: O.d[0], + sig: O.sig, + author: O.d[1], + time: O.d[2], + text: O.d[3], + curve: curvePublic, + }; + }); + + orderMessages(curvePublic, decrypted, req.sig); + req.cb(void 0, decrypted); + return deleteRangeRequest(txid); + } else { + console.log(parsed); + } + return; + } + + if ((parsed.validateKey || parsed.owners) && parsed.channel) { + return; + } + if (parsed.state && parsed.state === 1 && parsed.channel) { + if (channels[parsed.channel]) { + // parsed.channel is Ready + // channel[parsed.channel].ready(); + channels[parsed.channel].ready = true; + onChannelReady(parsed.channel); + var updateTypes = channels[parsed.channel].updateOnReady; + if (updateTypes) { + + //channels[parsed.channel].updateUI(updateTypes); + } + } + return; + } + var chan = parsed[3]; + if (!chan || !channels[chan]) { return; } + pushMsg(channels[chan], parsed[4]); + }; + + var onMessage = function (msg, sender, chan) { + if (!channels[chan.id]) { return; } + + var isMessage = pushMsg(channels[chan.id], msg); + if (isMessage) { + if (channels[chan.id].wc.myID !== sender) { + // Don't notify for your own messages + //channels[chan.id].notify(); + } + //channels[chan.id].refresh(); + // TODO emit message event + } + }; + + // listen for messages... + network.on('message', function(msg, sender) { + onDirectMessage(common, msg, sender); + }); + + messenger.removeFriend = function (curvePublic, cb) { + if (typeof(cb) !== 'function') { throw new Error('NO_CALLBACK'); } + var data = getFriend(proxy, curvePublic); + + if (!data) { + // friend is not valid + console.error('friend is not valid'); + return; + } + + var channel = channels[data.channel]; + if (!channel) { + return void cb("NO_SUCH_CHANNEL"); + } + + if (!network.webChannels.some(function (wc) { + return wc.id === channel.id; + })) { + console.error('bad channel: ', curvePublic); + } + + var msg = [Types.unfriend, proxy.curvePublic, +new Date()]; + var msgStr = JSON.stringify(msg); + var cryptMsg = channel.encryptor.encrypt(msgStr); + + // TODO emit remove_friend event? + try { + channel.wc.bcast(cryptMsg).then(function () { + delete friends[curvePublic]; + delete channels[curvePublic]; + common.whenRealtimeSyncs(realtime, function () { + cb(); + }); + }, function (err) { + console.error(err); + cb(err); + }); + } catch (e) { + cb(e); + } + }; + + var getChannelMessagesSince = function (chan, data, keys) { + console.log('Fetching [%s] messages since [%s]', data.curvePublic, data.lastKnownHash || ''); + var cfg = { + validateKey: keys.validateKey, + owners: [proxy.edPublic, data.edPublic], + lastKnownHash: data.lastKnownHash + }; + var msg = ['GET_HISTORY', chan.id, cfg]; + network.sendto(network.historyKeeper, JSON.stringify(msg)) + .then($.noop, function (err) { + throw new Error(err); + }); + }; + + var openFriendChannel = function (data, f) { + var keys = Curve.deriveKeys(data.curvePublic, proxy.curvePrivate); + var encryptor = Curve.createEncryptor(keys); + network.join(data.channel).then(function (chan) { + var channel = channels[data.channel] = { + id: data.channel, + sending: false, + friendEd: f, + keys: keys, + curve: data.curvePublic, + encryptor: encryptor, + messages: [], + wc: chan, + userList: [], + mapId: {}, + send: function (payload, cb) { + if (!network.webChannels.some(function (wc) { + if (wc.id === channel.wc.id) { return true; } + })) { + return void cb('NO_SUCH_CHANNEL'); + } + + var msg = [Types.message, proxy.curvePublic, +new Date(), payload]; + var msgStr = JSON.stringify(msg); + var cryptMsg = channel.encryptor.encrypt(msgStr); + + channel.wc.bcast(cryptMsg).then(function () { + pushMsg(channel, cryptMsg); + cb(); + }, function (err) { + cb(err); + }); + } + }; + chan.on('message', function (msg, sender) { + onMessage(msg, sender, chan); + }); + + var onJoining = function (peer) { + if (peer === Msg.hk) { return; } + if (channel.userList.indexOf(peer) !== -1) { return; } + + channel.userList.push(peer); + var msg = [Types.mapId, proxy.curvePublic, chan.myID]; + var msgStr = JSON.stringify(msg); + var cryptMsg = channel.encryptor.encrypt(msgStr); + network.sendto(peer, cryptMsg); + }; + chan.members.forEach(function (peer) { + if (peer === Msg.hk) { return; } + if (channel.userList.indexOf(peer) !== -1) { return; } + channel.userList.push(peer); + }); + chan.on('join', onJoining); + chan.on('leave', function (peer) { + var curvePublic = channel.mapId[peer]; + var i = channel.userList.indexOf(peer); + while (i !== -1) { + channel.userList.splice(i, 1); + i = channel.userList.indexOf(peer); + } + // update status + if (!curvePublic) { return; } + eachHandler('leave', function (f) { + f(curvePublic, channel.id); + }); + }); + + // FIXME don't subscribe to the channel implicitly + getChannelMessagesSince(chan, data, keys); + }, function (err) { + console.error(err); + }); + }; + + messenger.getFriendList = function (cb) { + var friends = proxy.friends; + if (!friends) { return void cb(void 0, []); } + + cb(void 0, Object.keys(proxy.friends).filter(function (k) { + return k !== 'me'; + })); + }; + + messenger.openFriendChannel = function (curvePublic, cb) { + if (typeof(curvePublic) !== 'string') { return void cb('INVALID_ID'); } + if (typeof(cb) !== 'function') { throw new Error('expected callback'); } + + var friend = clone(friends[curvePublic]); + if (typeof(friend) !== 'object') { + return void cb('NO_FRIEND_DATA'); + } + var channel = friend.channel; + if (!channel) { return void cb('E_NO_CHANNEL'); } + joining[channel] = cb; + openFriendChannel(friend, curvePublic); + }; + + messenger.sendMessage = function (curvePublic, payload, cb) { + var channel = getChannel(curvePublic); + if (!channel) { return void cb('NO_CHANNEL'); } + if (!network.webChannels.some(function (wc) { + if (wc.id === channel.wc.id) { return true; } + })) { + return void cb('NO_SUCH_CHANNEL'); + } + + var msg = [Types.message, proxy.curvePublic, +new Date(), payload]; + var msgStr = JSON.stringify(msg); + var cryptMsg = channel.encryptor.encrypt(msgStr); + + channel.wc.bcast(cryptMsg).then(function () { + pushMsg(channel, cryptMsg); + cb(); + }, function (err) { + cb(err); + }); + }; + + messenger.getStatus = function (curvePublic, cb) { + var channel = getChannel(curvePublic); + if (!channel) { return void cb('NO_SUCH_CHANNEL'); } + var online = channel.userList.some(function (nId) { + return channel.mapId[nId] === curvePublic; + }); + cb(void 0, online); + }; + + messenger.getFriendInfo = function (curvePublic, cb) { + setTimeout(function () { + var friend = friends[curvePublic]; + if (!friend) { return void cb('NO_SUCH_FRIEND'); } + // this clone will be redundant when ui uses postmessage + cb(void 0, clone(friend)); + }); + }; + + messenger.getMyInfo = function (cb) { + cb(void 0, { + curvePublic: proxy.curvePublic, + displayName: common.getDisplayName(), + }); + }; + + // TODO listen for changes to your friend list + // emit 'update' events for clients + + //var update = function (curvePublic + proxy.on('change', ['friends'], function (o, n, p) { + var curvePublic; + if (o === undefined) { + // new friend added + curvePublic = p.slice(-1)[0]; + eachHandler('friend', function (f) { + f(curvePublic, clone(n)); + }); + return; + } + + console.error(o, n, p); + }).on('remove', ['friends'], function (o, p) { + eachHandler('unfriend', function (f) { + f(p[1]); // TODO + }); + }); + + Object.freeze(messenger); + + return messenger; + }; + + // createInvite(theirPublicKey, function (e, data) { /* data.url */ }); + window.createInvitation = Msg.createInvitation = function (common, theirPublic, cb) { + +// theirPublic 'DHpRgRclYEvKdZO2gnN8bUyf6A4mEo8RmUGlGi9ChGo=' + + var proxy = common.getProxy(); + var myPublic = proxy.curvePublic; + + var mySecret = proxy.curvePrivate; + var keys = Curve.deriveKeys(theirPublic, mySecret); + var encryptor = Curve.createEncryptor(keys); + + var channel = common.createChannelId(); + + // create a channel encrypted with your curve private and their curvePublic + // TODO support 'owners'. You should be able to delete the channel. + var ListmapConfig = { + data: {}, + network: common.getNetwork(), + channel: channel, + readOnly: false, + validateKey: keys.validateKey, + crypto: encryptor, + username: 'invite-author', + logLevel: 1, + }; + + var lm = Listmap.create(ListmapConfig); + + var inviteProxy = lm.proxy; + inviteProxy.on('create', function () { + console.log('created'); + }) + .on('ready', function () { + /* do your stuff ... + then, once everything is synchronized, abort */ + + // a random channel + var destinationChannel = common.createChannelId(); + + // store all of the information necessary for them to become your friend + var myData = createData(proxy, destinationChannel); + Object.keys(myData).forEach(function (k) { + inviteProxy[k] = myData[k]; + }); + + //console.log(inviteProxy); + var urlSafeChannel = common.hexToBase64(destinationChannel); + var urlSafePublic = myPublic.replace(/\//g, '-'); + + var res = { + url: '/invite/#/1/' + urlSafeChannel + '/' + urlSafePublic + '/', + }; + +// "/invite/#/1/ilrOtygzDVoUSRpOOJrUuQ/e8jvf36S3chzkkcaMrLSW7PPrz7VDp85lIFNI26dTmr=/" + cb(void 0, res); + +/* + * add their key to your 'friends' object as a pending request? + * create a read-only link to that channel cb(void 0, { url: HERE }) + * /invite/#/1/invite/channel/yourCurvePublicKey +*/ + +/* UI + * copy link to clipboard + * instruct user to send that link to the other user out-of-band +*/ + + }) + .on('disconnect', function () { + }) + .on('change', [], function (/*o, n, p*/) { + + }); + }; + + return Msg; +}); diff --git a/www/common/wire.js b/www/common/wire.js index e0a3530b2..c4b59cfae 100644 --- a/www/common/wire.js +++ b/www/common/wire.js @@ -82,7 +82,7 @@ opt = { var cb = pending[txid]; if (data.error) { return void cb(data.error); } cb(void 0, data.content); - } catch (e) { console.error("UNHANDLED_MESSAGE", data); } + } catch (e) { console.error("UNHANDLED_MESSAGE", raw); } }); cb(void 0, rpc); From 34223e4b78bd859c3ec46ce9b0ba137b121a4835 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 4 Dec 2017 13:59:57 +0100 Subject: [PATCH 18/26] remove accidental inclusion --- www/common/common-messenger2.js | 754 -------------------------------- 1 file changed, 754 deletions(-) delete mode 100644 www/common/common-messenger2.js diff --git a/www/common/common-messenger2.js b/www/common/common-messenger2.js deleted file mode 100644 index be9a68972..000000000 --- a/www/common/common-messenger2.js +++ /dev/null @@ -1,754 +0,0 @@ -define([ - 'jquery', - '/bower_components/chainpad-crypto/crypto.js', - '/common/curve.js', - '/common/common-hash.js', - '/bower_components/chainpad-listmap/chainpad-listmap.js', -], function ($, Crypto, Curve, Hash, Listmap) { - 'use strict'; - var Msg = { - inputs: [], - }; - - var Types = { - message: 'MSG', - update: 'UPDATE', - unfriend: 'UNFRIEND', - mapId: 'MAP_ID', - mapIdAck: 'MAP_ID_ACK' - }; - - var clone = function (o) { - return JSON.parse(JSON.stringify(o)); - }; - - // TODO - // - mute a channel (hide notifications or don't open it?) - var createData = Msg.createData = function (proxy, hash) { - return { - channel: hash || Hash.createChannelId(), - displayName: proxy['cryptpad.username'], - profile: proxy.profile && proxy.profile.view, - edPublic: proxy.edPublic, - curvePublic: proxy.curvePublic, - avatar: proxy.profile && proxy.profile.avatar - }; - }; - - var getFriend = function (proxy, pubkey) { - if (pubkey === proxy.curvePublic) { - var data = createData(proxy); - delete data.channel; - return data; - } - return proxy.friends ? proxy.friends[pubkey] : undefined; - }; - - var getFriendList = Msg.getFriendList = function (proxy) { - if (!proxy.friends) { proxy.friends = {}; } - return proxy.friends; - }; - - var eachFriend = function (friends, cb) { - Object.keys(friends).forEach(function (id) { - if (id === 'me') { return; } - cb(friends[id], id, friends); - }); - }; - - Msg.getFriendChannelsList = function (proxy) { - var list = []; - eachFriend(proxy, function (friend) { - list.push(friend.channel); - }); - return list; - }; - - var msgAlreadyKnown = function (channel, sig) { - return channel.messages.some(function (message) { - return message[0] === sig; - }); - }; - - Msg.messenger = function (common) { - var messenger = { - handlers: { - message: [], - join: [], - leave: [], - update: [], - friend: [], - unfriend: [], - }, - range_requests: {}, - }; - - var eachHandler = function (type, g) { - messenger.handlers[type].forEach(g); - }; - - messenger.on = function (type, f) { - var stack = messenger.handlers[type]; - if (!Array.isArray(stack)) { - return void console.error('unsupported message type'); - } - if (typeof(f) !== 'function') { - return void console.error('expected function'); - } - stack.push(f); - }; - - var channels = messenger.channels = {}; - - var joining = {}; - - // declare common variables - var network = common.getNetwork(); - var proxy = common.getProxy(); - var realtime = common.getRealtime(); - Msg.hk = network.historyKeeper; - var friends = getFriendList(proxy); - - var getChannel = function (curvePublic) { - var friend = friends[curvePublic]; - if (!friend) { return; } - var chanId = friend.channel; - if (!chanId) { return; } - return channels[chanId]; - }; - - var initRangeRequest = function (txid, curvePublic, sig, cb) { - messenger.range_requests[txid] = { - messages: [], - cb: cb, - curvePublic: curvePublic, - sig: sig, - }; - }; - - var getRangeRequest = function (txid) { - return messenger.range_requests[txid]; - }; - - var deleteRangeRequest = function (txid) { - delete messenger.range_requests[txid]; - }; - - messenger.getMoreHistory = function (curvePublic, hash, count, cb) { - if (typeof(cb) !== 'function') { return; } - - if (typeof(hash) !== 'string') { - // FIXME hash is not necessarily defined. - // What does this mean? - console.error("not sure what to do here"); - return; - } - - var chan = getChannel(curvePublic); - if (typeof(chan) === 'undefined') { - console.error("chan is undefined. we're going to have a problem here"); - return; - } - - var txid = common.uid(); - initRangeRequest(txid, curvePublic, hash, cb); - var msg = [ 'GET_HISTORY_RANGE', chan.id, { - from: hash, - count: count, - txid: txid, - } - ]; - - network.sendto(network.historyKeeper, JSON.stringify(msg)).then(function () { - }, function (err) { - throw new Error(err); - }); - }; - - var getCurveForChannel = function (id) { - var channel = channels[id]; - if (!channel) { return; } - return channel.curve; - }; - - messenger.getChannelHead = function (curvePublic, cb) { - var friend = friends[curvePublic]; - if (!friend) { return void cb('NO_SUCH_FRIEND'); } - cb(void 0, friend.lastKnownHash); - }; - - messenger.setChannelHead = function (curvePublic, hash, cb) { - var friend = friends[curvePublic]; - if (!friend) { return void cb('NO_SUCH_FRIEND'); } - friend.lastKnownHash = hash; - cb(); - }; - - // Id message allows us to map a netfluxId with a public curve key - var onIdMessage = function (msg, sender) { - var channel; - var isId = Object.keys(channels).some(function (chanId) { - if (channels[chanId].userList.indexOf(sender) !== -1) { - channel = channels[chanId]; - return true; - } - }); - - if (!isId) { return; } - - var decryptedMsg = channel.encryptor.decrypt(msg); - - if (decryptedMsg === null) { - return void console.error("Failed to decrypt message"); - } - - if (!decryptedMsg) { - console.error('decrypted message was falsey but not null'); - return; - } - - var parsed; - try { - parsed = JSON.parse(decryptedMsg); - } catch (e) { - console.error(decryptedMsg); - return; - } - if (parsed[0] !== Types.mapId && parsed[0] !== Types.mapIdAck) { return; } - - // check that the responding peer's encrypted netflux id matches - // the sender field. This is to prevent replay attacks. - if (parsed[2] !== sender || !parsed[1]) { return; } - channel.mapId[sender] = parsed[1]; - eachHandler('join', function (f) { - f(parsed[1], channel.id); - }); - - if (parsed[0] !== Types.mapId) { return; } // Don't send your key if it's already an ACK - // Answer with your own key - var rMsg = [Types.mapIdAck, proxy.curvePublic, channel.wc.myID]; - var rMsgStr = JSON.stringify(rMsg); - var cryptMsg = channel.encryptor.encrypt(rMsgStr); - network.sendto(sender, cryptMsg); - }; - - var orderMessages = function (curvePublic, new_messages /*, sig */) { - var channel = getChannel(curvePublic); - var messages = channel.messages; - - // TODO improve performance, guarantee correct ordering - new_messages.reverse().forEach(function (msg) { - messages.unshift(msg); - }); - }; - - var removeFromFriendList = function (curvePublic, cb) { - if (!proxy.friends) { return; } - var friends = proxy.friends; - delete friends[curvePublic]; - common.whenRealtimeSyncs(realtime, cb); - }; - - var pushMsg = function (channel, cryptMsg) { - var msg = channel.encryptor.decrypt(cryptMsg); - var sig = cryptMsg.slice(0, 64); - if (msgAlreadyKnown(channel, sig)) { return; } - - var parsedMsg = JSON.parse(msg); - var curvePublic; - if (parsedMsg[0] === Types.message) { - // TODO validate messages here - var res = { - type: parsedMsg[0], - sig: sig, - author: parsedMsg[1], - time: parsedMsg[2], - text: parsedMsg[3], - // this makes debugging a whole lot easier - curve: getCurveForChannel(channel.id), - }; - - channel.messages.push(res); - eachHandler('message', function (f) { - f(res); - }); - - return true; - } - if (parsedMsg[0] === Types.update) { - if (parsedMsg[1] === proxy.curvePublic) { return; } - curvePublic = parsedMsg[1]; - var newdata = parsedMsg[3]; - var data = getFriend(proxy, parsedMsg[1]); - var types = []; - Object.keys(newdata).forEach(function (k) { - if (data[k] !== newdata[k]) { - types.push(k); - data[k] = newdata[k]; - } - }); - - eachHandler('update', function (f) { - f(clone(newdata), curvePublic); - }); - return; - } - if (parsedMsg[0] === Types.unfriend) { - curvePublic = parsedMsg[1]; - delete friends[curvePublic]; - - removeFromFriendList(parsedMsg[1], function () { - channel.wc.leave(Types.unfriend); - eachHandler('unfriend', function (f) { - f(curvePublic); - }); - }); - return; - } - }; - - /* Broadcast a display name, profile, or avatar change to all contacts - */ - - // TODO send event... - messenger.updateMyData = function () { - var friends = getFriendList(proxy); - var mySyncData = friends.me; - var myData = createData(proxy); - if (!mySyncData || mySyncData.displayName !== myData.displayName - || mySyncData.profile !== myData.profile - || mySyncData.avatar !== myData.avatar) { - delete myData.channel; - Object.keys(channels).forEach(function (chan) { - var channel = channels[chan]; - - if (!channel) { - return void console.error('NO_SUCH_CHANNEL'); - } - - - var msg = [Types.update, myData.curvePublic, +new Date(), myData]; - var msgStr = JSON.stringify(msg); - var cryptMsg = channel.encryptor.encrypt(msgStr); - channel.wc.bcast(cryptMsg).then(function () { - // TODO send event - //channel.refresh(); - }, function (err) { - console.error(err); - }); - }); - eachHandler('update', function (f) { - f(myData, myData.curvePublic); - }); - friends.me = myData; - } - }; - - var onChannelReady = function (chanId) { - var cb = joining[chanId]; - if (typeof(cb) !== 'function') { - return void console.error('channel ready without callback'); - } - delete joining[chanId]; - return cb(); - }; - - var onDirectMessage = function (common, msg, sender) { - if (sender !== Msg.hk) { return void onIdMessage(msg, sender); } - var parsed = JSON.parse(msg); - - if (/HISTORY_RANGE/.test(parsed[0])) { - //console.log(parsed); - var txid = parsed[1]; - var req = getRangeRequest(txid); - var type = parsed[0]; - if (!req) { - return void console.error("received response to unknown request"); - } - - if (type === 'HISTORY_RANGE') { - req.messages.push(parsed[2]); - } else if (type === 'HISTORY_RANGE_END') { - // process all the messages (decrypt) - var curvePublic = req.curvePublic; - var channel = getChannel(curvePublic); - - var decrypted = req.messages.map(function (msg) { - if (msg[2] !== 'MSG') { return; } - try { - return { - d: JSON.parse(channel.encryptor.decrypt(msg[4])), - sig: msg[4].slice(0, 64), - }; - } catch (e) { - console.log('failed to decrypt'); - return null; - } - }).filter(function (decrypted) { - return decrypted; - }).map(function (O) { - return { - type: O.d[0], - sig: O.sig, - author: O.d[1], - time: O.d[2], - text: O.d[3], - curve: curvePublic, - }; - }); - - orderMessages(curvePublic, decrypted, req.sig); - req.cb(void 0, decrypted); - return deleteRangeRequest(txid); - } else { - console.log(parsed); - } - return; - } - - if ((parsed.validateKey || parsed.owners) && parsed.channel) { - return; - } - if (parsed.state && parsed.state === 1 && parsed.channel) { - if (channels[parsed.channel]) { - // parsed.channel is Ready - // channel[parsed.channel].ready(); - channels[parsed.channel].ready = true; - onChannelReady(parsed.channel); - var updateTypes = channels[parsed.channel].updateOnReady; - if (updateTypes) { - - //channels[parsed.channel].updateUI(updateTypes); - } - } - return; - } - var chan = parsed[3]; - if (!chan || !channels[chan]) { return; } - pushMsg(channels[chan], parsed[4]); - }; - - var onMessage = function (msg, sender, chan) { - if (!channels[chan.id]) { return; } - - var isMessage = pushMsg(channels[chan.id], msg); - if (isMessage) { - if (channels[chan.id].wc.myID !== sender) { - // Don't notify for your own messages - //channels[chan.id].notify(); - } - //channels[chan.id].refresh(); - // TODO emit message event - } - }; - - // listen for messages... - network.on('message', function(msg, sender) { - onDirectMessage(common, msg, sender); - }); - - messenger.removeFriend = function (curvePublic, cb) { - if (typeof(cb) !== 'function') { throw new Error('NO_CALLBACK'); } - var data = getFriend(proxy, curvePublic); - - if (!data) { - // friend is not valid - console.error('friend is not valid'); - return; - } - - var channel = channels[data.channel]; - if (!channel) { - return void cb("NO_SUCH_CHANNEL"); - } - - if (!network.webChannels.some(function (wc) { - return wc.id === channel.id; - })) { - console.error('bad channel: ', curvePublic); - } - - var msg = [Types.unfriend, proxy.curvePublic, +new Date()]; - var msgStr = JSON.stringify(msg); - var cryptMsg = channel.encryptor.encrypt(msgStr); - - // TODO emit remove_friend event? - try { - channel.wc.bcast(cryptMsg).then(function () { - delete friends[curvePublic]; - delete channels[curvePublic]; - common.whenRealtimeSyncs(realtime, function () { - cb(); - }); - }, function (err) { - console.error(err); - cb(err); - }); - } catch (e) { - cb(e); - } - }; - - var getChannelMessagesSince = function (chan, data, keys) { - console.log('Fetching [%s] messages since [%s]', data.curvePublic, data.lastKnownHash || ''); - var cfg = { - validateKey: keys.validateKey, - owners: [proxy.edPublic, data.edPublic], - lastKnownHash: data.lastKnownHash - }; - var msg = ['GET_HISTORY', chan.id, cfg]; - network.sendto(network.historyKeeper, JSON.stringify(msg)) - .then($.noop, function (err) { - throw new Error(err); - }); - }; - - var openFriendChannel = function (data, f) { - var keys = Curve.deriveKeys(data.curvePublic, proxy.curvePrivate); - var encryptor = Curve.createEncryptor(keys); - network.join(data.channel).then(function (chan) { - var channel = channels[data.channel] = { - id: data.channel, - sending: false, - friendEd: f, - keys: keys, - curve: data.curvePublic, - encryptor: encryptor, - messages: [], - wc: chan, - userList: [], - mapId: {}, - send: function (payload, cb) { - if (!network.webChannels.some(function (wc) { - if (wc.id === channel.wc.id) { return true; } - })) { - return void cb('NO_SUCH_CHANNEL'); - } - - var msg = [Types.message, proxy.curvePublic, +new Date(), payload]; - var msgStr = JSON.stringify(msg); - var cryptMsg = channel.encryptor.encrypt(msgStr); - - channel.wc.bcast(cryptMsg).then(function () { - pushMsg(channel, cryptMsg); - cb(); - }, function (err) { - cb(err); - }); - } - }; - chan.on('message', function (msg, sender) { - onMessage(msg, sender, chan); - }); - - var onJoining = function (peer) { - if (peer === Msg.hk) { return; } - if (channel.userList.indexOf(peer) !== -1) { return; } - - channel.userList.push(peer); - var msg = [Types.mapId, proxy.curvePublic, chan.myID]; - var msgStr = JSON.stringify(msg); - var cryptMsg = channel.encryptor.encrypt(msgStr); - network.sendto(peer, cryptMsg); - }; - chan.members.forEach(function (peer) { - if (peer === Msg.hk) { return; } - if (channel.userList.indexOf(peer) !== -1) { return; } - channel.userList.push(peer); - }); - chan.on('join', onJoining); - chan.on('leave', function (peer) { - var curvePublic = channel.mapId[peer]; - var i = channel.userList.indexOf(peer); - while (i !== -1) { - channel.userList.splice(i, 1); - i = channel.userList.indexOf(peer); - } - // update status - if (!curvePublic) { return; } - eachHandler('leave', function (f) { - f(curvePublic, channel.id); - }); - }); - - // FIXME don't subscribe to the channel implicitly - getChannelMessagesSince(chan, data, keys); - }, function (err) { - console.error(err); - }); - }; - - messenger.getFriendList = function (cb) { - var friends = proxy.friends; - if (!friends) { return void cb(void 0, []); } - - cb(void 0, Object.keys(proxy.friends).filter(function (k) { - return k !== 'me'; - })); - }; - - messenger.openFriendChannel = function (curvePublic, cb) { - if (typeof(curvePublic) !== 'string') { return void cb('INVALID_ID'); } - if (typeof(cb) !== 'function') { throw new Error('expected callback'); } - - var friend = clone(friends[curvePublic]); - if (typeof(friend) !== 'object') { - return void cb('NO_FRIEND_DATA'); - } - var channel = friend.channel; - if (!channel) { return void cb('E_NO_CHANNEL'); } - joining[channel] = cb; - openFriendChannel(friend, curvePublic); - }; - - messenger.sendMessage = function (curvePublic, payload, cb) { - var channel = getChannel(curvePublic); - if (!channel) { return void cb('NO_CHANNEL'); } - if (!network.webChannels.some(function (wc) { - if (wc.id === channel.wc.id) { return true; } - })) { - return void cb('NO_SUCH_CHANNEL'); - } - - var msg = [Types.message, proxy.curvePublic, +new Date(), payload]; - var msgStr = JSON.stringify(msg); - var cryptMsg = channel.encryptor.encrypt(msgStr); - - channel.wc.bcast(cryptMsg).then(function () { - pushMsg(channel, cryptMsg); - cb(); - }, function (err) { - cb(err); - }); - }; - - messenger.getStatus = function (curvePublic, cb) { - var channel = getChannel(curvePublic); - if (!channel) { return void cb('NO_SUCH_CHANNEL'); } - var online = channel.userList.some(function (nId) { - return channel.mapId[nId] === curvePublic; - }); - cb(void 0, online); - }; - - messenger.getFriendInfo = function (curvePublic, cb) { - setTimeout(function () { - var friend = friends[curvePublic]; - if (!friend) { return void cb('NO_SUCH_FRIEND'); } - // this clone will be redundant when ui uses postmessage - cb(void 0, clone(friend)); - }); - }; - - messenger.getMyInfo = function (cb) { - cb(void 0, { - curvePublic: proxy.curvePublic, - displayName: common.getDisplayName(), - }); - }; - - // TODO listen for changes to your friend list - // emit 'update' events for clients - - //var update = function (curvePublic - proxy.on('change', ['friends'], function (o, n, p) { - var curvePublic; - if (o === undefined) { - // new friend added - curvePublic = p.slice(-1)[0]; - eachHandler('friend', function (f) { - f(curvePublic, clone(n)); - }); - return; - } - - console.error(o, n, p); - }).on('remove', ['friends'], function (o, p) { - eachHandler('unfriend', function (f) { - f(p[1]); // TODO - }); - }); - - Object.freeze(messenger); - - return messenger; - }; - - // createInvite(theirPublicKey, function (e, data) { /* data.url */ }); - window.createInvitation = Msg.createInvitation = function (common, theirPublic, cb) { - -// theirPublic 'DHpRgRclYEvKdZO2gnN8bUyf6A4mEo8RmUGlGi9ChGo=' - - var proxy = common.getProxy(); - var myPublic = proxy.curvePublic; - - var mySecret = proxy.curvePrivate; - var keys = Curve.deriveKeys(theirPublic, mySecret); - var encryptor = Curve.createEncryptor(keys); - - var channel = common.createChannelId(); - - // create a channel encrypted with your curve private and their curvePublic - // TODO support 'owners'. You should be able to delete the channel. - var ListmapConfig = { - data: {}, - network: common.getNetwork(), - channel: channel, - readOnly: false, - validateKey: keys.validateKey, - crypto: encryptor, - username: 'invite-author', - logLevel: 1, - }; - - var lm = Listmap.create(ListmapConfig); - - var inviteProxy = lm.proxy; - inviteProxy.on('create', function () { - console.log('created'); - }) - .on('ready', function () { - /* do your stuff ... - then, once everything is synchronized, abort */ - - // a random channel - var destinationChannel = common.createChannelId(); - - // store all of the information necessary for them to become your friend - var myData = createData(proxy, destinationChannel); - Object.keys(myData).forEach(function (k) { - inviteProxy[k] = myData[k]; - }); - - //console.log(inviteProxy); - var urlSafeChannel = common.hexToBase64(destinationChannel); - var urlSafePublic = myPublic.replace(/\//g, '-'); - - var res = { - url: '/invite/#/1/' + urlSafeChannel + '/' + urlSafePublic + '/', - }; - -// "/invite/#/1/ilrOtygzDVoUSRpOOJrUuQ/e8jvf36S3chzkkcaMrLSW7PPrz7VDp85lIFNI26dTmr=/" - cb(void 0, res); - -/* - * add their key to your 'friends' object as a pending request? - * create a read-only link to that channel cb(void 0, { url: HERE }) - * /invite/#/1/invite/channel/yourCurvePublicKey -*/ - -/* UI - * copy link to clipboard - * instruct user to send that link to the other user out-of-band -*/ - - }) - .on('disconnect', function () { - }) - .on('change', [], function (/*o, n, p*/) { - - }); - }; - - return Msg; -}); From f52d987515079c8a1cfb78201fa5e1298d85525b Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 4 Dec 2017 15:03:43 +0100 Subject: [PATCH 19/26] Ability to resize avatars before uploading them --- bower.json | 3 +- customize.dist/translations/messages.fr.js | 2 + customize.dist/translations/messages.js | 2 + www/common/common-ui-elements.js | 8 ++- www/common/sframe-common-file.js | 4 +- www/profile/app-profile.less | 3 ++ www/profile/inner.js | 57 ++++++++++++++++++++-- 7 files changed, 73 insertions(+), 6 deletions(-) diff --git a/bower.json b/bower.json index 10ab90388..fad124ba9 100644 --- a/bower.json +++ b/bower.json @@ -44,7 +44,8 @@ "open-sans-fontface": "^1.4.2", "bootstrap-tokenfield": "^0.12.1", "localforage": "^1.5.2", - "html2canvas": "^0.4.1" + "html2canvas": "^0.4.1", + "croppie": "^2.5.0" }, "resolutions": { "bootstrap": "v4.0.0-alpha.6" diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 7a0ee42d4..81b8c9421 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -298,6 +298,8 @@ define(function () { out.profile_namePlaceholder = 'Nom ou pseudo pour le profil'; out.profile_avatar = "Avatar"; out.profile_upload = " Importer un nouvel avatar"; + out.profile_uploadSizeError = "Erreur : votre avatar doit avoir une taille inférieure à {0}"; + out.profile_uploadTypeError = "Erreur : le format de votre avatar est invalide. Les formats autorisés sont : {0}"; out.profile_error = "Erreur lors de la création du profil : {0}"; out.profile_register = "Vous devez vous inscrire pour pouvoir créer un profil !"; out.profile_create = "Créer un profil"; diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index c37420611..160cf72fd 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -301,6 +301,8 @@ define(function () { out.profile_namePlaceholder = 'Name displayed in your profile'; out.profile_avatar = "Avatar"; out.profile_upload = " Upload a new avatar"; + out.profile_uploadSizeError = "Error: your avatar must be smaller than {0}"; + out.profile_uploadTypeError = "Error: your avatar type is not allowed. Allowed types are: {0}"; out.profile_error = "Error while creating your profile: {0}"; out.profile_register = "You have to sign up to create a profile!"; out.profile_create = "Create a profile"; diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 19d4e03bf..c060b0cdc 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -98,7 +98,13 @@ define([ target: data.target }; if (data.filter && !data.filter(file)) { - UI.log('Invalid avatar (type or size)'); + return; + } + if (data.transformer) { + data.transformer(file, function (newFile) { + data.FM.handleFile(newFile, ev); + if (callback) { callback(); } + }); return; } data.FM.handleFile(file, ev); diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index a07ce8e0c..4d0370122 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -212,7 +212,9 @@ define([ queue.next(); }; - var showNamePrompt = true; + // Don't show the rename prompt if we don't want to store the file in the drive (avatar) + var showNamePrompt = !config.noStore; + var promptName = function (file, cb) { var extIdx = file.name.lastIndexOf('.'); var name = extIdx !== -1 ? file.name.slice(0,extIdx) : file.name; diff --git a/www/profile/app-profile.less b/www/profile/app-profile.less index f7cd747d3..21e659a03 100644 --- a/www/profile/app-profile.less +++ b/www/profile/app-profile.less @@ -73,6 +73,9 @@ margin: 5px; } } + .cp-app-profile-resizer { + text-align: center; + } #cp-app-profile-displayname, #cp-app-profile-link { width: 100%; height: 40px; diff --git a/www/profile/inner.js b/www/profile/inner.js index 2728dc15e..88903ac57 100644 --- a/www/profile/inner.js +++ b/www/profile/inner.js @@ -11,6 +11,7 @@ define([ '/customize/messages.js', '/bower_components/marked/marked.min.js', 'cm/lib/codemirror', + '/bower_components/croppie/croppie.min.js', 'cm/mode/markdown/markdown', @@ -20,6 +21,7 @@ define([ 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/customize/src/less2/main.less', + 'css!/bower_components/croppie/croppie.css', ], function ( $, Crypto, @@ -32,7 +34,8 @@ define([ Realtime, Messages, Marked, - CodeMirror + CodeMirror, + Croppie ) { var APP = window.APP = { @@ -253,12 +256,44 @@ define([ createEditableInput($block, LINK_ID, placeholder, getValue, setValue); }; + var AVATAR_SIZE_LIMIT = 0.5; var allowedMediaTypes = [ 'image/png', 'image/jpeg', 'image/jpg', 'image/gif', ]; + var transformAvatar = function (file, cb) { + if (file.type === 'image/gif') { return void cb(file); } + var $croppie = $('
', { + 'class': 'cp-app-profile-resizer' + }); + + var todo = function () { + UI.confirm($croppie[0], function (yes) { + if (!yes) { return; } + $croppie.croppie('result', { + type: 'blob', + size: {width: 300, height: 300} + }).then(function(blob) { + blob.lastModifiedDate = new Date(); + blob.name = 'avatar'; + cb(blob); + }); + }); + }; + + var reader = new FileReader(); + reader.onload = function(e) { + $croppie.croppie({ + url: e.target.result, + viewport: { width: 100, height: 100 }, + boundary: { width: 400, height: 300 }, + }); + todo(); + }; + reader.readAsDataURL(file); + }; var addAvatar = function ($container) { var $block = $('
', {id: AVATAR_ID}).appendTo($container); var $span = $('').appendTo($block); @@ -318,14 +353,30 @@ define([ } }; APP.FM = common.createFileManager(fmConfig); + var accepted = ".gif,.jpg,.jpeg,.png"; var data = { FM: APP.FM, filter: function (file) { var sizeMB = Util.bytesToMegabytes(file.size); var type = file.type; - return sizeMB <= 0.5 && allowedMediaTypes.indexOf(type) !== -1; + // We can't resize .gif so we have to display an error if it is too big + if (sizeMB > AVATAR_SIZE_LIMIT && type === 'image/gif') { + UI.log(Messages._getKey('profile_uploadSizeError', [ + Messages._getKey('formattedMB', [AVATAR_SIZE_LIMIT]) + ])); + return false; + } + // Display an error if the image type is not allowed + if (allowedMediaTypes.indexOf(type) === -1) { + UI.log(Messages._getKey('profile_uploadTypeError', [ + accepted.split(',').join(', ') + ])); + return false; + } + return true; }, - accept: ".gif,.jpg,.jpeg,.png" + transformer: transformAvatar, + accept: accepted }; var $upButton = common.createButton('upload', false, data); $upButton.text(Messages.profile_upload); From e397f66b566239298482180ed567dbea3aa09346 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 4 Dec 2017 15:27:44 +0100 Subject: [PATCH 20/26] lint compliance --- www/profile/inner.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/www/profile/inner.js b/www/profile/inner.js index 88903ac57..6cab88eae 100644 --- a/www/profile/inner.js +++ b/www/profile/inner.js @@ -11,7 +11,6 @@ define([ '/customize/messages.js', '/bower_components/marked/marked.min.js', 'cm/lib/codemirror', - '/bower_components/croppie/croppie.min.js', 'cm/mode/markdown/markdown', @@ -21,6 +20,7 @@ define([ 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'less!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/customize/src/less2/main.less', + '/bower_components/croppie/croppie.min.js', 'css!/bower_components/croppie/croppie.css', ], function ( $, @@ -34,8 +34,7 @@ define([ Realtime, Messages, Marked, - CodeMirror, - Croppie + CodeMirror ) { var APP = window.APP = { From fc4fea9c547c3d24adc24e1c621c09a8020780d8 Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 4 Dec 2017 17:33:06 +0100 Subject: [PATCH 21/26] update 404 messages and show actual text faster --- customize.dist/four-oh-four.js | 6 +++--- customize.dist/translations/messages.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/customize.dist/four-oh-four.js b/customize.dist/four-oh-four.js index 91752b190..9f0a8c230 100644 --- a/customize.dist/four-oh-four.js +++ b/customize.dist/four-oh-four.js @@ -68,10 +68,10 @@ define([ }; }; - makeDecryptor(brand, 90, 4, function () { })(); + makeDecryptor(brand, 70, 2, function () { })(); makeDecryptor(title, 50, 14, function () { })(); - makeDecryptor(link, 20, 10, function () {})(); - makeDecryptor(message, 12, 5, function () { + makeDecryptor(link, 20, 4, function () {})(); + makeDecryptor(message, 12, 3, function () { console.log('done'); })(); }); diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index c37420611..11d403c9a 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -690,7 +690,7 @@ define(function () { out.four04_pageNotFound = "We couldn't find the page you were looking for."; out.four04_goToHome = "Go to the home page."; - out.four04_goToDrive = "Go to my CryptDrive."; + out.four04_goToDrive = "Go to your CryptDrive"; // BottomBar.html From 087eee55f613dfb823e288700cc1b4c79fcbec6f Mon Sep 17 00:00:00 2001 From: ansuz Date: Mon, 4 Dec 2017 18:31:07 +0100 Subject: [PATCH 22/26] update 404 page --- customize.dist/four-oh-four.js | 2 +- customize.dist/translations/messages.fr.js | 6 ++---- customize.dist/translations/messages.js | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/customize.dist/four-oh-four.js b/customize.dist/four-oh-four.js index 9f0a8c230..ab19f577d 100644 --- a/customize.dist/four-oh-four.js +++ b/customize.dist/four-oh-four.js @@ -18,7 +18,7 @@ define([ var loggedIn = LocalStore.isLoggedIn(); var link = h('a#cp-link', { href: loggedIn? '/drive/': '/', - }, loggedIn? Messages.four04_goToDrive: Messages.four04_goToHome); + }, loggedIn? Messages.header_logoTitle: Messages.header_homeTitle); var content = h('div#cp-main', [ img, diff --git a/customize.dist/translations/messages.fr.js b/customize.dist/translations/messages.fr.js index 81b8c9421..0ba3413a3 100644 --- a/customize.dist/translations/messages.fr.js +++ b/customize.dist/translations/messages.fr.js @@ -683,10 +683,8 @@ define(function () { out.tos_logs = "Les meta-données fournies par votre navigateur au serveur peuvent être enregistrées dans le but de maintenir le service."; out.tos_3rdparties = "Nous ne fournissons aucune donnée individuelle à des tierces parties à moins d'y être contraints par la loi."; - // BottomBar.html - - out.bottom_france = 'Fait avec amour en France'; - out.bottom_support = 'Un projet XWiki SAS Labs avec le soutien de OpenPaaS-ng'; + // 404 page + out.four04_pageNotFound = "Nous n'avons pas trouvé la page que vous cherchez."; // Header.html diff --git a/customize.dist/translations/messages.js b/customize.dist/translations/messages.js index 6bc891878..b83b54930 100644 --- a/customize.dist/translations/messages.js +++ b/customize.dist/translations/messages.js @@ -690,9 +690,8 @@ define(function () { out.tos_logs = "Metadata provided by your browser to the server may be logged for the purpose of maintaining the service."; out.tos_3rdparties = "We do not provide individualized data to third parties unless required to by law."; + // 404 page out.four04_pageNotFound = "We couldn't find the page you were looking for."; - out.four04_goToHome = "Go to the home page."; - out.four04_goToDrive = "Go to your CryptDrive"; // BottomBar.html From c3e3da49485909206bc4ca61f591c798e25d894f Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 5 Dec 2017 15:07:09 +0100 Subject: [PATCH 23/26] allow administrators to increase limits for friends --- config.example.js | 18 ++++++++++++++++++ rpc.js | 26 ++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/config.example.js b/config.example.js index ce38b5c3a..3a097eb87 100644 --- a/config.example.js +++ b/config.example.js @@ -159,6 +159,24 @@ module.exports = { */ defaultStorageLimit: 50 * 1024 * 1024, + /* + * CryptPad allows administrators to give custom limits to their friends. + * add an entry for each friend, identified by their user id, + * which can be found on the settings page. Include a 'limit' (number of bytes), + * a 'plan' (string), and a 'note' (string). + * + * hint: 1GB is 1024 * 1024 * 1024 bytes + */ + customLimits: { + /* + "https://my.awesome.website/user/#/1/cryptpad-user/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": { + limit: 20 * 1024 * 1024 * 1024, + plan: 'insider', + note: 'storage space donated by my.awesome.website' + } + */ + }, + /* * By default, CryptPad also contacts our accounts server once a day to check for changes in * the people who have accounts. This check-in will also send the version of your CryptPad diff --git a/rpc.js b/rpc.js index b0e3a0e6d..3e28f7fed 100644 --- a/rpc.js +++ b/rpc.js @@ -428,6 +428,27 @@ var updateLimits = function (config, publicKey, cb /*:(?string, ?any[])=>void*/) "Content-Length": Buffer.byteLength(body) } }; + + // read custom limits from the config + var customLimits = (function (custom) { + var limits = {}; + Object.keys(custom).forEach(function (k) { + k.replace(/\/([^\/]+)$/, function (all, safeKey) { + var id = unescapeKeyCharacters(safeKey || ''); + limits[id] = custom[k]; + }); + }); + return limits; + }(config.customLimits || {})); + + var isLimit = function (o) { + var valid = o && typeof(o) === 'object' && + typeof(o.limit) === 'number' && + typeof(o.plan) === 'string' && + typeof(o.note) === 'string'; + return valid; + }; + var req = Https.request(options, function (response) { if (!('' + response.statusCode).match(/^2\d\d$/)) { return void cb('SERVER ERROR ' + response.statusCode); @@ -442,6 +463,11 @@ var updateLimits = function (config, publicKey, cb /*:(?string, ?any[])=>void*/) try { var json = JSON.parse(str); limits = json; + Object.keys(customLimits).forEach(function (k) { + if (!isLimit(customLimits[k])) { return; } + limits[k] = customLimits[k]; + }); + var l; if (userId) { var limit = limits[userId]; From 291eb5702730bf16f1c4caaf3d15eabf4ee6f400 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 5 Dec 2017 15:07:35 +0100 Subject: [PATCH 24/26] fix auth app --- www/common/cryptpad-common.js | 1 + 1 file changed, 1 insertion(+) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index aeadac105..68a17a5c2 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -605,6 +605,7 @@ define([ var initialized = false; return function (f, rdyCfg) { + rdyCfg = rdyCfg || {}; if (initialized) { return void setTimeout(function () { f(void 0, env); }); } From 6e80dce9d4633b5d4ba9db41d40c67b91ceb5365 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 5 Dec 2017 15:08:01 +0100 Subject: [PATCH 25/26] check for invalid typeof checks --- .jshintrc | 1 - 1 file changed, 1 deletion(-) diff --git a/.jshintrc b/.jshintrc index aeddcda04..c95e9dbc4 100644 --- a/.jshintrc +++ b/.jshintrc @@ -7,7 +7,6 @@ "iterator": true, "latedef": true, "nocomma": true, - "notypeof": true, "shadow": false, "undef": true, "unused": true, From 1613b1c0867997c9e48784c7eaadf01618a2ed75 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 5 Dec 2017 15:15:49 +0100 Subject: [PATCH 26/26] flow compliance --- rpc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/rpc.js b/rpc.js index 3e28f7fed..dc5c539e3 100644 --- a/rpc.js +++ b/rpc.js @@ -436,6 +436,7 @@ var updateLimits = function (config, publicKey, cb /*:(?string, ?any[])=>void*/) k.replace(/\/([^\/]+)$/, function (all, safeKey) { var id = unescapeKeyCharacters(safeKey || ''); limits[id] = custom[k]; + return ''; }); }); return limits;