xmpp.chapril.org-conversejs/src/headless/utils/url.js
JC Brand c3933426b9 Refactor checkFileTypes
- Don't check the protocol in `checkFileTypes`, it should be doing one
    thing only, and that is check whether the URL ends with a particular
    file extension.
- Raise an error when a URI object can't be created from the passed in URL

Adds new function `isAllowedProtocolForMedia` which checks whether the
URL points to a file on the file system (`file:`), is in a Chrome
extension or uses HTTPs.

Use that in `shouldRenderMediaFromURL` to filter out URLs that shouldn't
be rendered.

Re-add utility methods to the `u` object so that 3rd party plugins can
use them.
2022-03-29 19:41:16 +02:00

142 lines
4.0 KiB
JavaScript

import URI from 'urijs';
import log from '@converse/headless/log';
import { api, converse } from '@converse/headless/core';
const { u } = converse.env;
/**
* Given a url, check whether the protocol being used is allowed for rendering
* the media in the chat (as opposed to just rendering a URL hyperlink).
* @param { String } url
* @returns { Boolean }
*/
function isAllowedProtocolForMedia(url) {
const uri = getURI(url);
const { protocol } = window.location;
if (['chrome-extension:','file:'].includes(protocol)) {
return true;
}
return (
protocol === 'http:' ||
(protocol === 'https:' && ['https', 'aesgcm'].includes(uri.protocol().toLowerCase()))
);
}
export function getURI (url) {
try {
return url instanceof URI ? url : new URI(url);
} catch (error) {
log.debug(error);
return null;
}
}
/**
* Given the an array of file extensions, check whether a URL points to a file
* ending in one of them.
* @param { String[] } types - An array of file extensions
* @param { String } url
* @returns { Boolean }
* @example
* checkFileTypes(['.gif'], 'https://conversejs.org/cat.gif?foo=bar');
*/
function checkFileTypes (types, url) {
const uri = getURI(url);
if (uri === null) {
throw new Error(`checkFileTypes: could not parse url ${url}`);
}
const filename = uri.filename().toLowerCase();
return !!types.filter(ext => filename.endsWith(ext)).length;
}
export function isDomainWhitelisted (whitelist, url) {
const uri = getURI(url);
const subdomain = uri.subdomain();
const domain = uri.domain();
const fulldomain = `${subdomain ? `${subdomain}.` : ''}${domain}`;
return whitelist.includes(domain) || whitelist.includes(fulldomain);
}
export function shouldRenderMediaFromURL (url_text, type) {
if (!isAllowedProtocolForMedia(url_text)) {
return false;
}
const may_render = api.settings.get('render_media');
const is_domain_allowed = isDomainAllowed(url_text, `allowed_${type}_domains`);
if (Array.isArray(may_render)) {
return is_domain_allowed && isDomainWhitelisted (may_render, url_text);
} else {
return is_domain_allowed && may_render;
}
}
export function filterQueryParamsFromURL (url) {
const paramsArray = api.settings.get('filter_url_query_params');
if (!paramsArray) return url;
const parsed_uri = getURI(url);
return parsed_uri.removeQuery(paramsArray).toString();
}
export function isDomainAllowed (url, setting) {
const allowed_domains = api.settings.get(setting);
if (!Array.isArray(allowed_domains)) {
return true;
}
try {
return isDomainWhitelisted(allowed_domains, url);
} catch (error) {
log.debug(error);
return false;
}
}
/**
* Accepts a {@link MediaURL} object and then checks whether its domain is
* allowed for rendering in the chat.
* @param { MediaURL } o
* @returns { Bool }
*/
export function isMediaURLDomainAllowed (o) {
return o.is_audio && isDomainAllowed(o.url, 'allowed_audio_domains') ||
o.is_video && isDomainAllowed(o.url, 'allowed_video_domains') ||
o.is_image && isDomainAllowed(o.url, 'allowed_image_domains');
}
export function isURLWithImageExtension (url) {
return checkFileTypes(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg'], url);
}
export function isGIFURL (url) {
return checkFileTypes(['.gif'], url);
}
export function isAudioURL (url) {
return checkFileTypes(['.ogg', '.mp3', '.m4a'], url);
}
export function isVideoURL (url) {
return checkFileTypes(['.mp4', '.webm'], url);
}
export function isImageURL (url) {
const regex = api.settings.get('image_urls_regex');
return regex?.test(url) || isURLWithImageExtension(url);
}
export function isEncryptedFileURL (url) {
return url.startsWith('aesgcm://');
}
Object.assign(u, {
isAudioURL,
isGIFURL,
isVideoURL,
isImageURL,
isURLWithImageExtension,
checkFileTypes,
getURI,
shouldRenderMediaFromURL,
isAllowedProtocolForMedia,
});