13 |
45 |
46 |
103 |
122 |
141 |
161 |
174 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/src/scripts/background.js:
--------------------------------------------------------------------------------
1 | /* Initialize settings */
2 | browser.storage.local.get("whois").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"whois":"https://whois.com/whois/"})}})
3 | browser.storage.local.get("server").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"server":"https://api.justreport.it/lookup/"})}})
4 | browser.storage.local.get("api-key").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"api-key":"DeVKFVPs3T40XYrUeQl9z5adMwopbYnY8jaAbecw"})}})
5 | browser.storage.local.get("mode").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"mode":"registrar"})}})
6 | browser.storage.local.get("spamcop").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"spamcop":""})}})
7 | browser.storage.local.get("action").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"action":"leave"})}})
8 | browser.storage.local.get("custom").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"custom":[]})}})
9 | browser.storage.local.get("trim").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"trim":true})}})
10 | browser.storage.local.get("extension").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"extension":"eml"})}})
11 | browser.storage.local.get("messageSource").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"messageSource":"locales"})}})
12 | browser.storage.local.get("customTitle").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"customTitle":""})}})
13 | browser.storage.local.get("customBody").then((item) => {if (Object.entries(item).length==0){browser.storage.local.set({"customBody":""})}})
14 |
15 |
16 |
17 | browser.messageDisplayAction.onClicked.addListener((tab) =>{
18 | browser.messageDisplay.getDisplayedMessage(tab.id).then((message) => {
19 | browser.messages.getFull(message.id).then((parsed) => {
20 | browser.messages.getRaw(message.id).then((raw) => {
21 | browser.storage.local.get("mode").then((configuration) => {
22 | var sender = parsed.headers['return-path'][0] || message.author;
23 | var spamDomain = extractSpamDomain(sender);
24 | var to = [];
25 | if (configuration.mode == "registrar" || configuration.mode == "all"){
26 | getAbuseEmail(spamDomain).then((x) => x().then((y) => y().then((response) => {
27 | if (configuration.mode == "all"){
28 | getSpamcopEmail().then((email) => {
29 | getCustomEmail().then((custom) => {
30 | to.push.apply(to, custom);
31 | to.push(email);
32 | if (response.status == "success") {
33 | to.push(response.data.email);
34 | composeEmailBasic(to, spamDomain, raw, message);
35 | }
36 | else
37 | composeEmailBasic(to, spamDomain, raw, message).then(() => {
38 | createPopup(spamDomain).then((popup) => popup());
39 | });
40 | })
41 | });
42 | }
43 | else {
44 | if (response.status == "success") {
45 | to.push(response.data.email);
46 | composeEmailBasic(to, spamDomain, raw, message);
47 | }
48 | else
49 | composeEmailBasic(to, spamDomain, raw, message).then(() => {
50 | createPopup(spamDomain).then((popup) => popup());
51 | });
52 | }
53 | })));
54 | }
55 | else if (configuration.mode == "spamcop_and_custom") {
56 | getSpamcopEmail().then((email) => {
57 | getCustomEmail().then((custom) => {
58 | to.push.apply(to, custom);
59 | to.push(email);
60 | composeEmailBasic(to, spamDomain, raw, message).then(() => {
61 | createPopup(spamDomain).then((popup) => popup());
62 | });
63 | })
64 | });
65 | }
66 | else if (configuration.mode == "custom")
67 | getCustomEmail().then((custom) => {
68 | composeEmailBasic(custom, spamDomain, raw, message);
69 | });
70 | else
71 | getSpamcopEmail().then((email) => {
72 | to.push(email);
73 | composeEmailBasic(to, spamDomain, raw, message);
74 | });
75 | performAction(message.id);
76 | });
77 | });
78 | });
79 | });
80 | });
81 |
82 | browser.menus.create({
83 | "title": "Report It",
84 | "visible": true
85 | });
86 |
87 | browser.menus.onClicked.addListener((info, tab) => {
88 | if ('selectedMessages' in info){
89 | processSelectedMessage([], info.selectedMessages.messages, 0);
90 | }
91 | });
92 |
93 | function processSelectedMessage(files, messages, index){
94 | if (index == messages.length) {
95 | browser.storage.local.get("mode").then((configuration) => {
96 | if (configuration.mode == "custom")
97 | getCustomEmail().then((custom) => {
98 | composeEmailSelected(custom, files, messages[0]);
99 | });
100 | else
101 | getSpamcopEmail().then((email) => {
102 | composeEmailSelected([email], files, messages[0]);
103 | });
104 | });
105 | } else {
106 | browser.messages.getRaw(messages[index].id).then((raw) => {
107 | getTrimmedFile(new Blob(convertRawToUint8Array(raw), { type: 'message/rfc822' })).then((file) => {
108 | files.push({file: file});
109 | performAction(messages[index].id);
110 | processSelectedMessage(files, messages, index+1);
111 | })
112 | });
113 | }
114 | }
115 |
116 | function convertRawToUint8Array(raw) {
117 | // Reason: https://thunderbird.topicbox.com/groups/addons/T06356567165277ee-M25e96f2d58e961d6167ad348
118 | let bytes = new Array(raw.length);
119 | for (let i = 0; i < bytes.length; i++) {
120 | bytes[i] = raw.charCodeAt(i) & 0xFF;
121 | }
122 | return [new Uint8Array(bytes)];
123 | }
124 |
125 | async function getTrimmedFile(tmpFile) {
126 | let item = await browser.storage.local.get("trim")
127 | let extensionSetting = await browser.storage.local.get("extension")
128 |
129 | let extension = extensionSetting.extension || 'eml';
130 | let fileName = `message.${extension}`;
131 |
132 | let blob = new Blob([tmpFile], { type: 'message/rfc822' });
133 |
134 | if (item.trim) {
135 | blob = blob.slice(0, 50 * 1024);
136 | }
137 |
138 | return new File([blob], fileName, { type: 'message/rfc822' });
139 | }
140 |
141 |
142 | async function performAction(messageId){
143 | browser.storage.local.get("action").then((item) => {
144 | // Mark all messages as junk
145 | browser.messages.update(messageId, {
146 | "junk": true
147 | });
148 | if (item.action == "move"){
149 | browser.messages.delete([messageId], false);
150 | }
151 | else if (item.action == "delete") {
152 | browser.messages.delete([messageId], true);
153 | }
154 | });
155 | }
156 |
157 | function getSpamcopEmail(){
158 | return browser.storage.local.get("spamcop").then((item) => {
159 | return item.spamcop;
160 | });
161 | }
162 |
163 | function getCustomEmail(){
164 | return browser.storage.local.get("custom").then((item) => {
165 | return item.custom;
166 | });
167 | }
168 |
169 |
170 | async function getMessageContent() {
171 | let { messageSource } = await browser.storage.local.get("messageSource");
172 |
173 | if (messageSource === "locales") {
174 | return {
175 | subject: browser.i18n.getMessage("background.subject.basic"),
176 | body: browser.i18n.getMessage("background.body.basic")
177 | };
178 | } else if (messageSource === "english") {
179 | return {
180 | subject: "Spam Abuse from",
181 | body: "The attached message was marked as spam as it is sending unsolicited emails. Please take appropriate action.\n\n--"
182 | };
183 | } else if (messageSource === "custom") {
184 | let { customTitle, customBody } = await browser.storage.local.get(["customTitle", "customBody"]);
185 | return {
186 | subject: customTitle,
187 | body: customBody
188 | };
189 | }
190 | }
191 |
192 | // Update composeEmailBasic and composeEmailSelected to use getMessageContent
193 | async function composeEmailBasic(to, domain, raw, message) {
194 | let file = await getTrimmedFile(new Blob(convertRawToUint8Array(raw), { type: 'message/rfc822' }));
195 | let identity = await getIdentity(message);
196 | let messageContent = await getMessageContent();
197 |
198 | let composeDetails = {
199 | to: to,
200 | subject: messageContent.subject + ": " + domain,
201 | plainTextBody: messageContent.body,
202 | attachments: [{ file: file }]
203 | };
204 |
205 | if (identity !== null) {
206 | composeDetails.identityId = identity.id;
207 | }
208 |
209 | await browser.compose.beginNew(composeDetails);
210 | }
211 |
212 | // Similar changes for composeEmailSelected
213 |
214 | async function composeEmailSelected(to, files, message) {
215 | let identity = await getIdentity(message);
216 | let messageContent = await getMessageContent();
217 |
218 | let composeDetails = {
219 | to: to,
220 | subject: messageContent.subject + " [" + files.length + "]",
221 | plainTextBody: messageContent.body,
222 | attachments: files
223 | };
224 |
225 | if (identity !== null) {
226 | composeDetails.identityId = identity.id;
227 | }
228 |
229 | await browser.compose.beginNew(composeDetails);
230 | }
231 |
232 | function createPopup(domain){
233 | return browser.storage.local.get("whois").then((item) => async function(){
234 | if (item.whois != "") {
235 | var window = await messenger.windows.create({
236 | url: item.whois + domain,
237 | type: "popup"
238 | });
239 | await setFocused(window);
240 | }
241 | });
242 | }
243 |
244 | async function setFocused(window){
245 | await messenger.windows.update(window.id, {
246 | focused: true
247 | });
248 | }
249 |
250 | function extractSpamDomain(author){
251 | if (author.includes("<"))
252 | return author.split("<")[1].split(">")[0].split("@")[1];
253 | else
254 | return author.split("@")[1];
255 | }
256 |
257 | function getAbuseEmail(domain){
258 | return browser.storage.local.get("server").then((server) => async function(){
259 | return await browser.storage.local.get("api-key").then((key) => async function(){
260 | var url = server["server"] + domain;
261 | var headers = {
262 | "Content-Type": "application/json",
263 | "x-api-key": key["api-key"]
264 | }
265 | var fetchInfo = {
266 | mode: "cors",
267 | method: "GET",
268 | headers: headers
269 | };
270 | var response = await fetch(url, fetchInfo);
271 | return await response.json();
272 | })
273 | });
274 | }
275 |
276 | async function getIdentity(message) {
277 | let accounts = await browser.accounts.list();
278 | let account = accounts.find(account => account.id === message.folder.accountId);
279 | if (!account || account.identities.length === 0) {
280 | return null;
281 | }
282 | return account.identities[0];
283 | }
--------------------------------------------------------------------------------