Skip to content
Snippets Groups Projects
Commit 8ec4d86f authored by Igor Markin's avatar Igor Markin
Browse files

Use mime normalizer from external repo

parent eb529b26
No related branches found
No related tags found
1 merge request!42Use external mime normalizer
Showing with 33 additions and 902 deletions
import { describe, it, expect } from "@jest/globals";
const fs = require("fs");
const path = require("path");
import MimeVerificationService from "../src/services/MimeVerificationService/MimeVerificationService";
describe("MimeVerificationService", () => {
it("performs HTML normalization", async () => {
const originalHtml = `<div class="test" id="text" >
<a href="https://test.com" target="_blank">
<img src="https://src.img" alt="test" contenteditable="false"/>
Picture
</a>
<br>
<p contenteditable="false" role="button" aria-label="hello">Hello world!</p>
</div>`;
const normalizedHtml =
"<DIV>\n" +
'<A href="https://test.com">\n' +
'<IMG alt="test" src="https://src.img"/>\n' +
"<TEXT>Picture</TEXT>\n" +
"</A>\n" +
"<P>\n<TEXT>Helloworld!</TEXT>\n</P>\n" +
"</DIV>\n";
const outHtml = MimeVerificationService.normalizeVendorHtml(
originalHtml,
"GMAIL"
);
expect(outHtml).toEqual(normalizedHtml);
});
it("cleans up gdrive attachments panes", () => {
const originalHtml =
'test11<div contenteditable="false" class="gmail_chip gmail_drive_chip" style="width: 396px; height: 18px; max-height: 18px; background-color: rgb(245, 245, 245); padding: 5px; font-weight: bold; font-size: 13px; cursor: default; border: 1px solid rgb(221, 221, 221); line-height: 1;"><a href="https://drive.google.com/file/d/1iRn30S2lznsRZZxZMRXEAIOTSzKPUumC/view?usp=drive_web" target="_blank" style="display: inline-block; max-width: 366px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration-line: none; padding: 1px 0; border: none;" aria-label="Designing-Distributed-Systems.pdf"><img alt="tet" style="vertical-align: bottom; border: none;" src="https://drive-thirdparty.googleusercontent.com/16/type/application/pdf">&nbsp;<span dir="ltr" style="vertical-align: bottom;">Designing-Distributed-Systems.pdf</span></a></div>';
const normalizedHTML =
"<TEXT>test11</TEXT>\n" +
'<A href="https://drive.google.com/file/d/1iRn30S2lznsRZZxZMRXEAIOTSzKPUumC/view?usp=drive_web"/>\n';
const outHTMl = MimeVerificationService.normalizeVendorHtml(
originalHtml,
"GMAIL"
);
expect(outHTMl).toEqual(normalizedHTML);
});
it("cleans up outlook attachments panes", () => {
const sentHTML = fs
.readFileSync(
path.resolve(__dirname, "data/outlook_onedrive_attachments_sender.html")
)
.toString();
const receivedHTML = fs
.readFileSync(
path.resolve(
__dirname,
"data/outlook_onedrive_attachments_receiver.html"
)
)
.toString();
const normalizedSentHTML = MimeVerificationService.normalizeVendorHtml(
sentHTML,
"OUTLOOK"
);
const normalizedReceivedHTML = MimeVerificationService.normalizeVendorHtml(
receivedHTML,
"OUTLOOK"
);
expect(normalizedReceivedHTML).toContain(normalizedSentHTML);
});
it("unwinds span tags for outlook", () => {
const originalHtml = `<div class="test" id="text" >
<a href="https://test.com" target="_blank">
<span><p>Hello</p><span><span>World</span></span></span>
Picture
</a>
<br>
<p contenteditable="false" role="button" aria-label="hello">Hello world!</p>
</div>`;
const normalizedHtml =
"<DIV>\n" +
'<A href="https://test.com">\n' +
"<P>\n<TEXT>Hello</TEXT>\n</P>\n" +
"<TEXT>World</TEXT>\n" +
"<TEXT>Picture</TEXT>\n" +
"</A>\n" +
"<P>\n<TEXT>Helloworld!</TEXT>\n</P>\n" +
"</DIV>\n";
const outHTML = MimeVerificationService.normalizeVendorHtml(
originalHtml,
"OUTLOOK"
);
expect(normalizedHtml).toContain(outHTML);
});
it("fixes issue 33", () => {
const sentHTML = fs
.readFileSync(path.resolve(__dirname, "data/outlook_issue_33_sent.html"))
.toString();
const receivedHTML = fs
.readFileSync(
path.resolve(__dirname, "data/outlook_issue_33_received.html")
)
.toString();
const normalizedSentHTML = MimeVerificationService.normalizeVendorHtml(
sentHTML,
"OUTLOOK"
);
const normalizedReceivedHTML = MimeVerificationService.normalizeVendorHtml(
receivedHTML,
"OUTLOOK"
);
expect(normalizedReceivedHTML).toContain(normalizedSentHTML);
});
});
......@@ -3,11 +3,13 @@ import { RKAUtility } from "../src";
describe("RKAUtility", () => {
it("performs string search using Rabin-Karp algorithm", async () => {
const originalString = "testreply";
const originalString = "<DIV>\n" + "<TEXT>testvam</TEXT>\n" + "</DIV>";
const hash = RKAUtility.getRabinFingerprint(originalString);
console.log(hash);
// const stringToSearchIn =
// "testreply[image:qrcode.png]<https://gcloud-dev.vrgnservices.com/?q=CiBzo4Tb6V46VK_i0XGdEAnLDyuVh6WMQ3OjFZ6hS3cpLBIg5StsOWsJqWSzc-MtBvtBnyxz0LMKxEuP6tBY3yQDR30=&timestamp=1605520113253>OnMon,Nov16,2020at11:48AMZdravkoIliev<zdravko.iliev61@gmail.com>wrote:>newtesttest>[image:qrcode.png]>><https://gcloud-dev.vrgnservices.com/?q=CiDS9DmcKnlqUc-nEEjRCMYms9Puat9ZYQGdCVty6BhYABIgXnc1XhSGgzos0B21kZTbe7tAVPBiRo9VXMQFUNDpdpc=&timestamp=1605520082515>>";
const stringToSearchIn =
"testreply[image:qrcode.png]<https://gcloud-dev.vrgnservices.com/?q=CiBzo4Tb6V46VK_i0XGdEAnLDyuVh6WMQ3OjFZ6hS3cpLBIg5StsOWsJqWSzc-MtBvtBnyxz0LMKxEuP6tBY3yQDR30=&timestamp=1605520113253>OnMon,Nov16,2020at11:48AMZdravkoIliev<zdravko.iliev61@gmail.com>wrote:>newtesttest>[image:qrcode.png]>><https://gcloud-dev.vrgnservices.com/?q=CiDS9DmcKnlqUc-nEEjRCMYms9Puat9ZYQGdCVty6BhYABIgXnc1XhSGgzos0B21kZTbe7tAVPBiRo9VXMQFUNDpdpc=&timestamp=1605520082515>>";
const stringToSearchIn = "<DIV>\n<TEXT>testvam</TEXT>\n</DIV>\n";
const index = RKAUtility.searchFingerprintInText(
stringToSearchIn,
......
<span>
<!-- https://code.vereign.com/light/clients/outlookaddin/-/issues/33 -->
<div>
<ol>
<li>
<a href="https://vk.com/alexey.lunin0" class="x_left_row"><span class="x_left_label x_inl_bl"><br class="x_Apple-interchange-newline">Моя страница</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/feed" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Новости</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/im" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Мессенджер</span><span class="x_left_count_wrap x_fl_r"><span class="x_inl_bl x_left_count">1</span></span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/friends" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Друзья</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/groups" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Сообщества</span><span class="x_left_count_wrap x_fl_r"><span class="x_inl_bl x_left_count">10</span></span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/albums16820605" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Фотографии</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/audios16820605" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Музыка</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/video" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Видео</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/clips" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Клипы</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/apps" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Игры</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<div class="x_more_div x_l_main"></div>
<li class="x_AppLeftMenuWithNotify">
<a href="https://vk.com/services" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Мини-приложения</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/vkpay" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">VK Pay</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/worki" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Работа</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<div class="x_more_div"></div>
<li>
<a href="https://vk.com/market" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Маркет</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/bookmarks?from_menu=1" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Закладки</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/docs" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Файлы</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/apps?act=manage" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Управление</span></a>
<div class="x_left_settings">
<div class="x_left_settings_inner"></div>
</div>
</li>
<div class="x_more_div"></div>
<li><a href="https://vk.com/covid19#utm_source=vk&amp;utm_medium=cpc&amp;utm_campaign=web_left_nav" class="x_left_row"><span class="x_left_icon x_fl_l"></span><span class="x_left_label x_inl_bl">Коронавирус</span></a></li>
</ol>
<br>
</div>
<div>
<br>
<div id="x_test-for-us"><a href="https://office.test.vereign.com/?q=CiDMDkdwWXSSqKTkZmupXsPRSREzYa9zE8KNfqdzEIHO2BIgzcz5rWorySQ4mm65FRbg6Bbtt75PNt2iqmIi3BcbsWs=&amp;timestamp=1605680510307" target="_blank"><img class="x_qrcode.png" name="x_qrcode.png" id="x_qrcode.png" alt="qrcode.png" width="160" src="cid:f6fe7676-87f4-4d86-8963-f36d3970331d"> </a></div>
<br>
</div>
</span>
<div>
<!-- https://code.vereign.com/light/clients/outlookaddin/-/issues/33 -->
<ol>
<li>
<a href="https://vk.com/alexey.lunin0" class="left_row"><span class="left_label inl_bl"><br class="Apple-interchange-newline">Моя страница</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/feed" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Новости</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/im" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Мессенджер</span><span class="left_count_wrap fl_r"><span class="inl_bl left_count">1</span></span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/friends" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Друзья</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/groups" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Сообщества</span><span class="left_count_wrap fl_r"><span class="inl_bl left_count">10</span></span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/albums16820605" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Фотографии</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/audios16820605" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Музыка</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/video" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Видео</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/clips" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Клипы</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/apps" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Игры</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<div class="more_div l_main"></div>
<li class="AppLeftMenuWithNotify">
<a href="https://vk.com/services" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Мини-приложения</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/vkpay" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">VK Pay</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/worki" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Работа</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<div class="more_div"></div>
<li>
<a href="https://vk.com/market" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Маркет</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/bookmarks?from_menu=1" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Закладки</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/docs" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Файлы</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<li>
<a href="https://vk.com/apps?act=manage" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Управление</span></a>
<div class="left_settings">
<div class="left_settings_inner"></div>
</div>
</li>
<div class="more_div"></div>
<li><a href="https://vk.com/covid19#utm_source=vk&amp;utm_medium=cpc&amp;utm_campaign=web_left_nav" class="left_row"><span class="left_icon fl_l"></span><span class="left_label inl_bl">Коронавирус</span></a></li>
</ol>
<br>
</div>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<!--[if lte mso 15 || CheckWebRef]-->
<div id="OwaReferenceAttachments" contenteditable="false">
<table style="padding-bottom: 13px; border-width: 0px; border-style: none;">
<tbody>
<tr valign="top">
<td>
<table style="border-width: 0px 0px 1px 0px; border-color:#C7C7C7; border-style: none none dotted none;">
<tbody>
<tr valign="top">
<td style="padding-bottom:7px;">
<table align="left" style="padding-right: 28px; border-width: 0px; background-color: rgb(255, 255, 255); border-spacing: 0px">
<tbody>
<tr valign="top">
<td style="padding: 0px;">
<div id="OwaReferenceAttachmentDescription" style="padding-left: 3px; font-size: 14px; font-family: 'Segoe UI', 'Segoe WP', 'Segoe UI WPC', Tahoma, Arial, sans-serif; color: rgb(102, 102, 102);">Пользователь Outlook Alex предоставил вам доступ к файлу OneDrive. Чтобы просмотреть его, щелкните ссылку ниже. </div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr valign="top">
<td>
<a href="https://1drv.ms/b/s!AibJ2eLVK8FvbfUtSHxMK5qSI1M" target="_blank">
<table align="left" style="padding-right: 28px; padding-bottom:10px; border-width: 0px; height:20px; background-color: rgb(255, 255, 255); border-spacing: 0px">
<tbody>
<tr valign="top">
<td style="padding: 0px;">
<div style="background-color: rgb(255, 255, 255); height: 20px; width: 20px; max-height: 20px;">
<a href="https://1drv.ms/b/s!AibJ2eLVK8FvbfUtSHxMK5qSI1M" target="_blank"><img width="20" style="border:0px;" src="https://r1.res.office365.com/owa/prem/images/dc-pdf_20.png"></a></div></td><td><div id="OwaReferenceAttachmentFileName2" style="padding: 0px 0px 0px 5px; font-size: 14px; font-family: 'Segoe UI', 'Segoe WP', 'Segoe UI WPC', Tahoma, Arial, sans-serif; color: rgb(0, 114, 198);"><a href="https://1drv.ms/b/s!AibJ2eLVK8FvbfUtSHxMK5qSI1M" target="_blank" style="text-decoration: none; margin: 0px; font-size: 14px; font-family: 'Segoe UI', 'Segoe WP', 'Segoe UI WPC', Tahoma, Arial, sans-serif; color: rgb(0, 114, 198);">Философия Жизни 2020.pdf</a></div></td><td style="display:none;visibility:hidden;" width="0" height="0"></td></tr></tbody></table></a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<div id="OwaReferenceAttachmentsEnd" style="display:none;visibility:hidden;"></div>
<!--[endif]-->
<span>
<div style="font-family:Calibri,Arial,Helvetica,sans-serif; font-size:12pt; color:rgb(0,0,0)">body1</div>
<br>
<div id="x_test-for-us"><a href="https://office.test.vereign.com/?q=CiB0mnF2S6nK48oig90YN1RycIpEoH6WoPvrryIQPSI9PBIgF7TtpfWU8fvPCRzE0qLvIGdZrza8rPMkX2Cw1GBL5Tg=&amp;timestamp=1605106666584" target="_blank"><img class="x_qrcode.png" name="x_qrcode.png" id="x_qrcode.png" alt="qrcode.png" width="160" src="cid:fcaae4ba-00d8-40a7-8431-01fe325a742d"> </a></div>
</span>
</body>
</html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<!--[if lte mso 15 || CheckWebRef]-->
<div id="OwaReferenceAttachments" contenteditable="false">
<table style="padding-bottom: 13px; border-width: 0px; border-style: none;">
<tbody>
<tr valign="top">
<td>
<table style="border-width: 0px 0px 1px 0px; border-color:#C7C7C7; border-style: none none dotted none;">
<tbody>
<tr valign="top">
<td style="padding-bottom:7px;">
<table align="left" style="padding-right: 28px; border-width: 0px; background-color: rgb(255, 255, 255); border-spacing: 0px">
<tbody>
<tr valign="top">
<td style="padding: 0px;">
<div id="OwaReferenceAttachmentDescription" style="padding-left: 3px; font-size: 14px; font-family: 'Segoe UI', 'Segoe WP', 'Segoe UI WPC', Tahoma, Arial, sans-serif; color: rgb(102, 102, 102);">Пользователь Outlook Alex предоставил вам доступ к файлу OneDrive. Чтобы просмотреть его, щелкните ссылку ниже. </div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr valign="top">
<td>
<a href="https://1drv.ms/b/s!AibJ2eLVK8FvbfUtSHxMK5qSI1M" target="_blank">
<table align="left" style="padding-right: 28px; padding-bottom:10px; border-width: 0px; height:20px; background-color: rgb(255, 255, 255); border-spacing: 0px">
<tbody>
<tr valign="top">
<td style="padding: 0px;">
<div style="background-color: rgb(255, 255, 255); height: 20px; width: 20px; max-height: 20px;">
<a href="https://1drv.ms/b/s!AibJ2eLVK8FvbfUtSHxMK5qSI1M" target="_blank"><img width="20" style="border:0px;" src="https://r1.res.office365.com/owa/prem/images/dc-pdf_20.png"></a></div></td><td><div id="OwaReferenceAttachmentFileName2" style="padding: 0px 0px 0px 5px; font-size: 14px; font-family: 'Segoe UI', 'Segoe WP', 'Segoe UI WPC', Tahoma, Arial, sans-serif; color: rgb(0, 114, 198);"><a href="https://1drv.ms/b/s!AibJ2eLVK8FvbfUtSHxMK5qSI1M" target="_blank" style="text-decoration: none; margin: 0px; font-size: 14px; font-family: 'Segoe UI', 'Segoe WP', 'Segoe UI WPC', Tahoma, Arial, sans-serif; color: rgb(0, 114, 198);">Философия Жизни 2020.pdf</a></div></td><td style="display:none;visibility:hidden;" width="0" height="0"><a href="cid:07eec89f-1443-42f5-a685-0b7c5912f6bb">Философия Жизни 2020.pdf</a></td></tr></tbody></table></a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<div id="OwaReferenceAttachmentsEnd" style="display:none;visibility:hidden;"></div>
<!--[endif]-->
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">body1</div>
</body>
</html>
import {
amendGmailNodes,
amendOutlookNodes,
cleanupGMailElementAttributes,
cleanupOutlookElementAttributes,
pruneGmailElement,
pruneOutlookElement,
} from "./normalizationStrategies";
import { HTMLNormalizer, PlainNormalizer } from "@vereign/mime-normalizer";
import { DOM } from "@vereign/dom";
import MailParser, { fixNewLines } from "../MailParser/MailParser";
import CryptoService from "../CryptoService";
import { EMAIL_VENDORS } from "../StatusesService";
import MimeVerificationService from "./index";
import RKAUtility from "../../utils/rka";
import { arrayBufferToBase64 } from "../../utils/common";
import VerificationError from "../VerificationService/VerificationError";
import {
removeSpacesAndLinebreaks,
removeQRCodes,
} from "../../utils/stringUtils";
import {
cleanupHtmlNodeAttributes,
printHtmlChildren,
printOutlookElement,
pruneHtmlNode,
} from "./utils";
const vendorAmendingFunctions = {
[EMAIL_VENDORS.GMAIL]: amendGmailNodes,
[EMAIL_VENDORS.OUTLOOK]: amendOutlookNodes,
};
const vendorPruningFunctions = {
[EMAIL_VENDORS.GMAIL]: pruneGmailElement,
[EMAIL_VENDORS.OUTLOOK]: pruneOutlookElement,
};
const vendorAttributesCleanupFunctions = {
[EMAIL_VENDORS.GMAIL]: cleanupGMailElementAttributes,
[EMAIL_VENDORS.OUTLOOK]: cleanupOutlookElementAttributes,
};
const vendorPrintingFunctions = {
[EMAIL_VENDORS.OUTLOOK]: printOutlookElement,
};
// Load JSDOM dynamically for Node env only, because built CRA is crashing with it
let JSDOM;
const loadJSDOM = async () => {
if (!JSDOM) {
JSDOM = (await import("jsdom")).JSDOM;
}
};
typeof window === "undefined" && loadJSDOM();
const prepareMimeForPartsVerification = async (
mimeString: string,
......@@ -104,14 +59,9 @@ const getNormalizedMimeParts = (
plainPart,
} = MimeVerificationService.getMimeHtmlAndPlainParts(mimeString);
const normalizedHtmlPart = MimeVerificationService.normalizeVendorHtml(
htmlPart,
senderSystem
);
const normalizedHtmlPart = normalizeVendorHtml(htmlPart, senderSystem);
const normalizedPlainPart = MimeVerificationService.normalizePlainPart(
plainPart
);
const normalizedPlainPart = normalizePlainPart(plainPart);
return {
htmlPart: normalizedHtmlPart,
......@@ -180,58 +130,19 @@ const preparePartsForSigning = async (
};
const normalizeVendorHtml = (htmlString: string, vendor: string): string => {
let document: HTMLDocument;
let document;
if (typeof DOMParser !== "undefined") {
const parser = new DOMParser();
document = parser.parseFromString(htmlString, "text/html");
} else {
const { window } = new JSDOM(htmlString);
const { window } = new DOM(htmlString);
document = window.document;
}
const mimeBody = document.body;
const amendNodesFunction = vendorAmendingFunctions[vendor];
if (amendNodesFunction) {
amendNodesFunction(document);
}
/**
* Remove unnecessary nodes
*/
const elementPruningFunction = vendorPruningFunctions[vendor];
if (!elementPruningFunction) {
throw new Error(
`Vendor "${vendor}" is not supported. Please, develop a pruning function for it.`
);
}
pruneHtmlNode(document, elementPruningFunction);
/**
* Cleanup unnecessary attributes of nodes
*/
const elementAttributesCleanupFunction =
vendorAttributesCleanupFunctions[vendor];
if (elementAttributesCleanupFunction) {
cleanupHtmlNodeAttributes(document, elementAttributesCleanupFunction);
}
/**
* Print nodes
*/
const vendorPrintFunction = vendorPrintingFunctions[vendor];
return printHtmlChildren(mimeBody, vendorPrintFunction, 0);
return HTMLNormalizer.normalizeVendorHtml(document, vendor);
};
const normalizePlainPart = (text: string): string => {
text = removeSpacesAndLinebreaks(text);
return removeQRCodes(text);
};
const normalizePlainPart = PlainNormalizer.normalizePlain;
export default {
normalizeVendorHtml,
......
export const ELEMENT_NODE = 1;
export const TEXT_NODE = 3;
export const DOCUMENT_NODE = 9;
const DUMMY_QR_CODE_ID = "dummyQrCode";
const elementsToRemove = { br: true, hr: true };
const attributesToKeep = {
alt: true,
src: true,
cite: true,
data: true,
datetime: true,
href: true,
value: true,
};
const isDummyQrCode = (element: HTMLElement): boolean => {
if (element.id === DUMMY_QR_CODE_ID) {
return true;
}
};
const pruneElement = (element: HTMLElement): boolean => {
if (isDummyQrCode(element)) {
return true;
}
return !!elementsToRemove[element.nodeName.toLowerCase()];
};
export const pruneGmailElement = (element: HTMLElement): boolean => {
return pruneElement(element);
};
/**
* Returns true if element should be completely removed
* @param element
*/
export const pruneOutlookElement = (element: HTMLElement): boolean => {
if (pruneElement(element)) {
return true;
}
// Remove Outlook generic <o:*> tags
return !!element.nodeName.toLowerCase().startsWith("o:");
};
const cloneAnchorFromPane = (a: HTMLAnchorElement, pane: HTMLElement) => {
try {
const url = new URL(a.href);
// If this is external url
if (url.host && url.protocol) {
pane.parentNode.insertBefore(a.cloneNode(false), pane);
}
} catch {
return;
}
};
export const amendOutlookNodes = (document: HTMLDocument): void => {
/**
* Get rid of attachments panes
*/
const attachmentsPanesConatiner = document.getElementById(
"OwaReferenceAttachments"
);
const attachmentsPanesContainerEnd = document.getElementById(
"OwaReferenceAttachmentsEnd"
);
if (attachmentsPanesConatiner) {
const as = attachmentsPanesConatiner.getElementsByTagName("a");
Array.from(as).forEach((a) => {
cloneAnchorFromPane(a, attachmentsPanesConatiner as HTMLElement);
});
attachmentsPanesConatiner.parentNode.removeChild(attachmentsPanesConatiner);
}
attachmentsPanesContainerEnd &&
attachmentsPanesContainerEnd.parentNode.removeChild(
attachmentsPanesContainerEnd
);
/**
* Unwind spans, because sometimes Outlook wraps everything into span after sending
*/
const spans = document.getElementsByTagName("span");
/**
* Sort spans by depth to start unwinding the deepest ones, which does not contain nested spans
*/
const spansDepths: { depth?: Array<Node> } = {};
Array.from(spans).forEach((span: Node) => {
let descendant = span;
let parent = descendant.parentNode;
let depth = 0;
while (parent && descendant !== parent) {
descendant = parent;
parent = descendant.parentNode;
depth++;
}
if (!spansDepths[depth]) {
spansDepths[depth] = [];
}
spansDepths[depth].push(span);
});
Object.keys(spansDepths)
.sort((a, b) => parseInt(b) - parseInt(a))
.forEach((depth) => {
spansDepths[depth].forEach((span) => {
let child = span.firstChild;
const parent = span.parentNode;
while (child) {
parent.insertBefore(child.cloneNode(true), span);
child = child.nextSibling;
}
span.parentNode.removeChild(span);
});
});
};
export const amendGmailNodes = (document: HTMLDocument): void => {
/**
* Look for attachments panes and remove everything but liks
*/
const attachmentsPanes = Array.from(
document.getElementsByClassName("gmail_chip")
);
attachmentsPanes.forEach((pane) => {
const as = pane.querySelectorAll("a");
as.forEach((a) => {
cloneAnchorFromPane(a, pane as HTMLElement);
});
});
attachmentsPanes.forEach((pane) => {
pane.parentNode.removeChild(pane);
});
};
export const cleanupGMailElementAttributes = (element: HTMLElement): void => {
if (element.attributes.length > 0) {
for (const attribute of element.attributes) {
if (attribute.name === "data-surl") {
element.setAttribute("src", attribute.value);
}
}
for (let i = 0; i < element.attributes.length; i++) {
const attribute = element.attributes[i];
if (!attributesToKeep[attribute.name]) {
element.removeAttribute(attribute.name);
i--;
}
}
}
};
export const cleanupOutlookElementAttributes = (element: HTMLElement): void => {
if (element.attributes.length > 0) {
for (const attribute of element.attributes) {
let valueSplit = attribute.value.split(" ");
valueSplit = valueSplit.map((value) =>
value.startsWith("x_") ? value.replace("x_", "") : value
);
element.setAttribute(attribute.name, valueSplit.join(" "));
}
for (let i = 0; i < element.attributes.length; i++) {
const attribute = element.attributes[i];
if (!attributesToKeep[attribute.name]) {
element.removeAttribute(attribute.name);
i--;
}
}
}
};
import { removeSpacesAndLinebreaks } from "../../utils/stringUtils";
import { escapeHtmlString } from "../..";
import { DOCUMENT_NODE, ELEMENT_NODE, TEXT_NODE } from "./constants";
export const printHtmlChildren = (
node: Node,
printFunction: (node: Node) => string,
depth: number
): string => {
let child = node.firstChild;
if (!child) {
return "";
}
if (child == node.lastChild && child.nodeType == TEXT_NODE) {
return printHtmlNode(child, printFunction, depth);
} else {
let result = "";
while (child) {
result = result.concat(printHtmlNode(child, printFunction, depth));
child = child.nextSibling;
}
return result;
}
};
export const printHtmlNode = (
node: Node,
printFunction: (node: Node) => string,
depth: number
): string => {
let result = "";
if (printFunction) {
const customPrintout = printFunction(node);
if (customPrintout) {
return customPrintout;
}
}
switch (node.nodeType) {
case TEXT_NODE: {
const text = removeSpacesAndLinebreaks(node.textContent);
if (text.length) {
result += "<TEXT>";
result += text;
result += "</TEXT>";
result += "\n";
}
break;
}
case DOCUMENT_NODE:
result += printHtmlChildren(node, printFunction, depth);
break;
case ELEMENT_NODE:
result += "<" + node.nodeName;
Array.from((node as HTMLElement).attributes)
.sort((a, b) => a.name.localeCompare(b.name))
.forEach((attribute) => {
result += ` ${attribute.name}`;
if (attribute.value) {
result += `="${escapeHtmlString(attribute.value)}"`;
}
});
if (node.firstChild) {
result += ">";
result += "\n";
result += printHtmlChildren(node, printFunction, depth + 1);
result += "</" + node.nodeName + ">";
} else {
result += "/>";
}
result += "\n";
break;
}
return result;
};
export const cleanupHtmlNodeAttributes = (
node: Node,
cleanupElementAttributes: (element: HTMLElement) => void
): void => {
if (node.nodeType === node.ELEMENT_NODE) {
cleanupElementAttributes(node as HTMLElement);
}
let child = node.firstChild;
while (child) {
cleanupHtmlNodeAttributes(child as HTMLElement, cleanupElementAttributes);
child = child.nextSibling;
}
};
export const pruneHtmlNode = (
node: Node,
pruneElement: (element: HTMLElement) => boolean
): boolean => {
let toBeRemoved = false;
switch (node.nodeType) {
case node.COMMENT_NODE:
case node.DOCUMENT_TYPE_NODE:
toBeRemoved = true;
break;
case node.TEXT_NODE: {
const trimmedText = node.textContent.trim();
if (trimmedText === "") {
toBeRemoved = true;
} else {
node.textContent = trimmedText;
}
break;
}
case node.ELEMENT_NODE:
toBeRemoved = pruneElement(node as HTMLElement);
}
if (toBeRemoved) {
return true;
}
const childrenToRemove = [];
let child = node.firstChild;
while (child) {
pruneHtmlNode(child, pruneElement) && childrenToRemove.push(child);
child = child.nextSibling;
}
childrenToRemove.forEach((child) => node.removeChild(child));
return false;
};
// TODO: Move this logic to amendOutlookNodes
export const printOutlookElement = (node: Node): string => {
if (node.nodeType === ELEMENT_NODE) {
if ((node as HTMLElement).classList.contains("WordSection1")) {
return printHtmlChildren(node, null, 0);
}
}
};
......@@ -1241,7 +1241,7 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
"@types/parse5@*":
"@types/parse5@*", "@types/parse5@^5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109"
integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==
......@@ -1333,6 +1333,17 @@
dependencies:
eslint-visitor-keys "^1.1.0"
"@vereign/dom@git+ssh://git@code.vereign.com:code/js-toolbox/gsdom.git":
version "1.0.0"
resolved "git+ssh://git@code.vereign.com:code/js-toolbox/gsdom.git#7482f7790813e81324e059ff2195fdf7c190f8f1"
dependencies:
"@types/parse5" "^5.0.3"
parse5 "^6.0.1"
"@vereign/mime-normalizer@git+ssh://git@code.vereign.com:code/js-toolbox/mime-normalizer.git":
version "1.0.0"
resolved "git+ssh://git@code.vereign.com:code/js-toolbox/mime-normalizer.git#65871ff761c9e51b526b00c9ef5d69cf80af5546"
abab@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.4.tgz#6dfa57b417ca06d21b2478f0e638302f99c2405c"
......@@ -4026,6 +4037,11 @@ parse5@5.1.1:
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
parse5@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
pascalcase@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment