Newer
Older
// Safe Links Cleaner
// Copyright 2021 David Byers <david.byers@liu.se>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Shared code
David Byers
committed
/**
* List of regexps that match safe links. The original URL must be
* collected in match group 1.
David Byers
committed
*/
const regexpList = [
'https?://[^.]+[.]safelinks[.]protection[.]outlook[.]com/[?]url=([^&]+)&.*',
'https?://linkprotect[.]cudasvc[.]com/url[?]a=([^&]+)&.*'
];
/**
* Concatenated regexp for all safe links types.
*/
const safelinksRegexp = new RegExp(
'(?:' + regexpList.map((string) => '(?:' + string + ')').join('|') + ')',
'gi'
);
David Byers
committed
/**
* The ID for the popup element that is added to the HTML document.
*/
const safelinksPopupId = 'safelinks-cleaner-thunderbird-popup';
/**
* The class that is added to the popup when visible.
*/
const safelinksPopupVisibleClass = 'safelinks-cleaner-thunderbird-popup-visible';
/**
* Return the original URL for a safe link.
* @param {string} link - The safe link.
* @returns {string} The original link or the safe link if there was an error.
*/
function untangleLink(link) {
return link.replaceAll(
safelinksRegexp,
(match, ...url) => {
return decodeURIComponent(url.find((el) => el));
}
catch (e) {
return url;
}
});
}
David Byers
committed
/**
* Check if a link is a safe link.
* @param {string} link - The URL to check.
* @returns {boolean} Returns true if the link is a safe link.
*/
function isTangledLink(link) {
return link.match(safelinksRegexp);
}
David Byers
committed
/**
* Return the text nodes under a DOM element.
* @param {Element} elem - The element to return text nodes for.
* @returns {Element[]} The text elements under elem.
*/
function getTextNodes(elem) {
var result = [];
if (elem) {
for (var nodes = elem.childNodes, i = nodes.length; i--;) {
let node = nodes[i];
let nodeType = node.nodeType;
if (nodeType == Node.TEXT_NODE) {
result.push(node);
}
else if (nodeType == Node.ELEMENT_NODE
|| nodeType == Node.DOCUMENT_NODE
|| nodeType == Node.DOCUMENT_FRAGMENT_NODE) {
result = result.concat(getTextNodes(node));
}
}
}
return result;
}
/**
* Add event handlers to a link so it will show the original url.
* @param {Element} link - The link to add the popup to.
*/
function addLinkPopup(link) {
link.addEventListener('mouseenter', withoutMutationObserver(showOriginalUrl), {passive: true});
link.addEventListener('mouseleave', scheduleHidePopup, {passive: true});
}
* @param {Element} root - DOM element in which to fix links.
* @param {Element} is_owa - Set to true when fixing Outlook.
console.log('enter fixAllTheLinks'); // DEBUG
for (const link of root.getElementsByTagName('a')) {
if (link.href) {
// Untangle link text
for (const node of getTextNodes(link)) {
if (isTangledLink(node.textContent)) {
textWasTangled = true;
node.textContent = untangleLink(node.textContent);
}
}
// Create popup event handlers
if (isTangledLink(link.href) && (!is_owa || textWasTangled)) {
console.log('exit fixAllTheLinks'); // DEBUG
/**
* Remove all safe links in an element
* @param {Element} root - DOM element in which to fix links.
*/
function removeAllTheLinks(root) {
console.log('enter removeAllTheLinks'); // DEBUG
for (const link of root.getElementsByTagName('a')) {
link.href = untangleLink(link.href);
for (const textNode of getTextNodes(root)) {
textNode.textContent = untangleLink(textNode.textContent);
}
console.log('exit removeAllTheLinks'); // DEBUG