// Load #1, load as little as possible because we are in a race to get the loading screen up. define([ '/bower_components/nthen/index.js', '/api/config', 'jquery', ], function (nThen, ApiConfig, $) { var common = {}; common.start = function (cfg) { cfg = cfg || {}; var realtime = !cfg.noRealtime; var secret; var hashes; var CpNfOuter; var Cryptpad; var Crypto; var Cryptget; var SFrameChannel; var sframeChan; var FilePicker; var Messenger; var Messaging; var Notifier; var Utils = {}; nThen(function (waitFor) { // Load #2, the loading screen is up so grab whatever you need... require([ '/common/sframe-chainpad-netflux-outer.js', '/common/cryptpad-common.js', '/bower_components/chainpad-crypto/crypto.js', '/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', '/common/common-util.js', '/common/common-realtime.js', ], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel, _FilePicker, _Messenger, _Messaging, _Notifier, _Hash, _Util, _Realtime) { CpNfOuter = _CpNfOuter; Cryptpad = _Cryptpad; Crypto = _Crypto; Cryptget = _Cryptget; SFrameChannel = _SFrameChannel; FilePicker = _FilePicker; Messenger = _Messenger; Messaging = _Messaging; Notifier = _Notifier; Utils.Hash = _Hash; Utils.Util = _Util; Utils.Realtime = _Realtime; if (localStorage.CRYPTPAD_URLARGS !== ApiConfig.requireConf.urlArgs) { console.log("New version, flushing cache"); Object.keys(localStorage).forEach(function (k) { if (k.indexOf('CRYPTPAD_CACHE|') !== 0) { return; } delete localStorage[k]; }); localStorage.CRYPTPAD_URLARGS = ApiConfig.requireConf.urlArgs; } var cache = {}; var localStore = {}; Object.keys(localStorage).forEach(function (k) { if (k.indexOf('CRYPTPAD_CACHE|') === 0) { cache[k.slice(('CRYPTPAD_CACHE|').length)] = localStorage[k]; return; } if (k.indexOf('CRYPTPAD_STORE|') === 0) { localStore[k.slice(('CRYPTPAD_STORE|').length)] = localStorage[k]; return; } }); SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) { sframeChan = sfc; }), false, { cache: cache, localStore: localStore, language: Cryptpad.getLanguage() }); Cryptpad.ready(waitFor()); })); }).nThen(function (waitFor) { $('#sbox-iframe').focus(); sframeChan.on('EV_CACHE_PUT', function (x) { Object.keys(x).forEach(function (k) { localStorage['CRYPTPAD_CACHE|' + k] = x[k]; }); }); sframeChan.on('EV_LOCALSTORE_PUT', function (x) { Object.keys(x).forEach(function (k) { if (typeof(x[k]) === "undefined") { delete localStorage['CRYPTPAD_STORE|' + k]; return; } localStorage['CRYPTPAD_STORE|' + k] = x[k]; }); }); 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(); } Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; })); }).nThen(function () { var readOnly = secret.keys && !secret.keys.editKeyStr; if (!secret.keys) { secret.keys = secret.key; } 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 name; nThen(function (waitFor) { Cryptpad.getLastName(waitFor(function (err, n) { if (err) { console.log(err); } name = n; })); }).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: Cryptpad.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(), 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 } } }; if (cfg.addData) { cfg.addData(metaObj.priv, Cryptpad); } sframeChan.event('EV_METADATA_UPDATE', metaObj); }); }; Cryptpad.onDisplayNameChanged(updateMeta); sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); proxy.on('change', 'settings', updateMeta); Cryptpad.onLogout(function () { sframeChan.event('EV_LOGOUT'); }); // Put in the following function the RPC queries that should also work in filepicker var addCommonRpc = function (sframeChan) { sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) { Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) { cb({error: err, response: response}); }); }); sframeChan.on('Q_GET_PIN_LIMIT_STATUS', function (data, cb) { Cryptpad.isOverPinLimit(function (e, overLimit, limits) { cb({ error: e, overLimit: overLimit, limits: limits }); }); }); sframeChan.on('Q_THUMBNAIL_GET', function (data, cb) { Cryptpad.getThumbnail(data.key, function (e, data) { cb({ error: e, data: data }); }); }); sframeChan.on('Q_THUMBNAIL_SET', function (data, cb) { Cryptpad.setThumbnail(data.key, data.value, function (e) { cb({error:e}); }); }); }; addCommonRpc(sframeChan); var currentTitle; var currentTabTitle; var setDocumentTitle = function () { if (!currentTabTitle) { document.title = currentTitle || 'CryptPad'; return; } var title = currentTabTitle.replace(/\{title\}/g, currentTitle || 'CryptPad'); document.title = title; }; sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) { currentTitle = newTitle; setDocumentTitle(); Cryptpad.renamePad(newTitle, undefined, function (err) { cb(err); }); }); sframeChan.on('EV_SET_TAB_TITLE', function (newTabTitle) { currentTabTitle = newTabTitle; setDocumentTitle(); }); sframeChan.on('Q_SETTINGS_SET_DISPLAY_NAME', function (newName, cb) { Cryptpad.setDisplayName(newName, function (err) { if (err) { console.log("Couldn't set username"); console.error(err); cb('ERROR'); return; } Cryptpad.changeDisplayName(newName, true); cb(); }); }); sframeChan.on('Q_LOGOUT', function (data, cb) { Cryptpad.logout(cb); }); sframeChan.on('EV_NOTIFY', function () { Notifier.notify(); }); sframeChan.on('Q_SET_LOGIN_REDIRECT', function (data, cb) { sessionStorage.redirectTo = window.location.href; cb(); }); sframeChan.on('Q_MOVE_TO_TRASH', function (data, cb) { cb = cb || $.noop; if (readOnly && hashes.editHash) { var appPath = window.location.pathname; Cryptpad.moveToTrash(cb, appPath + '#' + hashes.editHash); return; } Cryptpad.moveToTrash(cb); }); sframeChan.on('Q_SAVE_AS_TEMPLATE', function (data, cb) { Cryptpad.saveAsTemplate(Cryptget.put, data, cb); }); sframeChan.on('Q_SEND_FRIEND_REQUEST', function (netfluxId, cb) { Messaging.inviteFromUserlist(Cryptpad, netfluxId); cb(); }); Cryptpad.onFriendRequest = function (confirmText, cb) { sframeChan.query('Q_INCOMING_FRIEND_REQUEST', confirmText, function (err, data) { cb(data); }); }; Cryptpad.onFriendComplete = function (data) { sframeChan.event('EV_FRIEND_REQUEST', data); }; 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 var parse = function (msg) { try { return JSON.parse(msg); } catch (e) { return null; } }; var msgs = []; var onMsg = function (msg) { var parsed = parse(msg); if (parsed[0] === 'FULL_HISTORY_END') { cb(msgs); return; } if (parsed[0] !== 'FULL_HISTORY') { return; } if (parsed[1] && parsed[1].validateKey) { // First message return; } msg = parsed[1][4]; if (msg) { msg = msg.replace(/^cp\|/, ''); var decryptedMsg = crypto.decrypt(msg, true); msgs.push(decryptedMsg); } }; network.on('message', onMsg); network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', secret.channel, secret.keys.validateKey])); }); sframeChan.on('Q_GET_PAD_ATTRIBUTE', function (data, cb) { Cryptpad.getPadAttribute(data.key, function (e, data) { cb({ error: e, data: data }); }); }); sframeChan.on('Q_SET_PAD_ATTRIBUTE', function (data, cb) { Cryptpad.setPadAttribute(data.key, data.value, function (e) { cb({error:e}); }); }); sframeChan.on('Q_GET_ATTRIBUTE', function (data, cb) { Cryptpad.getAttribute(data.key, function (e, data) { cb({ error: e, data: data }); }); }); sframeChan.on('Q_SET_ATTRIBUTE', function (data, cb) { Cryptpad.setAttribute(data.key, data.value, function (e) { cb({error:e}); }); }); sframeChan.on('Q_SESSIONSTORAGE_PUT', function (data, cb) { sessionStorage[data.key] = data.value; cb(); }); // Present mode URL sframeChan.on('Q_PRESENT_URL_GET_VALUE', function (data, cb) { var parsed = Utils.Hash.parsePadUrl(window.location.href); cb(parsed.hashData && parsed.hashData.present); }); sframeChan.on('EV_PRESENT_URL_SET_VALUE', function (data) { var parsed = Utils.Hash.parsePadUrl(window.location.href); window.location.href = parsed.getUrl({ embed: parsed.hashData.embed, present: data }); }); // File upload var onFileUpload = function (sframeChan, data, cb) { require(['/common/outer/upload.js'], function (Files) { var sendEvent = function (data) { sframeChan.event("EV_FILE_UPLOAD_STATE", data); }; var updateProgress = function (progressValue) { sendEvent({ progress: progressValue }); }; var onComplete = function (href) { sendEvent({ complete: true, href: href }); }; var onError = function (e) { sendEvent({ error: e }); }; var onPending = function (cb) { sframeChan.query('Q_CANCEL_PENDING_FILE_UPLOAD', null, function (err, data) { if (data) { cb(); } }); }; data.blob = Crypto.Nacl.util.decodeBase64(data.blob); Files.upload(data, data.noStore, Cryptpad, updateProgress, onComplete, onError, onPending); cb(); }); }; sframeChan.on('Q_UPLOAD_FILE', function (data, cb) { onFileUpload(sframeChan, data, cb); }); // File picker var FP = {}; var initFilePicker = function (cfg) { if (!FP.$iframe) { var config = {}; config.onFilePicked = function (data) { sframeChan.event('EV_FILE_PICKED', data); }; config.onClose = function () { FP.$iframe.hide(); }; config.onFileUpload = onFileUpload; config.types = cfg; config.addCommonRpc = addCommonRpc; config.modules = { Cryptpad: Cryptpad, SFrameChannel: SFrameChannel }; FP.$iframe = $('