31 changed files with 1118 additions and 60346 deletions
Split View
Diff Options
-
2.jshintignore
-
4bower.json
-
30lib/commands/admin-rpc.js
-
5lib/commands/quota.js
-
103lib/hk-util.js
-
10lib/workers/db-worker.js
-
235scripts/tests/test-mailbox.js
-
4www/code/mermaid-new.css
-
59134www/code/mermaid.js
-
56www/code/mermaid.min.js
-
883www/common/common-ui-elements.js
-
7www/common/cryptpad-common.js
-
31www/common/diffMarked.js
-
11www/common/drive-ui.js
-
235www/common/inner/access.js
-
167www/common/inner/common-modal.js
-
181www/common/inner/properties.js
-
22www/common/outer/async-store.js
-
11www/common/outer/worker-channel.js
-
1www/common/requireconfig.js
-
8www/common/sframe-app-framework.js
-
58www/common/sframe-chainpad-netflux-inner.js
-
4www/common/sframe-chainpad-netflux-outer.js
-
9www/common/toolbar3.js
-
4www/common/translations/messages.it.json
-
19www/drive/inner.js
-
23www/kanban/inner.js
-
5www/poll/inner.js
-
24www/teams/inner.js
-
78www/whiteboard/app-whiteboard.less
-
100www/whiteboard/inner.js
@ -0,0 +1,235 @@ |
|||
/* globals process */ |
|||
|
|||
var Client = require("../../lib/client/"); |
|||
var Crypto = require("../../www/bower_components/chainpad-crypto"); |
|||
var Mailbox = Crypto.Mailbox; |
|||
var Nacl = require("tweetnacl/nacl-fast"); |
|||
var nThen = require("nthen"); |
|||
var Pinpad = require("../../www/common/pinpad"); |
|||
var Rpc = require("../../www/common/rpc"); |
|||
var Hash = require("../../www/common/common-hash"); |
|||
var CpNetflux = require("../../www/bower_components/chainpad-netflux"); |
|||
var Util = require("../../lib/common-util"); |
|||
|
|||
// you need more than 100 messages in the history, and you need a lastKnownHash between "50" and "length - 50"
|
|||
|
|||
var createMailbox = function (config, _cb) { |
|||
var cb = Util.once(Util.mkAsync(_cb)); |
|||
|
|||
var webchannel; |
|||
var user = config.user; |
|||
user.messages = []; |
|||
|
|||
CpNetflux.start({ |
|||
network: config.network, |
|||
channel: config.channel, |
|||
crypto: config.crypto, |
|||
owners: [ config.edPublic ], |
|||
|
|||
noChainPad: true, |
|||
|
|||
lastKnownHash: config.lastKnownHash, |
|||
onChannelError: function (err) { |
|||
cb(err); |
|||
}, |
|||
onConnect: function (wc /*, sendMessage */) { |
|||
webchannel = wc; |
|||
}, |
|||
onMessage: function (msg /*, user, vKey, isCp, hash, author */) { |
|||
user.messages.push(msg); |
|||
}, |
|||
onReady: function () { |
|||
cb(void 0, webchannel); |
|||
}, |
|||
}); |
|||
}; |
|||
|
|||
process.on('unhandledRejection', function (err) { |
|||
console.error(err); |
|||
}); |
|||
|
|||
var state = {}; |
|||
|
|||
var makeCurveKeys = function () { |
|||
var pair = Nacl.box.keyPair(); |
|||
return { |
|||
curvePrivate: Nacl.util.encodeBase64(pair.secretKey), |
|||
curvePublic: Nacl.util.encodeBase64(pair.publicKey), |
|||
}; |
|||
}; |
|||
|
|||
var makeEdKeys = function () { |
|||
var keys = Nacl.sign.keyPair.fromSeed(Nacl.randomBytes(Nacl.sign.seedLength)); |
|||
return { |
|||
edPrivate: Nacl.util.encodeBase64(keys.secretKey), |
|||
edPublic: Nacl.util.encodeBase64(keys.publicKey), |
|||
}; |
|||
}; |
|||
|
|||
var edKeys = makeEdKeys(); |
|||
var curveKeys = makeCurveKeys(); |
|||
var mailboxChannel = Hash.createChannelId(); |
|||
|
|||
var createUser = function (config, cb) { |
|||
// config should contain keys for a team rpc (ed)
|
|||
// teamEdKeys
|
|||
// rosterHash
|
|||
|
|||
var user; |
|||
nThen(function (w) { |
|||
Client.create(w(function (err, client) { |
|||
if (err) { |
|||
w.abort(); |
|||
return void cb(err); |
|||
} |
|||
user = client; |
|||
user.destroy = Util.mkEvent(true); |
|||
user.destroy.reg(function () { |
|||
user.network.disconnect(); |
|||
}); |
|||
})); |
|||
}).nThen(function (w) { |
|||
// make all the parameters you'll need
|
|||
|
|||
var network = user.network = user.config.network; |
|||
user.edKeys = edKeys; |
|||
user.curveKeys = curveKeys; |
|||
|
|||
user.mailbox = Mailbox.createEncryptor(user.curveKeys); |
|||
user.mailboxChannel = mailboxChannel; |
|||
|
|||
// create an anon rpc for alice
|
|||
Rpc.createAnonymous(network, w(function (err, rpc) { |
|||
if (err) { |
|||
w.abort(); |
|||
user.shutdown(); |
|||
return void console.error('ANON_RPC_CONNECT_ERR'); |
|||
} |
|||
user.anonRpc = rpc; |
|||
user.destroy.reg(function () { |
|||
user.anonRpc.destroy(); |
|||
}); |
|||
})); |
|||
|
|||
Pinpad.create(network, user.edKeys, w(function (err, rpc) { |
|||
if (err) { |
|||
w.abort(); |
|||
user.shutdown(); |
|||
console.error(err); |
|||
return console.log('RPC_CONNECT_ERR'); |
|||
} |
|||
user.rpc = rpc; |
|||
user.destroy.reg(function () { |
|||
user.rpc.destroy(); |
|||
}); |
|||
})); |
|||
}).nThen(function (w) { |
|||
// create and subscribe to your mailbox
|
|||
createMailbox({ |
|||
user: user, |
|||
|
|||
|
|||
lastKnownHash: config.lastKnownHash, |
|||
|
|||
network: user.network, |
|||
channel: user.mailboxChannel, |
|||
crypto: user.mailbox, |
|||
edPublic: user.edKeys.edPublic, |
|||
}, w(function (err /*, wc*/) { |
|||
if (err) { |
|||
w.abort(); |
|||
//console.error("Mailbox creation error");
|
|||
cb(err); |
|||
//process.exit(1);
|
|||
} |
|||
//wc.leave();
|
|||
})); |
|||
}).nThen(function () { |
|||
user.cleanup = function (cb) { |
|||
//console.log("Destroying user");
|
|||
// TODO remove your mailbox
|
|||
user.destroy.fire(); |
|||
cb = cb; |
|||
}; |
|||
|
|||
cb(void 0, user); |
|||
}); |
|||
}; |
|||
|
|||
var alice; |
|||
|
|||
nThen(function (w) { |
|||
createUser({ |
|||
//sharedConfig
|
|||
}, w(function (err, _alice) { |
|||
if (err) { |
|||
w.abort(); |
|||
return void console.log(err); |
|||
} |
|||
alice = _alice; |
|||
alice.name = 'alice'; |
|||
})); |
|||
/* |
|||
createUser(sharedConfig, w(function (err, _bob) { |
|||
if (err) { |
|||
w.abort(); |
|||
return void console.log(err); |
|||
} |
|||
bob = _bob; |
|||
bob.name = 'bob'; |
|||
}));*/ |
|||
}).nThen(function (w) { |
|||
var i = 0; |
|||
var next = w(); |
|||
|
|||
state.hashes = []; |
|||
|
|||
var send = function () { |
|||
if (i++ >= 160) { return next(); } |
|||
|
|||
var msg = alice.mailbox.encrypt(JSON.stringify({ |
|||
pewpew: 'bangbang', |
|||
}), alice.curveKeys.curvePublic); |
|||
|
|||
var hash = msg.slice(0, 64); |
|||
state.hashes.push(hash); |
|||
|
|||
alice.anonRpc.send('WRITE_PRIVATE_MESSAGE', [ |
|||
alice.mailboxChannel, |
|||
msg |
|||
//Nacl.util.encodeBase64(Nacl.randomBytes(128))
|
|||
], w(function (err) { |
|||
if (err) { throw new Error(err); } |
|||
console.log('message %s written successfully', i); |
|||
setTimeout(send, 15); |
|||
})); |
|||
}; |
|||
send(); |
|||
}).nThen(function (w) { |
|||
console.log("Connecting with second user"); |
|||
createUser({ |
|||
lastKnownHash: state.hashes[55], |
|||
}, w(function (err, _alice) { |
|||
if (err) { |
|||
w.abort(); |
|||
console.log("lastKnownHash: ", state.hashes[55]); |
|||
console.log(err); |
|||
process.exit(1); |
|||
//return void console.log(err);
|
|||
} |
|||
var user = state.alice2 = _alice; |
|||
|
|||
if (user.messages.length === 105) { |
|||
process.exit(0); |
|||
} |
|||
//console.log(user.messages, user.messages.length);
|
|||
process.exit(1); |
|||
})); |
|||
}).nThen(function () { |
|||
|
|||
|
|||
}).nThen(function () { |
|||
alice.cleanup(); |
|||
//bob.cleanup();
|
|||
}); |
|||
|
|||
@ -0,0 +1,4 @@ |
|||
.sectionTitle, .titleText { |
|||
font-weight: bold; |
|||
} |
|||
|
|||
59134
www/code/mermaid.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
56
www/code/mermaid.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,167 @@ |
|||
define([ |
|||
'jquery', |
|||
'/common/common-util.js', |
|||
'/common/common-hash.js', |
|||
'/common/common-interface.js', |
|||
'/common/common-ui-elements.js', |
|||
'/customize/messages.js', |
|||
'/bower_components/nthen/index.js', |
|||
], function ($, Util, Hash, UI, UIElements, Messages, nThen) { |
|||
var Modal = {}; |
|||
|
|||
Modal.override = function (data, obj) { |
|||
data.owners = obj.owners; |
|||
data.expire = obj.expire; |
|||
data.pending_owners = obj.pending_owners; |
|||
data.mailbox = obj.mailbox; |
|||
data.restricted = obj.restricted; |
|||
data.allowed = obj.allowed; |
|||
data.rejected = obj.rejected; |
|||
}; |
|||
Modal.loadMetadata = function (Env, data, waitFor, redraw) { |
|||
Env.common.getPadMetadata({ |
|||
channel: data.channel |
|||
}, waitFor(function (md) { |
|||
if (md && md.error) { return void console.error(md.error); } |
|||
Modal.override(data, md); |
|||
if (redraw) { Env.evRedrawAll.fire(redraw); } |
|||
})); |
|||
}; |
|||
Modal.getPadData = function (Env, opts, _cb) { |
|||
var cb = Util.once(Util.mkAsync(_cb)); |
|||
var common = Env.common; |
|||
opts = opts || {}; |
|||
var data = {}; |
|||
nThen(function (waitFor) { |
|||
var priv = common.getMetadataMgr().getPrivateData(); |
|||
var base = priv.origin; |
|||
common.getPadAttribute('', waitFor(function (err, val) { |
|||
if (err || !val) { |
|||
if (opts.access) { |
|||
data.password = priv.password; |
|||
// Access modal and the pad is not stored: we're not an owner
|
|||
// so we don't need the correct href, just the type
|
|||
var h = Hash.createRandomHash(priv.app, priv.password); |
|||
data.href = base + priv.pathname + '#' + h; |
|||
} else { |
|||
waitFor.abort(); |
|||
return void cb(err || 'EEMPTY'); |
|||
} |
|||
return; |
|||
} |
|||
if (!val.fileType) { |
|||
delete val.owners; |
|||
delete val.expire; |
|||
} |
|||
Util.extend(data, val); |
|||
if (data.href) { data.href = base + data.href; } |
|||
if (data.roHref) { data.roHref = base + data.roHref; } |
|||
}), opts.href); |
|||
|
|||
// If this is a file, don't try to look for metadata
|
|||
if (opts.channel && opts.channel.length > 34) { return; } |
|||
if (opts.channel) { data.channel = opts.channel; } |
|||
Modal.loadMetadata(Env, data, waitFor); |
|||
}).nThen(function () { |
|||
cb(void 0, data); |
|||
}); |
|||
}; |
|||
Modal.isOwned = function (Env, data) { |
|||
var common = Env.common; |
|||
data = data || {}; |
|||
var priv = common.getMetadataMgr().getPrivateData(); |
|||
var edPublic = priv.edPublic; |
|||
var owned = false; |
|||
if (Array.isArray(data.owners) && data.owners.length) { |
|||
if (data.owners.indexOf(edPublic) !== -1) { |
|||
owned = true; |
|||
} else { |
|||
Object.keys(priv.teams || {}).some(function (id) { |
|||
var team = priv.teams[id] || {}; |
|||
if (team.viewer) { return; } |
|||
if (data.owners.indexOf(team.edPublic) === -1) { return; } |
|||
owned = Number(id); |
|||
return true; |
|||
}); |
|||
} |
|||
} |
|||
return owned; |
|||
}; |
|||
|
|||
var blocked = false; |
|||
Modal.getModal = function (common, opts, _tabs, cb) { |
|||
if (blocked) { return; } |
|||
blocked = true; |
|||
var Env = { |
|||
common: common, |
|||
evRedrawAll: Util.mkEvent() |
|||
}; |
|||
var data; |
|||
var button = [{ |
|||
className: 'cancel', |
|||
name: Messages.filePicker_close, |
|||
onClick: function () {}, |
|||
keys: [13,27] |
|||
}]; |
|||
var tabs = []; |
|||
nThen(function (waitFor) { |
|||
Modal.getPadData(Env, opts, waitFor(function (e, _data) { |
|||
if (e) { |
|||
blocked = false; |
|||
waitFor.abort(); |
|||
return void cb(e); |
|||
} |
|||
data = _data; |
|||
})); |
|||
}).nThen(function (waitFor) { |
|||
var owned = Modal.isOwned(Env, data); |
|||
if (typeof(owned) !== "boolean") { |
|||
data.teamId = Number(owned); |
|||
} |
|||
_tabs.forEach(function (obj, i) { |
|||
obj.getTab(Env, data, opts, waitFor(function (e, c) { |
|||
if (e) { |
|||
blocked = false; |
|||
waitFor.abort(); |
|||
return void cb(e); |
|||
} |
|||
var node = (c instanceof $) ? c[0] : c; |
|||
tabs[i] = { |
|||
content: c && UI.dialog.customModal(node, { |
|||
buttons: obj.buttons || button, |
|||
onClose: function () { blocked = false; } |
|||
}), |
|||
disabled: !c, |
|||
title: obj.title, |
|||
icon: obj.icon |
|||
}; |
|||
})); |
|||
}); |
|||
}).nThen(function () { |
|||
var tabsContent = UI.dialog.tabs(tabs); |
|||
var modal = UI.openCustomModal(tabsContent, { |
|||
wide: opts.wide |
|||
}); |
|||
cb (void 0, modal); |
|||
|
|||
var sframeChan = common.getSframeChannel(); |
|||
var handler = sframeChan.on('EV_RT_METADATA', function (md) { |
|||
if (!$(modal).length) { |
|||
return void handler.stop(); |
|||
} |
|||
Modal.override(data, Util.clone(md)); |
|||
Env.evRedrawAll.fire(); |
|||
}); |
|||
var metadataMgr = common.getMetadataMgr(); |
|||
var f = function () { |
|||
if (!$(modal).length) { |
|||
return void metadataMgr.off('change', f); |
|||
} |
|||
Env.evRedrawAll.fire(); |
|||
}; |
|||
metadataMgr.onChange(f); |
|||
}); |
|||
}; |
|||
|
|||
return Modal; |
|||
}); |
|||
@ -0,0 +1,181 @@ |
|||
define([ |
|||
'jquery', |
|||
'/common/common-util.js', |
|||
'/common/common-hash.js', |
|||
'/common/common-interface.js', |
|||
'/common/common-ui-elements.js', |
|||
'/common/inner/common-modal.js', |
|||
'/common/hyperscript.js', |
|||
'/customize/messages.js', |
|||
'/bower_components/nthen/index.js', |
|||
], function ($, Util, Hash, UI, UIElements, Modal, h, |
|||
Messages, nThen) { |
|||
var Properties = {}; |
|||
|
|||
var getPadProperties = function (Env, data, opts, _cb) { |
|||
var cb = Util.once(Util.mkAsync(_cb)); |
|||
var common = Env.common; |
|||
opts = opts || {}; |
|||
var $d = $('<div>'); |
|||
if (!data) { return void cb(void 0, $d); } |
|||
|
|||
if (data.href) { |
|||
$('<label>', {'for': 'cp-app-prop-link'}).text(Messages.editShare).appendTo($d); |
|||
$d.append(UI.dialog.selectable(data.href, { |
|||
id: 'cp-app-prop-link', |
|||
})); |
|||
} |
|||
|
|||
if (data.roHref && !opts.noReadOnly) { |
|||
$('<label>', {'for': 'cp-app-prop-rolink'}).text(Messages.viewShare).appendTo($d); |
|||
$d.append(UI.dialog.selectable(data.roHref, { |
|||
id: 'cp-app-prop-rolink', |
|||
})); |
|||
} |
|||
|
|||
if (data.tags && Array.isArray(data.tags)) { |
|||
$d.append(h('div.cp-app-prop', [Messages.fm_prop_tagsList, h('br'), h('span.cp-app-prop-content', data.tags.join(', '))])); |
|||
} |
|||
|
|||
if (data.ctime) { |
|||
$d.append(h('div.cp-app-prop', [Messages.fm_creation, h('br'), h('span.cp-app-prop-content', new Date(data.ctime).toLocaleString())])); |
|||
} |
|||
|
|||
if (data.atime) { |
|||
$d.append(h('div.cp-app-prop', [Messages.fm_lastAccess, h('br'), h('span.cp-app-prop-content', new Date(data.atime).toLocaleString())])); |
|||
} |
|||
|
|||
if (!common.isLoggedIn()) { return void cb(void 0, $d); } |
|||
|
|||
// File and history size...
|
|||
var owned = Modal.isOwned(Env, data); |
|||
|
|||
// check the size of this file, including additional channels
|
|||
var bytes = 0; |
|||
var historyBytes; |
|||
var chan = [data.channel]; |
|||
if (data.rtChannel) { chan.push(data.rtChannel); } |
|||
if (data.lastVersion) { chan.push(Hash.hrefToHexChannelId(data.lastVersion)); } |
|||
|
|||
// Get the channels with history (no blobs)
|
|||
var channels = chan.filter(function (c) { return c.length === 32; }).map(function (id) { |
|||
if (id === data.rtChannel && data.lastVersion && data.lastCpHash) { |
|||
return { |
|||
channel: id, |
|||
lastKnownHash: data.lastCpHash |
|||
}; |
|||
} |
|||
return { |
|||
channel: id |
|||
}; |
|||
}); |
|||
|
|||
var history = common.makeUniversal('history'); |
|||
var trimChannels = []; |
|||
nThen(function (waitFor) { |
|||
// Get total size
|
|||
chan.forEach(function (c) { |
|||
common.getFileSize(c, waitFor(function (e, _bytes) { |
|||
if (e) { |
|||
// there was a problem with the RPC
|
|||
console.error(e); |
|||
} |
|||
bytes += _bytes; |
|||
})); |
|||
}); |
|||
|
|||
if (!owned) { return; } |
|||
// Get history size
|
|||
history.execCommand('GET_HISTORY_SIZE', { |
|||
pad: true, |
|||
channels: channels, |
|||
teamId: typeof(owned) === "number" && owned |
|||
}, waitFor(function (obj) { |
|||
if (obj && obj.error) { return; } |
|||
historyBytes = obj.size; |
|||
trimChannels = obj.channels; |
|||
})); |
|||
}).nThen(function () { |
|||
if (bytes === 0) { return void cb(void 0, $d); } |
|||
var formatted = UIElements.prettySize(bytes); |
|||
|
|||
if (!owned || !historyBytes || historyBytes > bytes || historyBytes < 0) { |
|||
$d.append(h('div.cp-app-prop', [ |
|||
Messages.upload_size, |
|||
h('br'), |
|||
h('span.cp-app-prop-content', formatted) |
|||
])); |
|||
return void cb(void 0, $d); |
|||
} |
|||
|
|||
|
|||
var p = Math.round((historyBytes / bytes) * 100); |
|||
var historyPrettySize = UIElements.prettySize(historyBytes); |
|||
var contentsPrettySize = UIElements.prettySize(bytes - historyBytes); |
|||
var button; |
|||
var spinner = UI.makeSpinner(); |
|||
var size = h('div.cp-app-prop', [ |
|||
Messages.upload_size, |
|||
h('br'), |
|||
h('div.cp-app-prop-size-container', [ |
|||
h('div.cp-app-prop-size-history', { style: 'width:'+p+'%;' }) |
|||
]), |
|||
h('div.cp-app-prop-size-legend', [ |
|||
h('div.cp-app-prop-history-size', [ |
|||
h('span.cp-app-prop-history-size-color'), |
|||
h('span.cp-app-prop-content', Messages._getKey('historyTrim_historySize', [historyPrettySize])) |
|||
]), |
|||
h('div.cp-app-prop-contents-size', [ |
|||
h('span.cp-app-prop-contents-size-color'), |
|||
h('span.cp-app-prop-content', Messages._getKey('historyTrim_contentsSize', [contentsPrettySize])) |
|||
]), |
|||
]), |
|||
button = h('button.btn.btn-danger-alt.no-margin', Messages.trimHistory_button), |
|||
spinner.spinner |
|||
]); |
|||
$d.append(size); |
|||
|
|||
var $button = $(button); |
|||
UI.confirmButton(button, { |
|||
classes: 'btn-danger' |
|||
}, function () { |
|||
$button.remove(); |
|||
spinner.spin(); |
|||
history.execCommand('TRIM_HISTORY', { |
|||
pad: true, |
|||
channels: trimChannels, |
|||
teamId: typeof(owned) === "number" && owned |
|||
}, function (obj) { |
|||
spinner.hide(); |
|||
if (obj && obj.error) { |
|||
$(size).append(h('div.alert.alert-danger', Messages.trimHistory_error)); |
|||
return; |
|||
} |
|||
$(size).remove(); |
|||
var formatted = UIElements.prettySize(bytes - historyBytes); |
|||
$d.append(h('div.cp-app-prop', [ |
|||
Messages.upload_size, |
|||
h('br'), |
|||
h('span.cp-app-prop-content', formatted) |
|||
])); |
|||
$d.append(h('div.alert.alert-success', Messages.trimHistory_success)); |
|||
}); |
|||
}); |
|||
|
|||
cb(void 0, $d); |
|||
}); |
|||
}; |
|||
|
|||
Properties.getPropertiesModal = function (common, opts, cb) { |
|||
cb = cb || function () {}; |
|||
opts = opts || {}; |
|||
var tabs = [{ |
|||
getTab: getPadProperties, |
|||
title: Messages.fc_prop, |
|||
icon: "fa fa-info-circle", |
|||
}]; |
|||
Modal.getModal(common, opts, tabs, cb); |
|||
}; |
|||
|
|||
return Properties; |
|||
}); |
|||
Write
Preview
Loading…
Cancel
Save