Merge branch 'staging' into new-messaging

This commit is contained in:
ansuz
2017-08-18 15:31:06 +02:00
44 changed files with 665 additions and 184 deletions

View File

@@ -30,6 +30,7 @@ body {
min-width: 20%;
max-width: 80%;
resize: horizontal;
font-size: initial;
}
.CodeMirror.fullPage {
//min-width: 100%;

View File

@@ -410,6 +410,7 @@ define([
}
});
/*
// add the splitter
if (!$iframe.has('.cp-splitter').length) {
var $preview = $iframe.find('#previewContainer');
@@ -437,6 +438,7 @@ define([
});
});
}
*/
Cryptpad.removeLoadingScreen();
setEditable(true);

View File

@@ -6,6 +6,23 @@ define([
var Nacl = window.nacl;
var module = {};
var blobToArrayBuffer = function (blob, cb) {
var reader = new FileReader();
reader.onloadend = function () {
cb(void 0, this.result);
};
reader.readAsArrayBuffer(blob);
};
var arrayBufferToString = function (AB) {
try {
return Nacl.util.encodeBase64(new Uint8Array(AB));
} catch (e) {
console.error(e);
return null;
}
};
module.create = function (common, config) {
var File = {};
@@ -227,19 +244,33 @@ define([
queue.next();
};
var handleFile = File.handleFile = function (file, e) {
var reader = new FileReader();
reader.onloadend = function () {
var handleFile = File.handleFile = function (file, e, thumbnail) {
var thumb;
var finish = function (arrayBuffer) {
var metadata = {
name: file.name,
type: file.type,
};
if (thumb) { metadata.thumbnail = thumb; }
queue.push({
blob: this.result,
metadata: {
name: file.name,
type: file.type,
},
blob: arrayBuffer,
metadata: metadata,
dropEvent: e
});
};
reader.readAsArrayBuffer(file);
var processFile = function () {
blobToArrayBuffer(file, function (e, buffer) {
finish(buffer);
});
};
if (!thumbnail) { return void processFile(); }
blobToArrayBuffer(thumbnail, function (e, buffer) {
if (e) { console.error(e); }
thumb = arrayBufferToString(buffer);
processFile();
});
};
var onFileDrop = File.onFileDrop = function (file, e) {

View File

@@ -256,7 +256,7 @@ define([
var $friend = ui.getFriend(curvePublic);
var $chat = ui.getChannel(curvePublic);
$friend.remove();
$chat.remove();
if ($chat) { $chat.remove(); }
ui.showInfo();
};
@@ -892,7 +892,7 @@ define([
var addToFriendList = Msg.addToFriendList = function (common, data, cb) {
var proxy = common.getProxy();
var friends = getFriendList(proxy);
var pubKey = data.curvePublic;
var pubKey = data.curvePublic; // todo validata data
if (pubKey === proxy.curvePublic) { return void cb("E_MYKEY"); }
@@ -938,7 +938,7 @@ define([
var todo = function (yes) {
if (yes) {
pending[sender] = msgData;
msg = ["FRIEND_REQ_OK", chan, createData(common, msgData.channel)];
msg = ["FRIEND_REQ_OK", chan, createData(proxy, msgData.channel)];
}
msgStr = Crypto.encrypt(JSON.stringify(msg), key);
network.sendto(sender, msgStr);
@@ -1005,7 +1005,7 @@ define([
if (!parsed.hashData) { return; }
// Message
var chan = parsed.hashData.channel;
var myData = createData(common);
var myData = createData(common.getProxy());
var msg = ["FRIEND_REQ", chan, myData];
// Encryption
var keyStr = parsed.hashData.key;

View File

@@ -8,6 +8,8 @@ define([
var BAD_STATE_TIMEOUT = typeof(AppConfig.badStateTimeout) === 'number'?
AppConfig.badStateTimeout: 30000;
var connected = false;
/*
TODO make this not blow up when disconnected or lagging...
*/
@@ -20,6 +22,7 @@ define([
}
var to = setTimeout(function () {
if (!connected) { return; }
realtime.abort();
// don't launch more than one popup
if (common.infiniteSpinnerDetected) { return; }
@@ -38,5 +41,10 @@ define([
}, 0);
};
common.setConnectionState = function (bool) {
if (typeof(bool) !== 'boolean') { return; }
connected = bool;
};
return common;
});

View File

@@ -0,0 +1,51 @@
define([
'/bower_components/tweetnacl/nacl-fast.min.js',
], function () {
var Nacl = window.nacl;
var Thumb = {
dimension: 150, // thumbnails are all 150px
};
// create thumbnail image from metadata
// return an img tag, or undefined if anything goes wrong
Thumb.fromMetadata = function (metadata) {
if (!metadata || typeof(metadata) !== 'object' || !metadata.thumbnail) { return; }
try {
var u8 = Nacl.util.decodeBase64(metadata.thumbnail);
var blob = new Blob([u8], {
type: 'image/png'
});
var url = URL.createObjectURL(blob);
var img = new Image();
img.src = url;
img.width = Thumb.dimension;
img.height = Thumb.dimension;
return img;
} catch (e) {
console.error(e);
return;
}
};
// assumes that your canvas is square
// nodeback returning blob
Thumb.fromCanvas = function (canvas, cb) {
canvas = canvas;
var c2 = document.createElement('canvas');
var d = Thumb.dimension;
c2.width = d;
c2.height = 2;
var ctx = c2.getContext('2d');
ctx.drawImage(canvas, 0, 0, d, d);
c2.toBlob(function (blob) {
cb(void 0, blob);
});
};
Thumb.fromVideo = function (video, cb) {
cb = cb; // WIP
};
return Thumb;
});

View File

@@ -571,6 +571,7 @@ define([
_onDisplayNameChanged.forEach(function (h) {
h(newName, isLocal);
});
common.clearTooltips();
};
// STORAGE
@@ -1848,12 +1849,18 @@ define([
Store.ready(function (err, storeObj) {
store = common.store = env.store = storeObj;
common.addDirectMessageHandler(common);
var proxy = getProxy();
var network = getNetwork();
network.on('disconnect', function () {
Realtime.setConnectionState(false);
});
network.on('reconnect', function () {
Realtime.setConnectionState(true);
});
if (Object.keys(proxy).length === 1) {
feedback("FIRST_APP_USE", true);
}

View File

@@ -50,25 +50,35 @@ define([
};
Curve.deriveKeys = function (theirs, mine) {
var pub = decodeBase64(theirs);
var secret = decodeBase64(mine);
try {
var pub = decodeBase64(theirs);
var secret = decodeBase64(mine);
var sharedSecret = Nacl.box.before(pub, secret);
var salt = decodeUTF8('CryptPad.signingKeyGenerationSalt');
var sharedSecret = Nacl.box.before(pub, secret);
var salt = decodeUTF8('CryptPad.signingKeyGenerationSalt');
// 64 uint8s
var hash = Nacl.hash(concatenateUint8s([salt, sharedSecret]));
var signKp = Nacl.sign.keyPair.fromSeed(hash.subarray(0, 32));
var cryptKey = hash.subarray(32, 64);
// 64 uint8s
var hash = Nacl.hash(concatenateUint8s([salt, sharedSecret]));
var signKp = Nacl.sign.keyPair.fromSeed(hash.subarray(0, 32));
var cryptKey = hash.subarray(32, 64);
return {
cryptKey: encodeBase64(cryptKey),
signKey: encodeBase64(signKp.secretKey),
validateKey: encodeBase64(signKp.publicKey)
};
return {
cryptKey: encodeBase64(cryptKey),
signKey: encodeBase64(signKp.secretKey),
validateKey: encodeBase64(signKp.publicKey)
};
} catch (e) {
console.error('invalid keys or other problem deriving keys');
console.error(e);
return null;
}
};
Curve.createEncryptor = function (keys) {
if (!keys || typeof(keys) !== 'object') {
return void console.error("invalid input for createEncryptor");
}
var cryptKey = decodeBase64(keys.cryptKey);
var signKey = decodeBase64(keys.signKey);
var validateKey = decodeBase64(keys.validateKey);

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,11 @@
(function () {
var Mod = function (ApiConfig) {
var requireConf;
if (ApiConfig && ApiConfig.requireConf) {
requireConf = ApiConfig.requireConf;
}
var urlArgs = typeof(requireConf.urlArgs) === 'string'? '?' + urlArgs: '';
var Module = {};
var isSupported = Module.isSupported = function () {
@@ -41,8 +48,8 @@
}
};
var DEFAULT_MAIN = '/customize/main-favicon.png';
var DEFAULT_ALT = '/customize/alt-favicon.png';
var DEFAULT_MAIN = '/customize/main-favicon.png' + urlArgs;
var DEFAULT_ALT = '/customize/alt-favicon.png' + urlArgs;
var createFavicon = function () {
console.log("creating favicon");
@@ -110,14 +117,14 @@
cancel: cancel,
};
};
return Module;
};
if (typeof(module) !== 'undefined' && module.exports) {
module.exports = Module;
module.exports = Mod();
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
define(function () {
return Module;
});
define(['/api/config'], Mod);
} else {
window.Visible = Module;
window.Visible = Mod();
}
}());

View File

@@ -341,25 +341,14 @@ function isDataSchema(url) {
return url.substr(i, 5).toLowerCase() === 'data:';
}
function getPDFFileNameFromURL(url) {
var defaultFilename = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'document.pdf';
if (isDataSchema(url)) {
console.warn('getPDFFileNameFromURL: ' + 'ignoring "data:" URL for performance reasons.');
return defaultFilename;
var query;
var title;
if (/\#/.test(url)) {
url.replace(/\#(.*)$/, function (all, t) {
title = t;
});
}
var reURI = /^(?:(?:[^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
var splitURI = reURI.exec(url);
var suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]);
if (suggestedFilename) {
suggestedFilename = suggestedFilename[0];
if (suggestedFilename.indexOf('%') !== -1) {
try {
suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0];
} catch (e) {}
}
}
return suggestedFilename || defaultFilename;
return title || 'document.pdf';
}
function normalizeWheelEventDelta(evt) {
var delta = Math.sqrt(evt.deltaX * evt.deltaX + evt.deltaY * evt.deltaY);
@@ -1192,11 +1181,13 @@ var PDFViewerApplication = {
setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
this.url = url;
this.baseUrl = url.split('#')[0];
var title = (0, _ui_utils.getPDFFileNameFromURL)(url, '');
var title = _ui_utils.getPDFFileNameFromURL(url);
if (!title) {
try {
title = decodeURIComponent((0, _pdfjsLib.getFilenameFromUrl)(url)) || url;
} catch (e) {
console.error(e)
title = url;
}
}

View File

@@ -24,7 +24,7 @@ body {
}
#app.ready {
background: url('/customize/bg3.jpg') no-repeat center center;
//background: url('/customize/bg3.jpg') no-repeat center center;
background-size: cover;
background-position: center;
}

View File

@@ -91,7 +91,14 @@ define([
Title.updateTitle(title || Title.defaultTitle);
toolbar.addElement(['pageTitle'], {pageTitle: title});
var displayFile = function (ev, sizeMb) {
var displayFile = function (ev, sizeMb, CB) {
var called_back;
var cb = function (e) {
if (called_back) { return; }
called_back = true;
if (CB) { CB(e); }
};
var $mt = $dlview.find('media-tag');
var cryptKey = secret.keys && secret.keys.fileKeyStr;
var hexFileName = Cryptpad.base64ToHex(secret.channel);
@@ -127,7 +134,7 @@ define([
// make pdfs big
var toolbarHeight = $iframe.find('#toolbar').height();
$iframe.find('media-tag iframe').css({
var $another_iframe = $iframe.find('media-tag iframe').css({
'height': 'calc(100vh - ' + toolbarHeight + 'px)',
'width': '100vw',
'position': 'absolute',
@@ -135,10 +142,19 @@ define([
'left': 0,
'border': 0
});
if ($another_iframe.length) {
$another_iframe.load(function () {
cb();
});
} else {
cb();
}
})
.on('decryptionError', function (e) {
var error = e.originalEvent;
Cryptpad.alert(error.message);
//Cryptpad.alert(error.message);
cb(error.message);
})
.on('decryptionProgress', function (e) {
var progress = e.originalEvent;
@@ -188,7 +204,9 @@ define([
var onClick = function (ev) {
if (decrypting) { return; }
decrypting = true;
displayFile(ev, sizeMb);
displayFile(ev, sizeMb, function (err) {
if (err) { Cryptpad.alert(err); }
});
};
if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); }
$dlform.find('#dl, #progress').click(onClick);

View File

@@ -2,7 +2,7 @@
<html class="cp">
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
<head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>

View File

@@ -1,3 +1,3 @@
<!DOCTYPE html>
<html dir="ltr" lang="en"><head><title>Rich Text Editor, editor1</title><style data-cke-temp="1">html{cursor:text;*cursor:auto}
img,input,textarea{cursor:default}</style><link type="text/css" rel="stylesheet" href="/customize/ckeditor-contents.css"><link type="text/css" rel="stylesheet" href="http://localhost:3000/bower_components/ckeditor/plugins/tableselection//styles/tableselection.css"></head><body><p><br></p></body></html>
img,input,textarea{cursor:default}</style><link type="text/css" rel="stylesheet" href="/customize/ckeditor-contents.css"><link type="text/css" rel="stylesheet" href="/bower_components/ckeditor/plugins/tableselection/styles/tableselection.css"></head><body><p><br></p></body></html>

View File

@@ -313,6 +313,10 @@ define([
if (!readOnly && !initializing) {
userDocStateDom.setAttribute("contenteditable", "true"); // lol wtf
}
$(userDocStateDom).find('script, applet, object, iframe').remove();
$(userDocStateDom).find('a').filter(function (i, x) {
return ! /^(https|http|ftp):\/\/[^\s\n]*$/.test(x.getAttribute('href'));
}).remove();
var patch = (DD).diff(inner, userDocStateDom);
(DD).apply(inner, patch);
if (readOnly) {
@@ -625,8 +629,10 @@ define([
if (stringify(hjson2) !== stringify(hjson)) {
console.log('err');
console.error("shjson2 !== shjson");
Cryptpad.errorLoadingScreen(Messages.wrongApp);
throw new Error();
// TODO(cjd): This is removed because the XSS filter in applyHjson()
// is applied on incoming content so it causes this to fail.
//Cryptpad.errorLoadingScreen(Messages.wrongApp);
//throw new Error();
}
}
} else {

View File

@@ -470,13 +470,16 @@
// CryptPad
var _iframe = window._iframe = iframe.$;
var fw = this;
var fw = this;
_iframe.contentWindow.onload = function () {}
var intr = setInterval(function () {
//console.log(_iframe.contentWindow.document.body);
if (_iframe.contentWindow && _iframe.contentWindow.document && _iframe.contentWindow.document.body) {
clearInterval(intr);
CKEDITOR.tools.callFunction(fw._.frameLoadedHandler, _iframe.contentWindow);
}
if (!_iframe.contentWindow) { return; }
if (!_iframe.contentWindow.document) { return; }
if (_iframe.contentWindow.document.readyState !== 'complete') { return; }
if (!_iframe.contentWindow.document.getElementsByTagName('title').length) { return; }
clearInterval(intr);
CKEDITOR.tools.callFunction(fw._.frameLoadedHandler, _iframe.contentWindow);
}, 10);
return;

View File

@@ -2,7 +2,7 @@
<html class="cp">
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
<head>
<title data-localization="main_title">Cryptpad: Zero Knowledge, Collaborative Real Time Editing</title>
<title data-localization="main_title">CryptPad: Zero Knowledge, Collaborative Real Time Editing</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" type="image/png" href="/customize/main-favicon.png" id="favicon"/>

View File

@@ -36,6 +36,7 @@ h6 { font-size: 24px; }
body {
.CodeMirror {
height: 100%;
font-size: initial;
}
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);

View File

@@ -11,6 +11,7 @@ define([
'/common/cryptget.js',
'/whiteboard/colors.js',
'/customize/application_config.js',
'/common/common-thumbnail.js',
'/bower_components/secure-fabric.js/dist/fabric.min.js',
'/bower_components/file-saver/FileSaver.min.js',
@@ -19,7 +20,7 @@ define([
'less!/customize/src/less/cryptpad.less',
'less!/whiteboard/whiteboard.less',
'less!/customize/src/less/toolbar.less',
], function ($, Config, Realtime, Crypto, Toolbar, TextPatcher, JSONSortify, JsonOT, Cryptpad, Cryptget, Colors, AppConfig) {
], function ($, Config, Realtime, Crypto, Toolbar, TextPatcher, JSONSortify, JsonOT, Cryptpad, Cryptget, Colors, AppConfig, Thumb) {
var saveAs = window.saveAs;
var Messages = Cryptpad.Messages;
@@ -212,13 +213,18 @@ window.canvas = canvas;
module.FM = Cryptpad.createFileManager({});
module.upload = function (title) {
$canvas[0].toBlob(function (blob) {
blob.name = title;
var reader = new FileReader();
reader.onloadend = function () {
module.FM.handleFile(blob);
};
reader.readAsArrayBuffer(blob);
var canvas = $canvas[0];
var finish = function (thumb) {
canvas.toBlob(function (blob) {
blob.name = title;
module.FM.handleFile(blob, void 0, thumb);
});
};
Thumb.fromCanvas(canvas, function (e, blob) {
// carry on even if you can't get a thumbnail
if (e) { console.error(e); }
finish(blob);
});
};