diff --git a/js/bootstrap.js b/js/bootstrap.js index d6693d1..7356ee8 100644 --- a/js/bootstrap.js +++ b/js/bootstrap.js @@ -8,36 +8,57 @@ // } // }); -browser.messageDisplayAction.onClicked.addListener((tabId) => { - if (MailHops.isLoaded) { - browser.messageDisplayAction.setPopup({popup: "content/mailhops_details.xhtml"}); - browser.messageDisplayAction.openPopup(); +// Keep track of MailHops instances per tab. +let tabHops = new Map(); + +browser.messageDisplayAction.onClicked.addListener((tab) => { + let mailHop = tabHops.get(tab.id); + if (mailHop.isLoaded) { + browser.messageDisplayAction.setPopup({ + tabId: tab.id, + popup: `content/mailhops_details.xhtml?tabId=${tab.id}` + }); + browser.messageDisplayAction.openPopup(); } }); -messenger.messageDisplay.onMessageDisplayed.addListener((tabId, message) => { - - messenger.messages.getFull(message.id).then((messagePart) => { - // get route - MailHops.init(message.id, messagePart.headers); - }); - -}); +async function initMessageTab(tab, message) { + let mailHop = new MailHops(); + let messagePart = await messenger.messages.getFull(message.id); + await mailHop.init(tab.id, message.id, messagePart.headers); + tabHops.set(tab.id, mailHop); +} +messenger.messageDisplay.onMessageDisplayed.addListener(initMessageTab); var port; function connected(p) { port = p; - port.onMessage.addListener(function(m) { + port.onMessage.addListener(function (m) { switch (m.command) { case 'details': + let mailHop = tabHops.get(m.tabId); port.postMessage({ "cmd": m.command, - "message": MailHops.message, - "response": MailHops.response, - "options": MailHops.options + "message": mailHop.message, + "response": mailHop.response, + "options": mailHop.options }); - break; + break; } }); } -browser.runtime.onConnect.addListener(connected); \ No newline at end of file +browser.runtime.onConnect.addListener(connected); + + +// Update all messages currently displayed. +async function updateAllCurrentMessages() { + let tabs = await browser.tabs.query({}) + let messageTabs = tabs.filter(tab => ["mail", "messageDisplay"].includes(tab.type)); + for (let messageTab of messageTabs) { + let message = await browser.messageDisplay.getDisplayedMessage(messageTab.id); + if (message) { + await initMessageTab(messageTab, message); + } + } +} +updateAllCurrentMessages(); diff --git a/js/experiment_apis.js b/js/experiment_apis.js deleted file mode 100644 index ac77999..0000000 --- a/js/experiment_apis.js +++ /dev/null @@ -1,82 +0,0 @@ -var { ExtensionCommon } = ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm"); -var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const ehb = "expandedHeadersBottomBox"; -const eh2 = "expandedHeaders2"; -const win = Services.wm.getMostRecentWindow("mail:3pane"); -const win2 = Services.wm.getMostRecentWindow("mail:messageWindow"); - -const mailHopsUI = class extends ExtensionCommon.ExtensionAPI { - getAPI(context) { - context.callOnClose(this); - return { - mailHopsUI: { - insert(wd, basePath, iconPath, iconText, id, target) { - let elm = wd.document.getElementById(target); - const compact = "compact"; - let iconSize = 48, marginTop = 0; - if (wd.document.getElementById(eh2).getAttribute(compact) == compact) { - iconSize = 32, marginTop = -16; - } - let mailHops = wd.document.getElementById(id); - if (mailHops) { - mailHops.setAttribute("image", basePath + iconPath); - mailHops.setAttribute("tooltiptext", iconText); - mailHops.setAttribute("style", "flex-shrink: 0; padding: 0; margin:" + marginTop + "px 2px 0 2px"); - for (let mhc of mailHops.children) { - if (mhc.nodeName == "image") { - mhc.width = iconSize; - mhc.height = iconSize; - mhc.setAttribute("style", "margin: 0 2px 0 2px"); - break; - } - } - } else { - let mailHops = wd.document.createXULElement("toolbarbutton"); - mailHops.id = id; - mailHops.setAttribute("image", basePath + iconPath); - mailHops.setAttribute("tooltiptext", iconText); - mailHops.setAttribute("style", "flex-shrink: 0; padding: 0; margin:" + marginTop + "px 2px 0 2px"); - mailHops.addEventListener("click", () => { - wd.document.getElementById("mailhops-messageDisplayAction-toolbarbutton").click(); - }, false); - wd.document.getElementById(ehb).insertBefore(mailHops, elm); - for (let mhc of mailHops.children) { - if (mhc.nodeName == "image") { - mhc.width = iconSize; - mhc.height = iconSize; - mhc.setAttribute("style", "margin: 0 2px 0 2px"); - break; - } - } - } - }, - mv(wd, id, target) { - let mailHops = wd.document.getElementById(id); - let elm = wd.document.getElementById(target); - wd.document.getElementById(ehb).insertBefore(mailHops, elm); - }, - rm(wd, id) { - if (wd.document.getElementById(id)) wd.document.getElementById(id).remove(); - }, - async insertBefore(basePath, iconPath, iconText, id, target) { - this.insert(win, basePath, iconPath, iconText, id, target); - if (win2) { - this.insert(win2, basePath, iconPath, iconText, id, target); - } - }, - async move(id, target) { - this.mv(win, id, target); - if (win2) this.mv(win2, id, target); - }, - async remove(id) { - this.rm(win, id); - if (win2) this.rm(win2, id); - } - } - } - } - close() { - let id = "countryIcon"; - if (win.document.getElementById(id)) win.document.getElementById(id).remove(); - } -}; \ No newline at end of file diff --git a/js/mailhops.js b/js/mailhops.js index 2208dd7..3125fd2 100644 --- a/js/mailhops.js +++ b/js/mailhops.js @@ -4,32 +4,32 @@ * @website: http://Mailhops.com */ -const MailHops = { - msgURI: null, - isLoaded: false, - loading: false, - previousId: null, - options: { - version: 'MailHops Plugin 4.3.3', +class MailHops { + msgURI = null + isLoaded = false + loading = false + tabId = null + options = { + version: 'MailHops Plugin 4.3.3', api_key: '', owm_key: '', - lang: 'en', + lang: 'en', unit: 'mi', theme: 'light', - api_http: 'https://', - api_host: 'api.Mailhops.com', - debug: false, - travel_time_junk: false, - country_filter: [] - }, - message: { + api_http: 'https://', + api_host: 'api.Mailhops.com', + debug: false, + travel_time_junk: false, + country_filter: [] + } + message = { id: null , map_url: '' , time: null , date: new Date().toISOString() , hash: '' , secure: [] - , headers: [] + , headers: [] , auth: [] , sender: { icon: '/images/refresh.png' @@ -37,444 +37,427 @@ const MailHops = { , description: '' }, error: '' - }, - response: {}, -}; - -MailHops.LOG = function(msg) { - if(!MailHops.options.debug) - return; - console.log(msg); -}; - -MailHops.init = function(id, headers) -{ - // prevent multiple loading - if (id == MailHops.previousId) return; - previousId = id; - - var getting = browser.storage.local.get(); - getting.then(data => { - if (data.api_key) { - MailHops.options.api_key = data.api_key; - } - if (data.owm_key) { - MailHops.options.owm_key = data.owm_key; - } - if (data.lang) { - MailHops.options.lang = data.lang; - } - if (data.unit) { - MailHops.options.unit = data.unit; - } - if (data.theme) { - MailHops.options.theme = data.theme; - } - if (data.travel_time_junk && data.travel_time_junk != 'off') { - MailHops.options.travel_time_junk = Boolean(data.travel_time_junk); - } - if (data.debug) { - MailHops.options.debug = Boolean(data.debug); - } - if (data.countries) { - MailHops.options.country_filter = data.countries.split(','); - } - MailHops.LOG('load MailHops prefs'); - // reset message - MailHops.message = { - id: id - , map_url: '' - , time: null - , date: new Date().toISOString() - , hash: '' - , secure: [] - , headers: headers - , auth: [] - , sender: { - icon: '/images/refresh.png' - , title: 'Loading...' - , description: '' - }, - error: '' - }; - MailHops.getRoute(); - }, (error) => { - MailHops.LOG('Error loading MailHops prefs'); - MailHops.loading = false; - }); - -}; - -MailHops.getRoute = async function () { - if (MailHops.loading) return; - - MailHops.loading = true; - // set loading icon - browser.messageDisplayAction.setPopup({ popup: '' }); - browser.messageDisplayAction.setIcon({ path: '/images/refresh.png' }); - browser.messageDisplayAction.setTitle({ title: 'Loading...' }); - if(browser.mailHopsUI) - browser.mailHopsUI.insertBefore("", '/images/refresh.png', '', "countryIcon", "expandedHeaders2"); - - //IP regex - var regexIp=/(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)(\/(?:[012]\d?|3[012]?|[456789])){0,1}$/; - var regexAllIp = /(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)(\/(?:[012]\d?|3[012]?|[456789])){0,1}/g; - var regexIPV6 = /s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*/g; - - var headReceived = MailHops.message.headers['received'] || []; - var headDate = MailHops.message.headers['date'] ? MailHops.message.headers['date'][0] : ''; - var headXReceived = MailHops.message.headers['x-received'] ? MailHops.message.headers['x-received'][0] : ''; - var headXOrigIP = MailHops.message.headers['x-originating-ip'] ? MailHops.message.headers['x-originating-ip'][0] : ''; - // auth box - var headXMailer = MailHops.message.headers['x-mailer'] ? MailHops.message.headers['x-mailer'][0] : ''; - var headUserAgent = MailHops.message.headers['user-agent'] ? MailHops.message.headers['user-agent'][0] : ''; - var headXMimeOLE = MailHops.message.headers['x-mimeole'] ? MailHops.message.headers['x-mimeole'][0] : ''; - var headReceivedSPF = MailHops.message.headers['received-spf'] ? MailHops.message.headers['received-spf'][0] : ''; - var headAuth = MailHops.message.headers['authentication-results'] ? MailHops.message.headers['authentication-results'][0] : ''; - var headListUnsubscribe = MailHops.message.headers['list-unsubscribe'] ? MailHops.message.headers['list-unsubscribe'][0] : ''; - - var all_ips = new Array(); - var rline = ''; - var firstDate = headDate; - var lastDate; - //empty secure and time - MailHops.message.secure = []; - MailHops.message.time = null; - try { - MailHops.message.date = new Date(headDate).toISOString(); - } catch (error) { - headDate = headDate.substring(0, headDate.lastIndexOf(' ')); } - try { - MailHops.message.date = new Date(headDate).toISOString(); - } catch (error) { - headDate = new Date(); - } - - MailHops.message.auth = MailHops.auth( headXMailer, headUserAgent, headXMimeOLE, headAuth, headReceivedSPF, headListUnsubscribe ); + response = {} - //loop through the received headers and parse for IP addresses - if (Boolean(headReceived)){ - var received_ips = new Array(); - for( var h=0; h < headReceived.length; h++ ) { - //build the received line by concat until semi-colon ; date/time - rline += headReceived[h]; - if(headReceived[h].indexOf(';') === -1) - continue; - // first and last dates are used to calculate time traveled - if(rline.indexOf(';') !== -1){ - if(!firstDate) - firstDate = rline.substring(rline.indexOf(';')+1).trim(); - if(!lastDate) - lastDate = rline.substring(rline.indexOf(';')+1).trim(); - } - - // IPV6 check - rline = rline.replace(/\[IPv6\:/g,'['); - if(rline.match(regexIPV6)){ - all_ips.unshift( rline.match(regexIPV6)[0] ); - //reset the line - rline=''; - continue; - } - // parse IPs out of Received line - received_ips = rline.match(regexAllIp); - //continue if no IPs found - if(!received_ips){ - //reset the line - rline=''; - continue; - } - //get unique IPs for each Received header - received_ips = received_ips.filter(function(item, pos) { - return received_ips.indexOf(item) === pos; - }); - for( var r=received_ips.length; r >= 0 ; r-- ){ - if(regexIp.test(received_ips[r]) && MailHops.testIP(received_ips[r],rline)){ - all_ips.unshift( received_ips[r] ); - } - } - //reset the line - rline=''; - } + LOG(msg) { + if (!this.options.debug) + return; + console.log(msg); } - // parse dates - if(firstDate && firstDate.indexOf('(') !==- 1) - firstDate = firstDate.substring(0,firstDate.indexOf('(')).trim(); - if(lastDate && lastDate.indexOf('(') !== -1) - lastDate = lastDate.substring(0,lastDate.indexOf('(')).trim(); - if(firstDate && lastDate){ + async init(tabId, id, headers) { + this.tabId = tabId; try { - firstDate = new Date(firstDate); - lastDate = new Date(lastDate); - MailHops.message.time = lastDate - firstDate; - } catch(e){ - MailHops.LOG('travel dates parse Error: '+JSON.stringify(e)); - MailHops.message.time = null; + var data = await browser.storage.local.get(); + if (data.api_key) { + this.options.api_key = data.api_key; + } + if (data.owm_key) { + this.options.owm_key = data.owm_key; + } + if (data.lang) { + this.options.lang = data.lang; + } + if (data.unit) { + this.options.unit = data.unit; + } + if (data.theme) { + this.options.theme = data.theme; + } + if (data.travel_time_junk && data.travel_time_junk != 'off') { + this.options.travel_time_junk = Boolean(data.travel_time_junk); + } + if (data.debug) { + this.options.debug = Boolean(data.debug); + } + if (data.countries) { + this.options.country_filter = data.countries.split(','); + } + this.LOG('load MailHops prefs'); + // reset message + this.message = { + id: id + , map_url: '' + , time: null + , date: new Date().toISOString() + , hash: '' + , secure: [] + , headers: headers + , auth: [] + , sender: { + icon: '/images/refresh.png' + , title: 'Loading...' + , description: '' + }, + error: '' + }; + this.getRoute(); + } catch (e) { + this.LOG('Error loading MailHops prefs'); } - } else { - MailHops.message.time = null; } - //get the originating IP address - if(Boolean(headXOrigIP)){ - headXOrigIP = headXOrigIP.replace(/\[IPv6\:/g,'['); - //IPV6 check - if(headXOrigIP.match(regexIPV6)){ - var ip = headXOrigIP.match(regexIPV6) - if(Boolean(ip) && ip.length && all_ips.indexOf(ip[0])==-1) - all_ips.unshift( ip[0] ); - } else { - var ip = headXOrigIP.match(regexAllIp); - if(Boolean(ip) && ip.length && all_ips.indexOf(ip[0])==-1) - all_ips.unshift( ip[0] ); + async getRoute() { + if (this.loading) return; + + this.loading = true; + // set loading icon + browser.messageDisplayAction.setPopup({ popup: '', tabId: this.tabId }); + browser.messageDisplayAction.setIcon({ path: '/images/refresh.png', tabId: this.tabId }); + browser.messageDisplayAction.setTitle({ title: 'Loading...', tabId: this.tabId }); + + //IP regex + var regexIp = /(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)(\/(?:[012]\d?|3[012]?|[456789])){0,1}$/; + var regexAllIp = /(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)(\/(?:[012]\d?|3[012]?|[456789])){0,1}/g; + var regexIPV6 = /s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*/g; + + var headReceived = this.message.headers['received'] || []; + var headDate = this.message.headers['date'] ? this.message.headers['date'][0] : ''; + var headXReceived = this.message.headers['x-received'] ? this.message.headers['x-received'][0] : ''; + var headXOrigIP = this.message.headers['x-originating-ip'] ? this.message.headers['x-originating-ip'][0] : ''; + // auth box + var headXMailer = this.message.headers['x-mailer'] ? this.message.headers['x-mailer'][0] : ''; + var headUserAgent = this.message.headers['user-agent'] ? this.message.headers['user-agent'][0] : ''; + var headXMimeOLE = this.message.headers['x-mimeole'] ? this.message.headers['x-mimeole'][0] : ''; + var headReceivedSPF = this.message.headers['received-spf'] ? this.message.headers['received-spf'][0] : ''; + var headAuth = this.message.headers['authentication-results'] ? this.message.headers['authentication-results'][0] : ''; + var headListUnsubscribe = this.message.headers['list-unsubscribe'] ? this.message.headers['list-unsubscribe'][0] : ''; + + var all_ips = new Array(); + var rline = ''; + var firstDate = headDate; + var lastDate; + //empty secure and time + this.message.secure = []; + this.message.time = null; + try { + this.message.date = new Date(headDate).toISOString(); + } catch (error) { + headDate = headDate.substring(0, headDate.lastIndexOf(' ')); } - } - if (all_ips.length) { - // set the message hash - MailHops.message.hash = btoa(MailHops.message.date + '' + all_ips.join(',')); - const cached = await MailHops.getCacheResponse(); - if (cached) { - MailHops.displayRoute(cached); - MailHops.isLoaded = true; - MailHops.loading = false; - } else { - MailHops.lookupRoute( all_ips.join(',') ); + try { + this.message.date = new Date(headDate).toISOString(); + } catch (error) { + headDate = new Date(); } - } else { - MailHops.clear(); - } -}; -//another ip check, dates will throw off the regex -MailHops.testIP = function(ip,header){ - var validIP = true; + this.message.auth = this.auth(headXMailer, headUserAgent, headXMimeOLE, headAuth, headReceivedSPF, headListUnsubscribe); - try { - var firstchar = header.substring(header.indexOf(ip)-1); - firstchar = firstchar.substring(0,1); - var lastchar = header.substring((header.indexOf(ip)+ip.length)); - lastchar = lastchar.substring(0,1); + //loop through the received headers and parse for IP addresses + if (Boolean(headReceived)) { + var received_ips = new Array(); + for (var h = 0; h < headReceived.length; h++) { + //build the received line by concat until semi-colon ; date/time + rline += headReceived[h]; + if (headReceived[h].indexOf(';') === -1) + continue; + // first and last dates are used to calculate time traveled + if (rline.indexOf(';') !== -1) { + if (!firstDate) + firstDate = rline.substring(rline.indexOf(';') + 1).trim(); + if (!lastDate) + lastDate = rline.substring(rline.indexOf(';') + 1).trim(); + } - if(firstchar.match(/\.|\d|\-/) + // IPV6 check + rline = rline.replace(/\[IPv6\:/g, '['); + if (rline.match(regexIPV6)) { + all_ips.unshift(rline.match(regexIPV6)[0]); + //reset the line + rline = ''; + continue; + } + // parse IPs out of Received line + received_ips = rline.match(regexAllIp); + //continue if no IPs found + if (!received_ips) { + //reset the line + rline = ''; + continue; + } + //get unique IPs for each Received header + received_ips = received_ips.filter(function (item, pos) { + return received_ips.indexOf(item) === pos; + }); + for (var r = received_ips.length; r >= 0; r--) { + if (regexIp.test(received_ips[r]) && this.testIP(received_ips[r], rline)) { + all_ips.unshift(received_ips[r]); + } + } + //reset the line + rline = ''; + } + } + + // parse dates + if (firstDate && firstDate.indexOf('(') !== - 1) + firstDate = firstDate.substring(0, firstDate.indexOf('(')).trim(); + if (lastDate && lastDate.indexOf('(') !== -1) + lastDate = lastDate.substring(0, lastDate.indexOf('(')).trim(); + if (firstDate && lastDate) { + try { + firstDate = new Date(firstDate); + lastDate = new Date(lastDate); + this.message.time = lastDate - firstDate; + } catch (e) { + this.LOG('travel dates parse Error: ' + JSON.stringify(e)); + this.message.time = null; + } + } else { + this.message.time = null; + } + + //get the originating IP address + if (Boolean(headXOrigIP)) { + headXOrigIP = headXOrigIP.replace(/\[IPv6\:/g, '['); + //IPV6 check + if (headXOrigIP.match(regexIPV6)) { + var ip = headXOrigIP.match(regexIPV6) + if (Boolean(ip) && ip.length && all_ips.indexOf(ip[0]) == -1) + all_ips.unshift(ip[0]); + } else { + var ip = headXOrigIP.match(regexAllIp); + if (Boolean(ip) && ip.length && all_ips.indexOf(ip[0]) == -1) + all_ips.unshift(ip[0]); + } + } + if (all_ips.length) { + // set the message hash + this.message.hash = btoa(this.message.date + '' + all_ips.join(',')); + const cached = await this.getCacheResponse(); + if (cached) { + this.displayRoute(cached); + this.isLoaded = true; + this.loading = false; + } else { + this.lookupRoute(all_ips.join(',')); + } + } else { + this.clear(); + } + }; + + //another ip check, dates will throw off the regex + testIP(ip, header) { + var validIP = true; + + try { + var firstchar = header.substring(header.indexOf(ip) - 1); + firstchar = firstchar.substring(0, 1); + var lastchar = header.substring((header.indexOf(ip) + ip.length)); + lastchar = lastchar.substring(0, 1); + + if (firstchar.match(/\.|\d|\-/) || lastchar.match(/\.|\d|\-/) - || ( firstchar == '?' && lastchar == '?' ) + || (firstchar == '?' && lastchar == '?') || (firstchar == ':' || lastchar == ':') || lastchar == ';' - || header.toLowerCase().indexOf(' id '+ip) !== -1 - || parseInt(ip.substring(0,ip.indexOf('.'))) >= 240 //IANA-RESERVED - ){ - //only if there is one instance of this IP - if(header.indexOf(ip) == header.lastIndexOf(ip)) - validIP = false; - } else if(header.indexOf('using SSL') !== -1 + || header.toLowerCase().indexOf(' id ' + ip) !== -1 + || parseInt(ip.substring(0, ip.indexOf('.'))) >= 240 //IANA-RESERVED + ) { + //only if there is one instance of this IP + if (header.indexOf(ip) == header.lastIndexOf(ip)) + validIP = false; + } else if (header.indexOf('using SSL') !== -1 || header.indexOf('using TLS') !== -1 || header.indexOf('version=TLSv1/SSLv3') !== -1 - ){ + ) { //check if this IP was part of a secure transmission - MailHops.message.secure.push(ip); - } - } catch(e) { - MailHops.LOG('testIP Error: '+JSON.stringify(e)); - } - return validIP; -}; - -MailHops.clear = function () { - MailHops.message.sender = { - title: 'Local', - countryCode: '', - icon: '/images/local.png' - }; - browser.messageDisplayAction.setIcon({ path: MailHops.message.sender.icon }); - browser.messageDisplayAction.setTitle({ title: MailHops.message.sender.title }); - if (browser.mailHopsUI) { - browser.mailHopsUI.insertBefore("", MailHops.message.sender.icon, MailHops.message.sender.title, "countryIcon", "expandedHeaders2"); - } - MailHops.isLoaded = true; - MailHops.loading = false; -} - -MailHops.error = function (status, data) { - MailHops.message.error = (data && data.error && data.error.message) ? data && data.error.message : 'Service Unavailable'; - MailHops.message.sender = { - title: (data && data.error && data.error.message) ? data && data.error.message : 'Service Unavailable', - countryCode: '', - icon: '/images/auth/error.png' - }; - browser.messageDisplayAction.setIcon({ path: MailHops.message.sender.icon }); - browser.messageDisplayAction.setTitle({ title: MailHops.message.sender.title }); - if (browser.mailHopsUI) - browser.mailHopsUI.insertBefore("", MailHops.message.sender.icon, MailHops.message.sender.title, "countryIcon", "expandedHeaders2"); -} - -MailHops.auth = function (header_xmailer, header_useragent, header_xmimeole, header_auth, header_spf, header_unsubscribe) { - let auth = []; - //SPF - if(header_spf){ - header_spf = header_spf.replace(/^\s+/, ""); - var headerSPFArr=header_spf.split(' '); - auth.push({ - type: 'SPF', - color: 'green', - icon: '/images/auth/' + headerSPFArr[0] + '.png', - copy: header_spf + '\n' + MailHopsUtils.spf(headerSPFArr[0]).trim() - }); - } - //Authentication-Results - //http://tools.ietf.org/html/rfc5451 - if(header_auth){ - var headerAuthArr=header_auth.split(';'); - var dkim_result; - var spf_result; - for(var h=0;h', '').trim() - }); - } - return auth; -} - -//mailhops lookup -MailHops.lookupRoute = function(header_route){ - - let lookupURL = '?'+MailHopsUtils.getAPIUrlParams(MailHops.options)+'&r='+String(header_route)+'&l='+MailHops.options.lang+'&u='+MailHops.options.unit; - - if(MailHops.options.owm_key != '') - lookupURL += '&owm_key='+MailHops.options.owm_key; - if(MailHops.message.time != null) - lookupURL += '&t=' + MailHops.message.time; - if(MailHops.message.date != null) - lookupURL += '&d='+MailHops.message.date; - - MailHops.message.map_url = MailHopsUtils.getAPIUrl() + '/map/' + lookupURL; - -//call mailhops api for lookup -var xmlhttp = new XMLHttpRequest(); - xmlhttp.open("GET", MailHopsUtils.getAPIUrl() + '/lookup/' + lookupURL ,true); - xmlhttp.onreadystatechange=function() { - if (xmlhttp.readyState===4){ - try { - let data = JSON.parse(xmlhttp.responseText); - if (xmlhttp.status === 200) { - MailHops.cacheResponse(data.response); - MailHops.displayRoute(data.response); - //tag the result - MailHops.tagResults(data, data.response.route); - } else if(data.error){ - MailHops.LOG(JSON.stringify(data.error)); - //display the error - MailHops.error(xmlhttp.status, data); - } - } catch(e){ - MailHops.LOG(e); - MailHops.error(); - } - } - MailHops.isLoaded = true; - MailHops.loading = false; - }; - xmlhttp.send(null); -}; - -MailHops.displayRoute = function (response) { - MailHops.response = response; - MailHops.message.sender = MailHopsUtils.getSender(response.route); - - if (MailHops.message.sender) { - browser.messageDisplayAction.setIcon({ path: MailHops.message.sender.icon }); - browser.messageDisplayAction.setTitle({ title: MailHops.message.sender.title }); - if (browser.mailHopsUI) - browser.mailHopsUI.insertBefore("", MailHops.message.sender.icon, MailHops.message.sender.title, "countryIcon", "expandedHeaders2"); - } else { - browser.messageDisplayAction.setIcon({ path: '/images/local.png' }); - browser.messageDisplayAction.setTitle({ title: 'Local' }); - if (browser.mailHopsUI) - browser.mailHopsUI.insertBefore("", '/images/local.png', 'Local', "countryIcon", "expandedHeaders2"); - } -}; - -// keep a daily cache -MailHops.cacheResponse = async function (response) { - let data = await browser.storage.local.get('messages'); - let cached_date = new Date(); - let messages = { - cached: cached_date.getUTCFullYear()+'-'+cached_date.getUTCMonth()+'-'+cached_date.getUTCDate(), - list: [] - }; - if (data.messages && data.messages.list && data.messages.cached === messages.cached) { - messages.list = data.messages.list; - } - messages.list[MailHops.message.hash] = response; - await browser.storage.local.set({messages: messages}); - MailHops.LOG('Cached Message ' + MailHops.message.id + ' hash ' + MailHops.message.hash); -}; - -// get cached message -MailHops.getCacheResponse = async function () { - let data = await browser.storage.local.get('messages'); - if (data.messages && data.messages.list[MailHops.message.hash]) { - MailHops.LOG('Found Cached Message '+MailHops.message.hash); - return data.messages.list[MailHops.message.hash]; - } - return false; -}; - -MailHops.tagResults = function(results, route){ - - if (!results) { - return false; - } - - //Add junk tag on messages taking too long to travel - try { - if(Boolean(MailHops.options.travel_time_junk) && MailHops.message.time != null && MailHops.message.time > 10000){ - messenger.messages.update(MailHops.message.id, { 'junk': true }); - MailHops.LOG( "Junk: Travel time match for " + MailHops.message.time ); - } - if (MailHops.options.country_filter.length && MailHops.message.sender && MailHops.message.sender.countryCode) { - if (MailHops.options.country_filter.indexOf(MailHops.message.sender.countryCode.toUpperCase()) != -1) { - messenger.messages.update(MailHops.message.id, { 'junk': true }); - MailHops.LOG( "Junk: Country code match for " + MailHops.message.sender.countryCode ); + //Authentication-Results + //http://tools.ietf.org/html/rfc5451 + if (header_auth) { + var headerAuthArr = header_auth.split(';'); + var dkim_result; + var spf_result; + for (var h = 0; h < headerAuthArr.length; h++) { + if (headerAuthArr[h].indexOf('dkim=') != -1) { + dkim_result = headerAuthArr[h]; + if (header_spf) + break; + } + if (!header_spf && headerAuthArr[h].indexOf('spf=') != -1) { + spf_result = headerAuthArr[h]; + if (dkim_result) + break; + } + } + if (dkim_result) { + dkim_result = dkim_result.replace(/^\s+/, ""); + var dkimArr = dkim_result.split(' '); + auth.push({ + type: 'DKIM', + color: 'green', + icon: '/images/auth/' + dkimArr[0].replace('dkim=', '') + '.png', + copy: dkim_result + '\n' + MailHopsUtils.dkim(dkimArr[0].replace('dkim=', '')).trim() + }); + } + if (spf_result) { + spf_result = spf_result.replace(/^\s+/, ""); + var spfArr = spf_result.split(' '); + auth.push({ + type: 'SPF', + color: 'green', + icon: '/images/auth/' + spfArr[0].replace('spf=', '') + '.png', + copy: spf_result + '\n' + MailHopsUtils.spf(spfArr[0].replace('spf=', '')).trim() + }); } } - } catch(e){ - MailHops.LOG("Error tagResults: " + e); + if (header_unsubscribe) { + auth.push({ + type: 'Unsubscribe', + color: 'grey', + link: header_unsubscribe.replace('<', '').replace('>', '').trim() + }); + } + return auth; } -}; \ No newline at end of file + + //mailhops lookup + lookupRoute(header_route) { + let mailHop = this; + + let lookupURL = '?' + MailHopsUtils.getAPIUrlParams(this.options) + '&r=' + String(header_route) + '&l=' + this.options.lang + '&u=' + this.options.unit; + + if (this.options.owm_key != '') + lookupURL += '&owm_key=' + this.options.owm_key; + if (this.message.time != null) + lookupURL += '&t=' + this.message.time; + if (this.message.date != null) + lookupURL += '&d=' + this.message.date; + + this.message.map_url = MailHopsUtils.getAPIUrl() + '/map/' + lookupURL; + + //call mailhops api for lookup + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open("GET", MailHopsUtils.getAPIUrl() + '/lookup/' + lookupURL, true); + xmlhttp.onreadystatechange = function () { + if (xmlhttp.readyState === 4) { + try { + let data = JSON.parse(xmlhttp.responseText); + if (xmlhttp.status === 200) { + mailHop.cacheResponse(data.response); + mailHop.displayRoute(data.response); + //tag the result + mailHop.tagResults(data, data.response.route); + } else if (data.error) { + mailHop.LOG(JSON.stringify(data.error)); + //display the error + mailHop.error(xmlhttp.status, data); + } + } catch (e) { + mailHop.LOG(e); + mailHop.error(); + } + } + mailHop.isLoaded = true; + mailHop.loading = false; + }; + xmlhttp.send(null); + } + + displayRoute(response) { + this.response = response; + this.message.sender = MailHopsUtils.getSender(response.route); + + if (this.message.sender) { + browser.messageDisplayAction.setIcon({ path: this.message.sender.icon, tabId: this.tabId }); + browser.messageDisplayAction.setTitle({ title: this.message.sender.title, tabId: this.tabId }); + } else { + browser.messageDisplayAction.setIcon({ path: '/images/local.png', tabId: this.tabId }); + browser.messageDisplayAction.setTitle({ title: 'Local', tabId: this.tabId }); + } + } + + // keep a daily cache + async cacheResponse(response) { + let data = await browser.storage.local.get('messages'); + let cached_date = new Date(); + let messages = { + cached: cached_date.getUTCFullYear() + '-' + cached_date.getUTCMonth() + '-' + cached_date.getUTCDate(), + list: [] + }; + if (data.messages && data.messages.list && data.messages.cached === messages.cached) { + messages.list = data.messages.list; + } + messages.list[this.message.hash] = response; + await browser.storage.local.set({ messages: messages }); + this.LOG('Cached Message ' + this.message.id + ' hash ' + this.message.hash); + } + + // get cached message + async getCacheResponse() { + let data = await browser.storage.local.get('messages'); + if (data.messages && data.messages.list[this.message.hash]) { + this.LOG('Found Cached Message ' + this.message.hash); + return data.messages.list[this.message.hash]; + } + return false; + }; + + tagResults(results, route) { + if (!results) { + return false; + } + + //Add junk tag on messages taking too long to travel + try { + if (Boolean(this.options.travel_time_junk) && this.message.time != null && this.message.time > 10000) { + messenger.messages.update(this.message.id, { 'junk': true }); + this.LOG("Junk: Travel time match for " + this.message.time); + } + if (this.options.country_filter.length && this.message.sender && this.message.sender.countryCode) { + if (this.options.country_filter.indexOf(this.message.sender.countryCode.toUpperCase()) != -1) { + messenger.messages.update(this.message.id, { 'junk': true }); + this.LOG("Junk: Country code match for " + this.message.sender.countryCode); + } + } + } catch (e) { + this.LOG("Error tagResults: " + e); + } + } +} diff --git a/js/mailhops_details.js b/js/mailhops_details.js index e8b0673..07c2819 100644 --- a/js/mailhops_details.js +++ b/js/mailhops_details.js @@ -1,5 +1,9 @@ +const queryString = window.location.search; +const urlParams = new URLSearchParams(queryString); +const tabId = parseInt(urlParams.get("tabId"), 10); + var port = browser.runtime.connect({ name: "MailHops" }); -port.postMessage({ command: "details" }); +port.postMessage({ command: "details", tabId }); port.onMessage.addListener(function (msg) { updateContent(msg); @@ -161,4 +165,4 @@ function updateContent(msg, noauth) { if(!noauth) updateContent(msg, true); } -} \ No newline at end of file +} diff --git a/manifest.json b/manifest.json index 822091a..934c33c 100644 --- a/manifest.json +++ b/manifest.json @@ -11,24 +11,13 @@ "applications": { "gecko": { "id": "thunderbird@mailhops.com", - "strict_min_version": "74.0", - "strict_max_version": "102.*" + "strict_min_version": "112.0" } }, "default_locale": "en", "icons": { "32": "images/mailhops32.png" }, - "experiment_apis": { - "mailHopsUI": { - "schema": "schema.json", - "parent": { - "scopes": ["addon_parent"], - "paths": [["mailHopsUI"]], - "script": "js/experiment_apis.js" - } - } - }, "options_ui": { "page": "content/preferences.html", "open_in_tab": true @@ -48,4 +37,4 @@ "js/bootstrap.js" ] } -} \ No newline at end of file +} diff --git a/schema.json b/schema.json deleted file mode 100644 index da0700e..0000000 --- a/schema.json +++ /dev/null @@ -1,61 +0,0 @@ -[ - { - "namespace": "mailHops", - "functions": [ - { - "name": "insertBefore", - "type": "function", - "async": true, - "parameters": [ - { - "name": "basePath", - "type": "string" - }, - { - "name": "iconPath", - "type": "string", - "description": "path of icon." - }, - { - "name": "iconText", - "type": "string" - }, - { - "name": "id", - "type": "string" - }, - { - "name": "target", - "type": "string" - } - ] - }, - { - "name": "move", - "type": "function", - "async": true, - "parameters": [ - { - "name": "id", - "type": "string" - }, - { - "name": "target", - "type": "string" - } - ] - }, - { - "name": "remove", - "type": "function", - "async": true, - "parameters": [ - { - "name": "id", - "type": "string" - } - ] - } - ] - } -]