Get rid of dependence on usercontent.riot.im

This commit is contained in:
Michael Telatynski 2020-02-07 22:07:29 +00:00
parent 3c49515e08
commit 40b8db84e3

View file

@ -26,7 +26,7 @@ import {decryptFile} from '../../../utils/DecryptFile';
import Tinter from '../../../Tinter'; import Tinter from '../../../Tinter';
import request from 'browser-request'; import request from 'browser-request';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import SdkConfig from "../../../SdkConfig"; import AccessibleButton from "../elements/AccessibleButton";
// A cached tinted copy of require("../../../../res/img/download.svg") // A cached tinted copy of require("../../../../res/img/download.svg")
@ -94,84 +94,6 @@ Tinter.registerTintable(updateTintedDownloadImage);
// The downside of using a second domain is that it complicates hosting, // The downside of using a second domain is that it complicates hosting,
// the downside of using a sandboxed iframe is that the browers are overly // the downside of using a sandboxed iframe is that the browers are overly
// restrictive in what you are allowed to do with the generated URL. // restrictive in what you are allowed to do with the generated URL.
//
// For now given how unusable the blobs generated in sandboxed iframes are we
// default to using a renderer hosted on "usercontent.riot.im". This is
// overridable so that people running their own version of the client can
// choose a different renderer.
//
// To that end the current version of the blob generation is the following
// html:
//
// <html><head><script>
// var params = window.location.search.substring(1).split('&');
// var lockOrigin;
// for (var i = 0; i < params.length; ++i) {
// var parts = params[i].split('=');
// if (parts[0] == 'origin') lockOrigin = decodeURIComponent(parts[1]);
// }
// window.onmessage=function(e){
// if (lockOrigin === undefined || e.origin === lockOrigin) eval("("+e.data.code+")")(e);
// }
// </script></head><body></body></html>
//
// This waits to receive a message event sent using the window.postMessage API.
// When it receives the event it evals a javascript function in data.code and
// runs the function passing the event as an argument. This version adds
// support for a query parameter controlling the origin from which messages
// will be processed as an extra layer of security (note that the default URL
// is still 'v1' since it is backwards compatible).
//
// In particular it means that the rendering function can be written as a
// ordinary javascript function which then is turned into a string using
// toString().
//
const DEFAULT_CROSS_ORIGIN_RENDERER = "https://usercontent.riot.im/v1.html";
/**
* Render the attachment inside the iframe.
* We can't use imported libraries here so this has to be vanilla JS.
*/
function remoteRender(event) {
const data = event.data;
const img = document.createElement("img");
img.id = "img";
img.src = data.imgSrc;
const a = document.createElement("a");
a.id = "a";
a.rel = data.rel;
a.target = data.target;
a.download = data.download;
a.style = data.style;
a.style.fontFamily = "Arial, Helvetica, Sans-Serif";
a.href = window.URL.createObjectURL(data.blob);
a.appendChild(img);
a.appendChild(document.createTextNode(data.textContent));
const body = document.body;
// Don't display scrollbars if the link takes more than one line
// to display.
body.style = "margin: 0px; overflow: hidden";
body.appendChild(a);
}
/**
* Update the tint inside the iframe.
* We can't use imported libraries here so this has to be vanilla JS.
*/
function remoteSetTint(event) {
const data = event.data;
const img = document.getElementById("img");
img.src = data.imgSrc;
img.style = data.imgStyle;
const a = document.getElementById("a");
a.style = data.style;
}
/** /**
* Get the current CSS style for a DOMElement. * Get the current CSS style for a DOMElement.
@ -283,7 +205,6 @@ export default createReactClass({
// will be inside the iframe so we wont be able to update // will be inside the iframe so we wont be able to update
// it directly. // it directly.
this._iframe.current.contentWindow.postMessage({ this._iframe.current.contentWindow.postMessage({
code: remoteSetTint.toString(),
imgSrc: tintedDownloadImageURL, imgSrc: tintedDownloadImageURL,
style: computedStyle(this._dummyLink.current), style: computedStyle(this._dummyLink.current),
}, "*"); }, "*");
@ -306,7 +227,7 @@ export default createReactClass({
// Wait for the user to click on the link before downloading // Wait for the user to click on the link before downloading
// and decrypting the attachment. // and decrypting the attachment.
let decrypting = false; let decrypting = false;
const decrypt = () => { const decrypt = (e) => {
if (decrypting) { if (decrypting) {
return false; return false;
} }
@ -323,16 +244,15 @@ export default createReactClass({
}); });
}).finally(() => { }).finally(() => {
decrypting = false; decrypting = false;
return;
}); });
}; };
return ( return (
<span className="mx_MFileBody"> <span className="mx_MFileBody">
<div className="mx_MFileBody_download"> <div className="mx_MFileBody_download">
<a href="javascript:void(0)" onClick={decrypt}> <AccessibleButton onClick={decrypt}>
{ _t("Decrypt %(text)s", { text: text }) } { _t("Decrypt %(text)s", { text: text }) }
</a> </AccessibleButton>
</div> </div>
</span> </span>
); );
@ -341,7 +261,6 @@ export default createReactClass({
// When the iframe loads we tell it to render a download link // When the iframe loads we tell it to render a download link
const onIframeLoad = (ev) => { const onIframeLoad = (ev) => {
ev.target.contentWindow.postMessage({ ev.target.contentWindow.postMessage({
code: remoteRender.toString(),
imgSrc: tintedDownloadImageURL, imgSrc: tintedDownloadImageURL,
style: computedStyle(this._dummyLink.current), style: computedStyle(this._dummyLink.current),
blob: this.state.decryptedBlob, blob: this.state.decryptedBlob,
@ -355,13 +274,7 @@ export default createReactClass({
}, "*"); }, "*");
}; };
// If the attachment is encryped then put the link inside an iframe. // If the attachment is encrypted then put the link inside an iframe.
let renderer_url = DEFAULT_CROSS_ORIGIN_RENDERER;
const appConfig = SdkConfig.get();
if (appConfig && appConfig.cross_origin_renderer_url) {
renderer_url = appConfig.cross_origin_renderer_url;
}
renderer_url += "?origin=" + encodeURIComponent(window.location.origin);
return ( return (
<span className="mx_MFileBody"> <span className="mx_MFileBody">
<div className="mx_MFileBody_download"> <div className="mx_MFileBody_download">
@ -373,7 +286,7 @@ export default createReactClass({
*/ } */ }
<a ref={this._dummyLink} /> <a ref={this._dummyLink} />
</div> </div>
<iframe src={renderer_url} onLoad={onIframeLoad} ref={this._iframe} /> <iframe src="usercontent/" onLoad={onIframeLoad} ref={this._iframe} sandbox="allow-scripts allow-downloads" />
</div> </div>
</span> </span>
); );