diff --git a/customize.dist/main.js b/customize.dist/main.js index a110caaa3..7aaa77675 100644 --- a/customize.dist/main.js +++ b/customize.dist/main.js @@ -5,8 +5,9 @@ define([ '/common/common-interface.js', '/common/common-realtime.js', '/common/common-constants.js', + '/common/outer/local-store.js', '/customize/messages.js', -], function ($, Config, Cryptpad, UI, Realtime, Constants, Messages) { +], function ($, Config, Cryptpad, UI, Realtime, Constants, LocalStore, Messages) { window.APP = { Cryptpad: Cryptpad, @@ -25,7 +26,7 @@ define([ // Make sure we don't display non-translated content (empty button) $main.find('#data').removeClass('hidden'); - if (Cryptpad.isLoggedIn()) { + if (LocalStore.isLoggedIn()) { if (window.location.pathname === '/') { window.location = '/drive/'; return; @@ -47,7 +48,7 @@ define([ $('#buttons').find('.nologin').hide(); $logout.click(function () { - Cryptpad.logout(function () { + LocalStore.logout(function () { window.location.reload(); }); }); @@ -109,7 +110,7 @@ define([ proxy.edPublic = result.edPublic; Realtime.whenRealtimeSyncs(result.realtime, function () { - Cryptpad.login(result.userHash, result.userName, function () { + LocalStore.login(result.userHash, result.userName, function () { document.location.href = '/drive/'; }); }); diff --git a/www/auth/main.js b/www/auth/main.js index 577e1e966..5f1a5a333 100644 --- a/www/auth/main.js +++ b/www/auth/main.js @@ -1,9 +1,11 @@ define([ 'jquery', '/common/cryptpad-common.js', + '/common/common-constants.js', + '/common/outer/local-store.js', '/common/test.js', '/bower_components/tweetnacl/nacl-fast.min.js' -], function ($, Cryptpad, Test) { +], function ($, Cryptpad, Constants, LocalStore, Test) { var Nacl = window.nacl; var signMsg = function (msg, privKey) { @@ -20,7 +22,8 @@ define([ ]; // Safari is weird about localStorage in iframes but seems to let sessionStorage slide. - localStorage.User_hash = localStorage.User_hash || sessionStorage.User_hash; + localStorage[Constants.userHashKey] = localStorage[Constants.userHashKey] || + sessionStorage[Constants.userHashKey]; Cryptpad.ready(function () { console.log('IFRAME READY'); @@ -40,7 +43,7 @@ define([ } else if (data.cmd === 'SIGN') { if (!AUTHORIZED_DOMAINS.filter(function (x) { return x.test(domain); }).length) { ret.error = "UNAUTH_DOMAIN"; - } else if (!Cryptpad.isLoggedIn()) { + } else if (!LocalStore.isLoggedIn()) { ret.error = "NOT_LOGGED_IN"; } else { var proxy = Cryptpad.getStore().getProxy().proxy; diff --git a/www/bounce/main.js b/www/bounce/main.js index b8814e943..dfd3bb2cd 100644 --- a/www/bounce/main.js +++ b/www/bounce/main.js @@ -7,4 +7,4 @@ define([], function () { var bounceTo = decodeURIComponent(window.location.hash.slice(1)); if (!bounceTo) { return; } window.location.href = bounceTo; -}); \ No newline at end of file +}); diff --git a/www/common/common-feedback.js b/www/common/common-feedback.js new file mode 100644 index 000000000..728d362cd --- /dev/null +++ b/www/common/common-feedback.js @@ -0,0 +1,52 @@ +define(['/customize/messages.js'], function (Messages) { + var Feedback = {}; + + Feedback.init = function (state) { + Feedback.state = state; + }; + + var randomToken = function () { + return Math.random().toString(16).replace(/0./, ''); + }; + var ajax = function (url, cb) { + var http = new XMLHttpRequest(); + http.open('HEAD', url); + http.onreadystatechange = function() { + if (this.readyState === this.DONE) { + if (cb) { cb(); } + } + }; + http.send(); + }; + Feedback.send = function (action, force) { + if (!action) { return; } + if (force !== true) { + if (!Feedback.state) { return; } + } + + var href = '/common/feedback.html?' + action + '=' + randomToken(); + ajax(href); + }; + + Feedback.reportAppUsage = function () { + var pattern = window.location.pathname.split('/') + .filter(function (x) { return x; }).join('.'); + if (/^#\/1\/view\//.test(window.location.hash)) { + Feedback.send(pattern + '_VIEW'); + } else { + Feedback.send(pattern); + } + }; + + Feedback.reportScreenDimensions = function () { + var h = window.innerHeight; + var w = window.innerWidth; + Feedback.send('DIMENSIONS:' + h + 'x' + w); + }; + Feedback.reportLanguage = function () { + Feedback.send('LANG_' + Messages._languageUsed); + }; + + + return Feedback; +}); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 729b1c129..aea63b510 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1,17 +1,22 @@ define([ 'jquery', '/api/config', - '/common/cryptpad-common.js', '/common/common-util.js', '/common/common-hash.js', '/common/common-language.js', '/common/common-interface.js', + '/common/common-feedback.js', '/common/media-tag.js', + '/customize/messages.js', 'css!/common/tippy.css', -], function ($, Config, Cryptpad, Util, Hash, Language, UI, MediaTag) { +], function ($, Config, Util, Hash, Language, UI, Feedback, MediaTag, Messages) { var UIElements = {}; - var Messages = Cryptpad.Messages; + + // Configure MediaTags to use our local viewer + if (MediaTag && MediaTag.PdfPlugin) { + MediaTag.PdfPlugin.viewer = '/common/pdfjs/web/viewer.html'; + } UIElements.updateTags = function (common, href) { var sframeChan = common.getSframeChannel(); @@ -143,7 +148,7 @@ define([ toSave: toSave }, function () { UI.alert(Messages.templateSaved); - common.feedback('TEMPLATE_CREATED'); + Feedback.send('TEMPLATE_CREATED'); }); }; UI.prompt(Messages.saveTemplatePrompt, title, todo); @@ -236,6 +241,53 @@ define([ }; // Avatars + + // Enable mediatags + $(window.document).on('decryption', function (e) { + var decrypted = e.originalEvent; + if (decrypted.callback) { + var cb = decrypted.callback; + cb(function (mediaObject) { + var root = mediaObject.element; + if (!root) { return; } + + if (mediaObject.type === 'image') { + $(root).data('blob', decrypted.blob); + } + + if (mediaObject.type !== 'download') { return; } + + var metadata = decrypted.metadata; + + var title = ''; + var size = 0; + if (metadata && metadata.name) { + title = metadata.name; + } + + if (decrypted.blob) { + size = decrypted.blob.size; + } + + var sizeMb = Util.bytesToMegabytes(size); + + var $btn = $(root).find('button'); + $btn.addClass('btn btn-success') + .attr('type', 'download') + .html(function () { + var text = Messages.download_mt_button + '
'; + if (title) { + text += '' + Util.fixHTML(title) + '
'; + } + if (size) { + text += '' + Messages._getKey('formattedMB', [sizeMb]) + ''; + } + return text; + }); + }); + } + }); + UIElements.displayMediatagImage = function (Common, $tag, cb) { if (!$tag.length || !$tag.is('media-tag')) { return void cb('NOT_MEDIATAG'); } var observer = new MutationObserver(function(mutations) { @@ -434,6 +486,7 @@ define([ // allowed options tags: ['a', 'hr', 'p'] UIElements.createDropdown = function (config) { if (typeof config !== "object" || !Array.isArray(config.options)) { return; } + if (config.feedback && !config.common) { return void console.error("feedback in a dropdown requires sframe-common"); } var allowedTags = ['a', 'p', 'hr']; var isValidOption = function (o) { @@ -500,7 +553,7 @@ define([ setActive($val); $innerblock.scrollTop($val.position().top + $innerblock.scrollTop()); } - if (config.feedback) { Cryptpad.feedback(config.feedback); } + if (config.feedback) { Feedback.send(config.feedback); } }; $container.click(function (e) { @@ -676,6 +729,7 @@ define([ left: true, // Open to the left of the button container: config.$initBlock, // optional feedback: "USER_ADMIN", + common: Common }; var $userAdmin = UIElements.createDropdown(dropdownConfigUser); @@ -777,7 +831,8 @@ define([ options: options, // Entries displayed in the menu //left: true, // Open to the left of the button container: $initBlock, // optional - isSelect: true + isSelect: true, + common: common }; var $block = UIElements.createDropdown(dropdownConfig); $block.attr('id', 'cp-language-selector'); @@ -861,7 +916,7 @@ define([ sframeChan.query('Q_TEMPLATE_USE', data.href, function () { first = false; UI.removeLoadingScreen(); - common.feedback('TEMPLATE_USED'); + Feedback.send('TEMPLATE_USED'); }); if (focus) { focus.focus(); } return; diff --git a/www/common/cryptget.js b/www/common/cryptget.js index f900eafb2..270d69501 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -2,12 +2,12 @@ define([ 'jquery', '/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-netflux/chainpad-netflux.js', - '/common/cryptpad-common.js', '/common/common-util.js', '/common/common-hash.js', '/common/common-realtime.js', + '/common/outer/network-config.js', '/bower_components/textpatcher/TextPatcher.js' -], function ($, Crypto, CPNetflux, Cryptpad, Util, Hash, Realtime, TextPatcher) { +], function ($, Crypto, CPNetflux, Util, Hash, Realtime, NetConfig, TextPatcher) { //var Messages = Cryptpad.Messages; //var noop = function () {}; var finish = function (S, err, doc) { @@ -29,7 +29,7 @@ define([ var secret = Hash.getSecrets('pad', hash); if (!secret.keys) { secret.keys = secret.key; } // support old hashses var config = { - websocketURL: Cryptpad.getWebsocketURL(), + websocketURL: NetConfig.getWebsocketURL(), channel: secret.channel, validateKey: secret.keys.validateKey || undefined, crypto: Crypto.createEncryptor(secret.keys), diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 1d8b73a68..181f6b7ae 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -6,25 +6,18 @@ define([ '/common/common-util.js', '/common/common-hash.js', '/common/common-messaging.js', - '/file/file-crypto.js', '/common/common-realtime.js', '/common/common-language.js', '/common/common-constants.js', + '/common/common-feedback.js', + '/common/outer/local-store.js', - '/common/clipboard.js', '/common/pinpad.js', '/customize/application_config.js', - '/common/media-tag.js', '/bower_components/nthen/index.js', - '/bower_components/localforage/dist/localforage.min.js', ], function ($, Config, Messages, Store, Util, Hash, - Messaging, FileCrypto, Realtime, Language, Constants, Clipboard, - Pinpad, AppConfig, MediaTag, Nthen, localForage) { - - // Configure MediaTags to use our local viewer - if (MediaTag && MediaTag.PdfPlugin) { - MediaTag.PdfPlugin.viewer = '/common/pdfjs/web/viewer.html'; - } + Messaging, Realtime, Language, Constants, Feedback, LocalStore, + Pinpad, AppConfig, Nthen) { /* This file exposes functionality which is specific to Cryptpad, but not to any particular pad type. This includes functions for committing metadata @@ -36,11 +29,9 @@ define([ var origin = encodeURIComponent(window.location.hostname); var common = window.Cryptpad = { Messages: Messages, - Clipboard: Clipboard, donateURL: 'https://accounts.cryptpad.fr/#/donate?on=' + origin, upgradeURL: 'https://accounts.cryptpad.fr/#/?on=' + origin, account: {}, - MediaTag: MediaTag, }; var PINNING_ENABLED = AppConfig.enablePinning; @@ -49,63 +40,6 @@ define([ var rpc; var anon_rpc; - // import common utilities for export - //common.find = Util.find; - //common.hexToBase64 = Util.hexToBase64; - //common.base64ToHex = Util.base64ToHex; - //var deduplicateString = common.deduplicateString = Util.deduplicateString; - //common.uint8ArrayToHex = Util.uint8ArrayToHex; - //common.replaceHash = Util.replaceHash; - //common.getHash = Util.getHash; - //common.fixFileName = Util.fixFileName; - //common.bytesToMegabytes = Util.bytesToMegabytes; - //common.bytesToKilobytes = Util.bytesToKilobytes; - //common.fetch = Util.fetch; - //common.throttle = Util.throttle; - //common.createRandomInteger = Util.createRandomInteger; - //common.getAppType = Util.getAppType; - //common.notAgainForAnother = Util.notAgainForAnother; - //common.uid = Util.uid; - //common.slice = Util.slice; - - // import hash utilities for export - //var createRandomHash = common.createRandomHash = Hash.createRandomHash; - //common.parseTypeHash = Hash.parseTypeHash; - //var parsePadUrl = common.parsePadUrl = Hash.parsePadUrl; - //common.isNotStrongestStored = Hash.isNotStrongestStored; - //var hrefToHexChannelId = common.hrefToHexChannelId = Hash.hrefToHexChannelId; - //var getRelativeHref = common.getRelativeHref = Hash.getRelativeHref; - //common.getBlobPathFromHex = Hash.getBlobPathFromHex; - - //common.getEditHashFromKeys = Hash.getEditHashFromKeys; - //common.getViewHashFromKeys = Hash.getViewHashFromKeys; - //common.getFileHashFromKeys = Hash.getFileHashFromKeys; - //common.getUserHrefFromKeys = Hash.getUserHrefFromKeys; - //common.getSecrets = Hash.getSecrets; - //common.getHashes = Hash.getHashes; - //common.createChannelId = Hash.createChannelId; - //common.findWeaker = Hash.findWeaker; - //common.findStronger = Hash.findStronger; - //common.serializeHash = Hash.serializeHash; - //common.createInviteUrl = Hash.createInviteUrl; - - // Messaging - //common.addDirectMessageHandler = Messaging.addDirectMessageHandler; - //common.inviteFromUserlist = Messaging.inviteFromUserlist; - //common.getFriendList = Messaging.getFriendList; - //common.getFriendChannelsList = Messaging.getFriendChannelsList; - //common.createData = Messaging.createData; - //common.getPendingInvites = Messaging.getPending; - //common.getLatestMessages = Messaging.getLatestMessages; - - // Realtime - //var whenRealtimeSyncs = common.whenRealtimeSyncs = function (realtime, cb) { - //Realtime.whenRealtimeSyncs(common, realtime, cb); - //}; - //common.beginDetectingInfiniteSpinner = function (realtime) { - //Realtime.beginDetectingInfiniteSpinner(common, realtime); - //}; - var getStore = common.getStore = function () { if (store) { return store; } throw new Error("Store is not ready!"); @@ -157,62 +91,6 @@ define([ if (typeof cb === "function") { cb(null, name); } return name; }; - common.getAccountName = function () { - return localStorage[Constants.userNameKey]; - }; - - // REFACTOR: move to util? - var randomToken = function () { - return Math.random().toString(16).replace(/0./, ''); - }; - - common.isFeedbackAllowed = function () { - try { - var entry = Util.find(getProxy(), [ - 'settings', - 'general', - 'allowUserFeedback' - ]); - if (!entry) { return false; } - return true; - } catch (e) { - console.error(e); - return false; - } - }; - var feedback = common.feedback = function (action, force) { - if (!action) { return; } - if (force !== true) { - try { - if (!common.isFeedbackAllowed()) { return; } - } catch (e) { return void console.error(e); } - } - - var href = '/common/feedback.html?' + action + '=' + randomToken(); - $.ajax({ - type: "HEAD", - url: href, - }); - }; - - common.reportAppUsage = function () { - var pattern = window.location.pathname.split('/') - .filter(function (x) { return x; }).join('.'); - if (/^#\/1\/view\//.test(window.location.hash)) { - feedback(pattern + '_VIEW'); - } else { - feedback(pattern); - } - }; - - common.reportScreenDimensions = function () { - var h = window.innerHeight; - var w = window.innerWidth; - feedback('DIMENSIONS:' + h + 'x' + w); - }; - common.reportLanguage = function () { - feedback('LANG_' + Messages._languageUsed); - }; common.getUid = function () { if (store && store.getProxy() && store.getProxy().proxy) { @@ -227,94 +105,6 @@ define([ return; }; - common.getWebsocketURL = function () { - if (!Config.websocketPath) { return Config.websocketURL; } - var path = Config.websocketPath; - if (/^ws{1,2}:\/\//.test(path)) { return path; } - - var protocol = window.location.protocol.replace(/http/, 'ws'); - var host = window.location.host; - var url = protocol + '//' + host + path; - - return url; - }; - - common.login = function (hash, name, cb) { - if (!hash) { throw new Error('expected a user hash'); } - if (!name) { throw new Error('expected a user name'); } - hash = Hash.serializeHash(hash); - localStorage.setItem(Constants.userHashKey, hash); - localStorage.setItem(Constants.userNameKey, name); - if (cb) { cb(); } - }; - - var eraseTempSessionValues = common.eraseTempSessionValues = function () { - // delete sessionStorage values that might have been left over - // from the main page's /user redirect - [ - 'login', - 'login_user', - 'login_pass', - 'login_rmb', - 'register' - ].forEach(function (k) { - delete sessionStorage[k]; - }); - }; - - var logoutHandlers = []; - common.logout = function (cb) { - [ - Constants.userNameKey, - Constants.userHashKey, - 'loginToken', - 'plan', - ].forEach(function (k) { - sessionStorage.removeItem(k); - localStorage.removeItem(k); - delete localStorage[k]; - delete sessionStorage[k]; - }); - localForage.clear(); - // Make sure we have an FS_hash in localStorage before reloading all the tabs - // so that we don't end up with tabs using different anon hashes - if (!localStorage[Constants.fileHashKey]) { - localStorage[Constants.fileHashKey] = Hash.createRandomHash(); - } - eraseTempSessionValues(); - - logoutHandlers.forEach(function (h) { - if (typeof (h) === "function") { h(); } - }); - - if (cb) { cb(); } - }; - common.onLogout = function (h) { - if (typeof (h) !== "function") { return; } - if (logoutHandlers.indexOf(h) !== -1) { return; } - logoutHandlers.push(h); - }; - - var getUserHash = common.getUserHash = function () { - var hash = localStorage[Constants.userHashKey]; - - if (['undefined', 'undefined/'].indexOf(hash) !== -1) { - localStorage.removeItem(Constants.userHashKey); - return; - } - - if (hash) { - var sHash = Hash.serializeHash(hash); - if (sHash !== hash) { localStorage[Constants.userHashKey] = sHash; } - } - - return hash; - }; - - var isLoggedIn = common.isLoggedIn = function () { - return typeof getUserHash() === "string"; - }; - common.hasSigningKeys = function (proxy) { return typeof(proxy) === 'object' && typeof(proxy.edPrivate) === 'string' && @@ -336,11 +126,6 @@ define([ }; }; - common.isArray = $.isArray; - - /* - * localStorage formatting - */ var makePad = common.makePad = function (href, title) { var now = +new Date(); return { @@ -367,9 +152,6 @@ define([ if (cb) { cb(err, data); } }); }; - common.setLSAttribute = function (attr, value) { - localStorage[attr] = value; - }; // STORAGE common.getPadAttribute = function (attr, cb) { @@ -382,15 +164,6 @@ define([ }); }; - common.setThumbnail = function (key, value, cb) { - localForage.setItem(key, value, cb); - }; - common.getThumbnail = function (key, cb) { - localForage.getItem(key, cb); - }; - common.clearThumbnail = function (cb) { - localForage.clear(cb); - }; /* this returns a reference to your proxy. changing it will change your drive. */ @@ -482,10 +255,6 @@ define([ cb(void 0, all); }; - common.getLSAttribute = function (attr) { - return localStorage[attr]; - }; - // STORAGE - TEMPLATES var listTemplates = common.listTemplates = function (type) { var allTemplates = getStore().listTemplates(); @@ -746,7 +515,7 @@ define([ }; var pinsReady = common.pinsReady = function () { - if (!isLoggedIn()) { + if (!LocalStore.isLoggedIn()) { return false; } if (!PINNING_ENABLED) { @@ -883,7 +652,7 @@ define([ }; common.isOverPinLimit = function (cb) { - if (!common.isLoggedIn()) { return void cb(null, false); } + if (!LocalStore.isLoggedIn()) { return void cb(null, false); } var usage; var andThen = function (e, limit, plan) { if (e) { return void cb(e); } @@ -960,52 +729,6 @@ define([ }); }; - - $(window.document).on('decryption', function (e) { - var decrypted = e.originalEvent; - if (decrypted.callback) { - var cb = decrypted.callback; - cb(function (mediaObject) { - var root = mediaObject.element; - if (!root) { return; } - - if (mediaObject.type === 'image') { - $(root).data('blob', decrypted.blob); - } - - if (mediaObject.type !== 'download') { return; } - - var metadata = decrypted.metadata; - - var title = ''; - var size = 0; - if (metadata && metadata.name) { - title = metadata.name; - } - - if (decrypted.blob) { - size = decrypted.blob.size; - } - - var sizeMb = Util.bytesToMegabytes(size); - - var $btn = $(root).find('button'); - $btn.addClass('btn btn-success') - .attr('type', 'download') - .html(function () { - var text = Messages.download_mt_button + '
'; - if (title) { - text += '' + Util.fixHTML(title) + '
'; - } - if (size) { - text += '' + Messages._getKey('formattedMB', [sizeMb]) + ''; - } - return text; - }); - }); - } - }); - common.getShareHashes = function (secret, cb) { if (!window.location.hash) { var hashes = Hash.getHashes(secret.channel, secret); @@ -1072,27 +795,42 @@ define([ var network; var provideFeedback = function () { if (Object.keys(proxy).length === 1) { - feedback("FIRST_APP_USE", true); + Feedback.send("FIRST_APP_USE", true); } if (typeof(window.Proxy) === 'undefined') { - feedback("NO_PROXIES"); + Feedback.send("NO_PROXIES"); } var shimPattern = /CRYPTPAD_SHIM/; if (shimPattern.test(Array.isArray.toString())) { - feedback("NO_ISARRAY"); + Feedback.send("NO_ISARRAY"); } if (shimPattern.test(Array.prototype.fill.toString())) { - feedback("NO_ARRAYFILL"); + Feedback.send("NO_ARRAYFILL"); } if (typeof(Symbol) === 'undefined') { - feedback('NO_SYMBOL'); + Feedback.send('NO_SYMBOL'); } - common.reportScreenDimensions(); - common.reportLanguage(); + Feedback.reportScreenDimensions(); + Feedback.reportLanguage(); + }; + var initFeedback = function () { + // Initialize feedback + try { + var entry = Util.find(getProxy(), [ + 'settings', + 'general', + 'allowUserFeedback' + ]); + Feedback.init(entry); + } catch (e) { + console.error(e); + Feedback.init(false); + } + provideFeedback(); }; Nthen(function (waitFor) { @@ -1107,7 +845,7 @@ define([ network.on('reconnect', function () { Realtime.setConnectionState(true); }); - provideFeedback(); + initFeedback(); }), common); }).nThen(function (waitFor) { $(waitFor()); @@ -1137,15 +875,14 @@ define([ if (!o && n) { document.location.reload(); } else if (o && !n) { - common.logout(); + LocalStore.logout(); if (getNetwork()) { getNetwork().disconnect(); } } }); - - if (PINNING_ENABLED && isLoggedIn()) { + if (PINNING_ENABLED && LocalStore.isLoggedIn()) { console.log("logged in. pads will be pinned"); var w0 = waitFor(); Pinpad.create(network, proxy, function (e, call) { @@ -1241,7 +978,7 @@ define([ if (sessionStorage.migrateAnonDrive) { var w = waitFor(); require(['/common/mergeDrive.js'], function (Merge) { - var hash = localStorage.FS_hash; + var hash = LocalStore.getFSHash(); Merge.anonDriveIntoUser(getStore().getProxy(), hash, function () { delete sessionStorage.migrateAnonDrive; w(); diff --git a/www/common/fsStore.js b/www/common/fsStore.js index beff0078e..87dbc5eb7 100644 --- a/www/common/fsStore.js +++ b/www/common/fsStore.js @@ -6,9 +6,13 @@ define([ '/common/userObject.js', '/common/common-interface.js', '/common/common-hash.js', + '/common/common-util.js', '/common/common-constants.js', '/common/migrate-user-object.js', -], function ($, Listmap, Crypto, TextPatcher, FO, UI, Hash, Constants, Migrate) { + '/common/outer/network-config.js', + '/common/outer/local-store.js', +], function ($, Listmap, Crypto, TextPatcher, FO, UI, Hash, Util, Constants, Migrate, NetConfig, + LocalStore) { /* This module uses localStorage, which is synchronous, but exposes an asyncronous API. This is so that we can substitute other storage @@ -192,7 +196,7 @@ define([ var onReady = function (f, proxy, Cryptpad, exp) { var fo = exp.fo = FO.init(proxy.drive, { Cryptpad: Cryptpad, - loggedIn: Cryptpad.isLoggedIn() + loggedIn: LocalStore.isLoggedIn() }); var todo = function () { fo.fixFiles(); @@ -207,7 +211,7 @@ define([ var requestLogin = function () { // log out so that you don't go into an endless loop... - Cryptpad.logout(); + LocalStore.logout(); // redirect them to log in, and come back when they're done. sessionStorage.redirectTo = window.location.href; @@ -215,7 +219,7 @@ define([ }; var tokenKey = 'loginToken'; - if (Cryptpad.isLoggedIn()) { + 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. */ @@ -254,7 +258,7 @@ define([ } // if the user is logged in, but does not have signing keys... - if (Cryptpad.isLoggedIn() && (!Cryptpad.hasSigningKeys(proxy) || + if (LocalStore.isLoggedIn() && (!Cryptpad.hasSigningKeys(proxy) || !Cryptpad.hasCurveKeys(proxy))) { return void requestLogin(); } @@ -287,14 +291,14 @@ define([ if (!Cryptpad || initialized) { return; } initialized = true; - var hash = Cryptpad.getUserHash() || localStorage.FS_hash || Hash.createRandomHash(); + 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: Cryptpad.getWebsocketURL(), + websocketURL: NetConfig.getWebsocketURL(), channel: secret.channel, readOnly: false, validateKey: secret.keys.validateKey || undefined, @@ -311,8 +315,8 @@ define([ exp.proxy = rt.proxy; rt.proxy.on('create', function (info) { exp.info = info; - if (!Cryptpad.getUserHash()) { - localStorage.FS_hash = Hash.getEditHashFromKeys(info.channel, secret.keys); + 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 diff --git a/www/common/login.js b/www/common/login.js index 4254ce4be..20661adcf 100644 --- a/www/common/login.js +++ b/www/common/login.js @@ -2,12 +2,12 @@ define([ 'jquery', '/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-crypto/crypto.js', - '/common/cryptpad-common.js', '/common/common-util.js', + '/common/outer/network-config.js', '/common/credential.js', '/bower_components/tweetnacl/nacl-fast.min.js', '/bower_components/scrypt-async/scrypt-async.min.js', // better load speed -], function ($, Listmap, Crypto, Cryptpad, Util, Cred) { +], function ($, Listmap, Crypto, Util, NetConfig, Cred) { var Exports = { Cred: Cred, }; @@ -58,7 +58,7 @@ define([ var loadUserObject = function (opt, cb) { var config = { - websocketURL: Cryptpad.getWebsocketURL(), + websocketURL: NetConfig.getWebsocketURL(), channel: opt.channelHex, data: {}, validateKey: opt.keys.validateKey, // derived validation key diff --git a/www/common/mergeDrive.js b/www/common/mergeDrive.js index ad57aef37..d61a1db18 100644 --- a/www/common/mergeDrive.js +++ b/www/common/mergeDrive.js @@ -1,9 +1,9 @@ define([ - '/common/cryptpad-common.js', '/common/cryptget.js', '/common/userObject.js', '/common/common-hash.js', -], function (Cryptpad, Crypt, FO, Hash) { + '/common/outer/local-store.js', +], function (Crypt, FO, Hash, LocalStore) { 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 || !Cryptpad.isLoggedIn()) { + if (!fsHash || !LocalStore.isLoggedIn()) { 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,7 +105,7 @@ define([ if (parsed) { var proxy = proxyData.proxy; var oldFo = FO.init(parsed.drive, { - loggedIn: Cryptpad.isLoggedIn() + loggedIn: LocalStore.isLoggedIn() }); var onMigrated = function () { oldFo.fixFiles(); diff --git a/www/common/migrate-user-object.js b/www/common/migrate-user-object.js index 05c9e35c7..c5d45a73b 100644 --- a/www/common/migrate-user-object.js +++ b/www/common/migrate-user-object.js @@ -1,10 +1,10 @@ -define([], function () { +define(['/common/common-feedback.js'], function (Feedback) { // Start migration check // Versions: // 1: migrate pad attributes // 2: migrate indent settings (codemirror) - return function (userObject, Cryptpad) { + return function (userObject) { var version = userObject.version || 0; // DEPRECATED @@ -46,7 +46,7 @@ define([], function () { }; if (version < 2) { migrateAttributes(); - Cryptpad.feedback('Migrate-2', true); + Feedback.send('Migrate-2', true); userObject.version = version = 2; } @@ -60,7 +60,7 @@ define([], function () { }; if (version < 3) { migrateLanguage(); - Cryptpad.feedback('Migrate-3', true); + Feedback.send('Migrate-3', true); userObject.version = version = 3; } @@ -77,7 +77,7 @@ define([], function () { }; if (version < 4) { migrateFeedback(); - Cryptpad.feedback('Migrate-4', true); + Feedback.send('Migrate-4', true); userObject.version = version = 4; } @@ -99,7 +99,7 @@ define([], function () { }; if (version < 5) { migrateDates(); - Cryptpad.feedback('Migrate-5', true); + Feedback.send('Migrate-5', true); userObject.version = version = 5; } }; diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js new file mode 100644 index 000000000..09d135d2e --- /dev/null +++ b/www/common/outer/local-store.js @@ -0,0 +1,125 @@ +define([ + '/common/common-constants.js', + '/common/common-hash.js', + '/bower_components/localforage/dist/localforage.min.js', +], function (Constants, Hash, localForage) { + var LocalStore = {}; + + LocalStore.setThumbnail = function (key, value, cb) { + localForage.setItem(key, value, cb); + }; + LocalStore.getThumbnail = function (key, cb) { + localForage.getItem(key, cb); + }; + LocalStore.clearThumbnail = function (cb) { + cb = cb || function () {}; + localForage.clear(cb); + }; + + LocalStore.setFSHash = function (hash) { + var sHash = Hash.serializeHash(hash); + localStorage[Constants.fileHashKey] = sHash; + }; + LocalStore.getFSHash = function () { + var hash = localStorage[Constants.fileHashKey]; + + if (['undefined', 'undefined/'].indexOf(hash) !== -1) { + localStorage.removeItem(Constants.fileHashKey); + return; + } + + if (hash) { + var sHash = Hash.serializeHash(hash); + if (sHash !== hash) { localStorage[Constants.fileHashKey] = sHash; } + } + + return hash; + }; + + var getUserHash = LocalStore.getUserHash = function () { + var hash = localStorage[Constants.userHashKey]; + + if (['undefined', 'undefined/'].indexOf(hash) !== -1) { + localStorage.removeItem(Constants.userHashKey); + return; + } + + if (hash) { + var sHash = Hash.serializeHash(hash); + if (sHash !== hash) { localStorage[Constants.userHashKey] = sHash; } + } + + return hash; + }; + + LocalStore.getAccountName = function () { + return localStorage[Constants.userNameKey]; + }; + + LocalStore.isLoggedIn = function () { + return typeof getUserHash() === "string"; + }; + + + + + + LocalStore.login = function (hash, name, cb) { + if (!hash) { throw new Error('expected a user hash'); } + if (!name) { throw new Error('expected a user name'); } + hash = Hash.serializeHash(hash); + localStorage.setItem(Constants.userHashKey, hash); + localStorage.setItem(Constants.userNameKey, name); + if (cb) { cb(); } + }; + var eraseTempSessionValues = LocalStore.eraseTempSessionValues = function () { + // delete sessionStorage values that might have been left over + // from the main page's /user redirect + [ + 'login', + 'login_user', + 'login_pass', + 'login_rmb', + 'register' + ].forEach(function (k) { + delete sessionStorage[k]; + }); + }; + var logoutHandlers = []; + LocalStore.logout = function (cb) { + [ + Constants.userNameKey, + Constants.userHashKey, + 'loginToken', + 'plan', + ].forEach(function (k) { + sessionStorage.removeItem(k); + localStorage.removeItem(k); + delete localStorage[k]; + delete sessionStorage[k]; + }); + LocalStore.clearThumbnail(); + // Make sure we have an FS_hash in localStorage before reloading all the tabs + // so that we don't end up with tabs using different anon hashes + if (!LocalStore.getFSHash()) { + LocalStore.setFSHash(Hash.createRandomHash()); + } + eraseTempSessionValues(); + + logoutHandlers.forEach(function (h) { + if (typeof (h) === "function") { h(); } + }); + + if (cb) { cb(); } + }; + LocalStore.onLogout = function (h) { + if (typeof (h) !== "function") { return; } + if (logoutHandlers.indexOf(h) !== -1) { return; } + logoutHandlers.push(h); + }; + + + + + return LocalStore; +}); diff --git a/www/common/outer/network-config.js b/www/common/outer/network-config.js new file mode 100644 index 000000000..09b34ae0e --- /dev/null +++ b/www/common/outer/network-config.js @@ -0,0 +1,19 @@ +define([ + '/api/config' +], function (ApiConfig) { + var Config = {}; + + Config.getWebsocketURL = function () { + if (!ApiConfig.websocketPath) { return ApiConfig.websocketURL; } + var path = ApiConfig.websocketPath; + if (/^ws{1,2}:\/\//.test(path)) { return path; } + + var protocol = window.location.protocol.replace(/http/, 'ws'); + var host = window.location.host; + var url = protocol + '//' + host + path; + + return url; + }; + + return Config; +}); diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 7a6893895..470d82500 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -11,6 +11,7 @@ define([ '/common/common-util.js', '/common/common-interface.js', '/common/common-thumbnail.js', + '/common/common-feedback.js', '/customize/application_config.js', '/bower_components/file-saver/FileSaver.min.js', @@ -30,6 +31,7 @@ define([ Util, UI, Thumb, + Feedback, AppConfig) { var SaveAs = window.saveAs; @@ -330,7 +332,7 @@ define([ var feedback = function (action, force) { if (state === STATE.DISCONNECTED || state === STATE.INITIALIZING) { return; } - common.feedback(action, force); + Feedback.send(action, force); }; var createFilePicker = function () { diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js index 3b8d3d3ad..c0818e64e 100644 --- a/www/common/sframe-common-codemirror.js +++ b/www/common/sframe-common-codemirror.js @@ -212,6 +212,7 @@ define([ left: true, // Open to the left of the button isSelect: true, feedback: 'CODE_LANGUAGE', + common: Common }; var $block = exp.$language = UIElements.createDropdown(dropdownConfig); $block.find('button').attr('title', Messages.languageButtonTitle); @@ -249,6 +250,7 @@ define([ isSelect: true, initialValue: lastTheme, feedback: 'CODE_THEME', + common: Common }; var $block = exp.$theme = UIElements.createDropdown(dropdownConfig); $block.find('button').attr('title', Messages.themeButtonTitle); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 504f97f16..3e6c7c84c 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -38,8 +38,12 @@ define([ '/common/common-hash.js', '/common/common-util.js', '/common/common-realtime.js', + '/common/common-constants.js', + '/common/common-feedback.js', + '/common/outer/local-store.js', ], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel, - _FilePicker, _Messenger, _Messaging, _Notifier, _Hash, _Util, _Realtime) { + _FilePicker, _Messenger, _Messaging, _Notifier, _Hash, _Util, _Realtime, + _Constants, _Feedback, _LocalStore) { CpNfOuter = _CpNfOuter; Cryptpad = _Cryptpad; Crypto = _Crypto; @@ -52,6 +56,9 @@ define([ Utils.Hash = _Hash; Utils.Util = _Util; Utils.Realtime = _Realtime; + Utils.Constants = _Constants; + Utils.Feedback = _Feedback; + Utils.LocalStore = _LocalStore; if (localStorage.CRYPTPAD_URLARGS !== ApiConfig.requireConf.urlArgs) { console.log("New version, flushing cache"); @@ -135,14 +142,14 @@ define([ }, priv: { edPublic: proxy.edPublic, - accountName: Cryptpad.getAccountName(), + 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: Cryptpad.isFeedbackAllowed(), + feedbackAllowed: Utils.Feedback.state, friends: proxy.friends || {}, settings: proxy.settings || {}, isPresent: parsed.hashData && parsed.hashData.present, @@ -164,7 +171,7 @@ define([ sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); proxy.on('change', 'settings', updateMeta); - Cryptpad.onLogout(function () { + Utils.LocalStore.onLogout(function () { sframeChan.event('EV_LOGOUT'); }); @@ -187,7 +194,7 @@ define([ }); sframeChan.on('Q_THUMBNAIL_GET', function (data, cb) { - Cryptpad.getThumbnail(data.key, function (e, data) { + Utils.LocalStore.getThumbnail(data.key, function (e, data) { cb({ error: e, data: data @@ -195,7 +202,7 @@ define([ }); }); sframeChan.on('Q_THUMBNAIL_SET', function (data, cb) { - Cryptpad.setThumbnail(data.key, data.value, function (e) { + Utils.LocalStore.setThumbnail(data.key, data.value, function (e) { cb({error:e}); }); }); @@ -240,7 +247,7 @@ define([ }); sframeChan.on('Q_LOGOUT', function (data, cb) { - Cryptpad.logout(cb); + Utils.LocalStore.logout(cb); }); sframeChan.on('EV_NOTIFY', function () { @@ -415,7 +422,8 @@ define([ config.addCommonRpc = addCommonRpc; config.modules = { Cryptpad: Cryptpad, - SFrameChannel: SFrameChannel + SFrameChannel: SFrameChannel, + Utils: Utils }; FP.$iframe = $('