ensure asatt=1 is aded to image download URL to set Content-Disposition: attachment

This commit is contained in:
or-else
2023-12-21 16:06:19 -08:00
parent 40916e2fc3
commit f932b973f8
10 changed files with 65 additions and 10 deletions
+30
View File
@@ -172,6 +172,36 @@ export function sanitizeUrlForMime(url, mimeMajor) {
return null;
}
// Append query parameter 'asatt=1' to the URL.
// It will cause Tinode server to add 'Content-Disposition: attachment' header when serving it.
// The URL here is always absolute.
export function urlAsAttachment(url) {
// TODO: check if URL is local or remote, i.e. compare to window.location.origin.
/*
if (typeof window.location == 'object') {
if (!url.startsWith(window.location.origin)) {
return url;
}
}
*/
// Split URL into host+path, query, fragment.
let query = '', fragment = '';
const idxF = url.indexOf('#');
if (idxF > 0) {
fragment = url.substring(idxF+1);
url = url.substring(0, idxF);
}
const idxQ = url.indexOf('?');
if (idxQ > 0) {
query = url.substring(idxQ+1);
url = url.substring(0, idxQ);
}
// Add parameter and reassemble.
const params = new URLSearchParams(query);
params.append('asatt', '1');
return `${url}?${params.toString()}` + (fragment ? `#${fragment}` : '');
}
// Given message's received status, return name and color of a delivery indicator icon.
export function deliveryMarker(received) {
switch (received) {
+5 -1
View File
@@ -5,6 +5,7 @@ import SendMessage from './send-message.jsx';
import { REM_SIZE } from '../config.js';
import { fitImageSize } from '../lib/blob-helpers.js';
import { bytesToHumanSize, shortenFileName } from '../lib/strformat.js';
import { urlAsAttachment } from '../lib/utils.js';
export default class ImagePreview extends React.PureComponent {
constructor(props) {
@@ -71,13 +72,16 @@ export default class ImagePreview extends React.PureComponent {
const width = this.props.content.width || '-';
const height = this.props.content.height || '-';
const download_url = urlAsAttachment(this.props.content.url);
return (
<div id="image-preview">
<div id="image-preview-caption-panel">
{this.props.onSendMessage ?
<span>{fname}</span>
:
<a href={this.props.content.url} download={this.props.content.filename}>
<a href={download_url} download={this.props.content.filename}>
<i className="material-icons">file_download</i> <FormattedMessage
id="download_action" defaultMessage="download" description="Call to action [download]" />
</a>
+23 -2
View File
@@ -3419,7 +3419,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ sanitizeUrl: () => (/* binding */ sanitizeUrl),
/* harmony export */ sanitizeUrlForMime: () => (/* binding */ sanitizeUrlForMime),
/* harmony export */ theCard: () => (/* binding */ theCard),
/* harmony export */ updateFavicon: () => (/* binding */ updateFavicon)
/* harmony export */ updateFavicon: () => (/* binding */ updateFavicon),
/* harmony export */ urlAsAttachment: () => (/* binding */ urlAsAttachment)
/* harmony export */ });
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tinode-sdk */ "tinode-sdk");
/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_0__);
@@ -3540,6 +3541,23 @@ function sanitizeUrlForMime(url, mimeMajor) {
}
return null;
}
function urlAsAttachment(url) {
let query = '',
fragment = '';
const idxF = url.indexOf('#');
if (idxF > 0) {
fragment = url.substring(idxF + 1);
url = url.substring(0, idxF);
}
const idxQ = url.indexOf('?');
if (idxQ > 0) {
query = url.substring(idxQ + 1);
url = url.substring(0, idxQ);
}
const params = new URLSearchParams(query);
params.append('asatt', '1');
return `${url}?${params.toString()}` + (fragment ? `#${fragment}` : '');
}
function deliveryMarker(received) {
switch (received) {
case tinode_sdk__WEBPACK_IMPORTED_MODULE_0__.Tinode.MESSAGE_STATUS_SENDING:
@@ -13408,6 +13426,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../config.js */ "./src/config.js");
/* harmony import */ var _lib_blob_helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/blob-helpers.js */ "./src/lib/blob-helpers.js");
/* harmony import */ var _lib_strformat_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../lib/strformat.js */ "./src/lib/strformat.js");
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
@@ -13471,12 +13491,13 @@ class ImagePreview extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom
const fname = (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_5__.shortenFileName)(this.props.content.filename, maxlength) || '-';
const width = this.props.content.width || '-';
const height = this.props.content.height || '-';
const download_url = (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_6__.urlAsAttachment)(this.props.content.url);
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
id: "image-preview"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
id: "image-preview-caption-panel"
}, this.props.onSendMessage ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, fname) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: this.props.content.url,
href: download_url,
download: this.props.content.filename
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons"
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -2359,7 +2359,7 @@ Drafty.tagName = function (style) {
return FORMAT_TAGS[style] && FORMAT_TAGS[style].html_tag;
};
Drafty.attrValue = function (style, data) {
if (data && DECORATORS[style]) {
if (data && DECORATORS[style] && DECORATORS[style].props) {
return DECORATORS[style].props(data);
}
return undefined;
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long