mirror of
https://github.com/MailHops/mailhops-plugin.git
synced 2025-05-16 06:10:08 -07:00
496 lines
17 KiB
JavaScript
496 lines
17 KiB
JavaScript
/*
|
|
* @author: Andrew Van Tassel
|
|
* @email: andrew@andrewvantassel.com
|
|
* @website: http://mailhops.com
|
|
*/
|
|
|
|
var mailHops =
|
|
{
|
|
msgURI: null,
|
|
isLoaded: false,
|
|
options: {
|
|
'version':'MailHops Plugin 3.1.1',
|
|
'lan':'en',
|
|
'unit':'mi',
|
|
'api_http':'https://',
|
|
'api_host':'api.mailhops.com',
|
|
'debug':false,
|
|
'hide_compact':false,
|
|
'bar_color': '#5E7A9B',
|
|
'font_color': '#ffffff',
|
|
'font_size': '14px',
|
|
'country_tag':false,
|
|
'travel_time_junk':false,
|
|
'country_filter':[]
|
|
},
|
|
message: {
|
|
secure:[]
|
|
,time: null
|
|
}
|
|
};
|
|
|
|
mailHops.LOG = function(msg) {
|
|
if(!mailHops.options.debug)
|
|
return;
|
|
var consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
|
|
consoleService.logStringMessage('MailHops: '+msg);
|
|
};
|
|
|
|
mailHops.init = function() {
|
|
//import nativeJSON
|
|
var nativeJSON = Components.classes["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON);
|
|
|
|
//load preferences
|
|
mailHops.loadPref();
|
|
|
|
document.getElementById("mailhopsLogo").addEventListener("click", function () {
|
|
window.openDialog("chrome://mailhops/content/preferences.xul","","chrome, dialog, modal, centerscreen").focus();
|
|
});
|
|
|
|
document.getElementById("mailhopsDataPaneRefresh").addEventListener("click", function () {
|
|
mailHops.refreshCache();
|
|
});
|
|
|
|
mailHops.isLoaded = true;
|
|
|
|
};
|
|
|
|
mailHops.loadPref = function(reload)
|
|
{
|
|
mailHops.LOG('load MailHops prefs');
|
|
//get preferences
|
|
mailHops.options.lan = mailHops.getCharPref('mail.mailHops.lang','en');
|
|
mailHops.options.unit = mailHops.getCharPref('mail.mailHops.unit','mi');
|
|
mailHops.options.fkey = mailHops.getCharPref('mail.mailHops.fkey','');//forecast.io api_key
|
|
|
|
//Display
|
|
mailHops.options.bar_color = mailHops.getCharPref('mail.mailHops.bar_color','#5E7A9B');
|
|
|
|
mailHops.options.font_color = mailHops.getCharPref('mail.mailHops.font_color','#ffffff');
|
|
|
|
mailHops.options.font_size = mailHops.getCharPref('mail.mailHops.font_size','14px');
|
|
|
|
mailHops.options.debug = mailHops.getCharPref('mail.mailHops.debug','false')=='true'?true:false;
|
|
|
|
mailHops.options.hide_compact = mailHops.getCharPref('mail.mailHops.hide_compact','false')=='true'?true:false;
|
|
|
|
mailHops.options.api_host = mailHops.getCharPref('mail.mailHops.api_host','api.mailhops.com');
|
|
|
|
mailHops.options.api_http = mailHops.getCharPref('mail.mailHops.api_http','https://');
|
|
|
|
mailHops.options.api_key = mailHops.getCharPref('mail.mailHops.api_key','');
|
|
|
|
mailHops.options.map_provider = mailHops.getCharPref('mail.mailHops.map_provider','OpenStreetMap.Mapnik');
|
|
|
|
mailHops.options.country_tag = mailHops.getCharPref('mail.mailHops.country_tag','false')=='true'?true:false;
|
|
|
|
mailHops.options.travel_time_junk = mailHops.getCharPref('mail.mailHops.travel_time_junk','false')=='true'?true:false;
|
|
|
|
mailHops.options.country_filter = mailHops.getCharPref('mail.mailHops.country_filter',[]);
|
|
|
|
//init display
|
|
mailHopsDisplay.init( mailHops.options, reload );
|
|
};
|
|
|
|
mailHops.StreamListener =
|
|
{
|
|
content: "" ,
|
|
found: false ,
|
|
onDataAvailable: function ( request , context , inputStream , offset , count )
|
|
{
|
|
try {
|
|
var sis = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance ( Components.interfaces.nsIScriptableInputStream ) ;
|
|
sis.init( inputStream ) ;
|
|
|
|
if( !this.found )
|
|
{
|
|
this.content += sis.read ( count ) ;
|
|
this.content = this.content.replace ( /\r/g , "" ) ;
|
|
var pos = this.content.indexOf ( "\n\n" ) ;
|
|
|
|
if ( pos > -1 )
|
|
{
|
|
// last header line must end with LF -> pos+1 !!!
|
|
this.content = this.content.substr ( 0 , pos + 1 ) ;
|
|
this.found = true ;
|
|
}
|
|
}
|
|
} catch(e) {
|
|
//failed to read input stream
|
|
mailHops.LOG('StreamListener Error: '+JSON.stringify(e));
|
|
}
|
|
},
|
|
onStartRequest: function ( request , context )
|
|
{
|
|
this.content = "" ;
|
|
this.found = false ;
|
|
},
|
|
onStopRequest: function ( aRequest , aContext , aStatusCode )
|
|
{
|
|
mailHops.headers = Components.classes["@mozilla.org/messenger/mimeheaders;1"].createInstance ( Components.interfaces.nsIMimeHeaders ) ;
|
|
mailHops.headers.initialize ( this.content , this.content.length ) ;
|
|
mailHops.getRoute() ;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* loop through the header, find out if we have received-from headers
|
|
*/
|
|
mailHops.loadHeaderData = function() {
|
|
|
|
if(!!mailHops.options.hide_compact){
|
|
// CompactHeader toggle header logic
|
|
var compactHeadersView = document.getElementById('CompactHeader_collapsedHeaderView');
|
|
if(!!compactHeadersView){
|
|
if(compactHeadersView.collapsed===true){
|
|
mailHopsDisplay.toggleMailHopsBar(true);
|
|
} else if(compactHeadersView.collapsed===false){
|
|
mailHopsDisplay.toggleMailHopsBar(false);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
var msgURI = null ;
|
|
|
|
if ( gDBView ){
|
|
msgURI = gDBView.URIForFirstSelectedMessage;
|
|
}
|
|
if ( msgURI == null ){
|
|
return;
|
|
}
|
|
mailHops.msgURI = msgURI;
|
|
var messenger = Components.classes["@mozilla.org/messenger;1"].createInstance ( Components.interfaces.nsIMessenger ) ;
|
|
var msgService = messenger.messageServiceFromURI ( msgURI ) ;
|
|
msgService.CopyMessage ( msgURI , mailHops.StreamListener , false , null , msgWindow , {} ) ;
|
|
};
|
|
|
|
mailHops.getRoute = function(){
|
|
//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;
|
|
|
|
// TODO test IPV6 regex for Received headers, currently only used for X-Originating-IP
|
|
// IPv6 addresses including compressed and IPv4-embedded variants (RFC 2373)
|
|
// http://regexlib.com/REDetails.aspx?regexp_id=2919
|
|
var regexIPV6 = /(::|(([a-fA-F0-9]{1,4}):){7}(([a-fA-F0-9]{1,4}))|(:(:([a-fA-F0-9]{1,4})){1,6})|((([a-fA-F0-9]{1,4}):){1,6}:)|((([a-fA-F0-9]{1,4}):)(:([a-fA-F0-9]{1,4})){1,6})|((([a-fA-F0-9]{1,4}):){2}(:([a-fA-F0-9]{1,4})){1,5})|((([a-fA-F0-9]{1,4}):){3}(:([a-fA-F0-9]{1,4})){1,4})|((([a-fA-F0-9]{1,4}):){4}(:([a-fA-F0-9]{1,4})){1,3})|((([a-fA-F0-9]{1,4}):){5}(:([a-fA-F0-9]{1,4})){1,2}))/;
|
|
|
|
var headReceived = mailHops.headers.extractHeader ( "Received" , true );
|
|
var headDate = mailHops.headers.extractHeader ( "Date" , true );
|
|
var headXReceived = mailHops.headers.extractHeader ( "X-Received" , false );
|
|
var headXOrigIP = mailHops.headers.extractHeader ( "X-Originating-IP" , false );
|
|
// auth box
|
|
var headXMailer = mailHops.headers.extractHeader ( "X-Mailer" , false );
|
|
var headUserAgent = mailHops.headers.extractHeader ( "User-Agent" , false );
|
|
var headXMimeOLE = mailHops.headers.extractHeader ( "X-MimeOLE" , false );
|
|
var headReceivedSPF = mailHops.headers.extractHeader ( "Received-SPF" , false );
|
|
var headAuth = mailHops.headers.extractHeader ( "Authentication-Results" , false );
|
|
var headListUnsubscribe = mailHops.headers.extractHeader ( "List-Unsubscribe" , false ) ;
|
|
|
|
var all_ips = new Array();
|
|
var rline = '',firstDate=headDate,lastDate;
|
|
//empty secure and time
|
|
mailHops.message.secure = [];
|
|
mailHops.message.time = null;
|
|
|
|
mailHopsDisplay.lists( headListUnsubscribe );
|
|
|
|
mailHopsDisplay.auth( headXMailer, headUserAgent, headXMimeOLE, headAuth, headReceivedSPF );
|
|
|
|
//loop through the received headers and parse for IP addresses
|
|
if (!!headReceived){
|
|
var received_ips = new Array();
|
|
var headReceivedArr = headReceived.split('\n');
|
|
for( var h=0; h < headReceivedArr.length; h++ ) {
|
|
//build the received line by concat until semi-colon ; date/time
|
|
rline += headReceivedArr[h];
|
|
if(headReceivedArr[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();
|
|
}
|
|
|
|
// parse IPs out of Received line
|
|
received_ips = rline.match(regexAllIp);
|
|
//continue if no IPs found
|
|
if(!received_ips)
|
|
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='';
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
mailHops.message.time = lastDate - firstDate;
|
|
} catch(e){
|
|
mailHops.LOG('travel dates parse Error: '+JSON.stringify(e));
|
|
mailHops.message.time = null;
|
|
}
|
|
} else {
|
|
mailHops.message.time = null;
|
|
}
|
|
|
|
//get the originating IP address
|
|
if(!!headXOrigIP){
|
|
//remove brackets
|
|
headXOrigIP = headXOrigIP.replace('[','').replace(']','');
|
|
//IPV6 check
|
|
if(headXOrigIP.indexOf(':') !== -1 && headXOrigIP.match(regexIPV6)){
|
|
all_ips.unshift( headXOrigIP );
|
|
} else {
|
|
var ip = headXOrigIP.match(regexAllIp);
|
|
if(!!ip && ip.length && all_ips.indexOf(ip[0])==-1)
|
|
all_ips.unshift( ip[0] );
|
|
}
|
|
}
|
|
|
|
if ( all_ips.length ){
|
|
mailHops.lookupRoute ( all_ips ) ;
|
|
} else {
|
|
mailHopsDisplay.clear( true );
|
|
}
|
|
};
|
|
//another ip check, dates will throw off the regex
|
|
mailHops.testIP = function(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 == '?' )
|
|
|| 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 {
|
|
//check if this IP was part of a secure transmission
|
|
if(header.indexOf('using SSL') != -1){
|
|
if(header.substring(header.indexOf('using SSL')+11,header.indexOf('using SSL')+12) == '.')
|
|
mailHops.message.secure.push(ip+':'+header.substring(header.indexOf('using SSL'),header.indexOf('using TLS')+14));
|
|
else
|
|
mailHops.message.secure.push(ip+':'+header.substring(header.indexOf('using SSL'),header.indexOf('using TLS')+11));
|
|
}
|
|
else if(header.indexOf('using TLS') != -1){
|
|
if(header.substring(header.indexOf('using TLS')+11,header.indexOf('using TLS')+12) == '.')
|
|
mailHops.message.secure.push(ip+':'+header.substring(header.indexOf('using TLS'),header.indexOf('using TLS')+14));
|
|
else
|
|
mailHops.message.secure.push(ip+':'+header.substring(header.indexOf('using TLS'),header.indexOf('using TLS')+11));
|
|
}
|
|
else if(header.indexOf('version=TLSv1/SSLv3') != -1)
|
|
mailHops.message.secure.push(ip+':'+'using TLSv1/SSLv3');
|
|
}
|
|
} catch(e) {
|
|
mailHops.LOG('testIP Error: '+JSON.stringify(e));
|
|
}
|
|
return validIP;
|
|
};
|
|
|
|
mailHops.setupEventListener = function(){
|
|
if ( mailHops.isLoaded ){
|
|
return ;
|
|
}
|
|
|
|
mailHops.init();
|
|
mailHops.registerObserver();
|
|
|
|
var listener = {
|
|
onStartHeaders: function() { mailHopsDisplay.clear(); }
|
|
, onEndHeaders: mailHops.loadHeaderData
|
|
};
|
|
gMessageListeners.push( listener );
|
|
};
|
|
|
|
//preferences observers
|
|
mailHops.registerObserver = function(){
|
|
var prefService = Components.classes["@mozilla.org/preferences-service;1"].getService( Components.interfaces.nsIPrefService ) ;
|
|
mailHops._branch = prefService.getBranch( "mail.mailHops." ) ;
|
|
mailHops._branch.QueryInterface( Components.interfaces.nsIPrefBranchInternal ) ;
|
|
mailHops._branch.addObserver( "" , mailHops , false ) ;
|
|
};
|
|
|
|
mailHops.unregisterObserver = function(){
|
|
if ( !mailHops._branch ){
|
|
return ;
|
|
}
|
|
|
|
mailHops._branch.removeObserver ( "" , mailHops ) ;
|
|
};
|
|
|
|
mailHops.observe = function ( aSubject , aTopic , aData )
|
|
{
|
|
if ( aTopic == "nsPref:changed" )
|
|
mailHops.loadPref(true);
|
|
};
|
|
|
|
mailHops.getCharPref = function ( strName , strDefault ){
|
|
var value;
|
|
if (!pref){
|
|
var pref = Components.classes["@mozilla.org/preferences-service;1"].getService( Components.interfaces.nsIPrefBranch ) ;
|
|
}
|
|
try {
|
|
value = pref.getCharPref ( strName ) ;
|
|
} catch(e){
|
|
value = strDefault ;
|
|
}
|
|
return ( value ) ;
|
|
};
|
|
|
|
//mailhops lookup
|
|
mailHops.lookupRoute = function(header_route){
|
|
|
|
//setup loading
|
|
mailHopsDisplay.clear();
|
|
|
|
var lookupURL = mailHopsUtils.getAPIUrl(mailHops.options)+'/lookup/?'+mailHopsUtils.getAPIUrlParams(mailHops.options)+'&r='+String(header_route)+'&l='+mailHops.options.lan+'&u='+mailHops.options.unit;
|
|
|
|
if(mailHops.options.fkey != '')
|
|
lookupURL += '&fkey='+mailHops.options.fkey;
|
|
if(mailHops.message.time != null)
|
|
lookupURL += '&t='+mailHops.message.time;
|
|
|
|
//check for cache
|
|
var cached_results = mailHops.getResults();
|
|
|
|
if(cached_results){
|
|
mailHops.LOG('Found Cached Result');
|
|
try {
|
|
cached_results = JSON.parse(cached_results);
|
|
mailHopsDisplay.route(header_route, mailHops.message, cached_results.response, cached_results.meta, lookupURL);
|
|
return;
|
|
} catch(e){
|
|
mailHops.LOG('Failed to parse cached result: '+JSON.stringify(e));
|
|
}
|
|
}
|
|
|
|
mailHops.LOG(lookupURL);
|
|
|
|
//call mailhops api for lookup
|
|
var xmlhttp = new XMLHttpRequest();
|
|
|
|
xmlhttp.open("GET", lookupURL ,true);
|
|
xmlhttp.onreadystatechange=function() {
|
|
if (xmlhttp.readyState===4){
|
|
try {
|
|
var data = JSON.parse(xmlhttp.responseText);
|
|
if(xmlhttp.status===200){
|
|
var d = new Date();
|
|
data.meta.cached = d.toISOString();
|
|
//save the result
|
|
mailHops.saveResults(JSON.stringify(data),data.response.route);
|
|
//display the result
|
|
mailHopsDisplay.route(header_route, mailHops.message, data.response, data.meta, lookupURL);
|
|
} else if(data.error){
|
|
mailHops.LOG(JSON.stringify(data));
|
|
//display the error
|
|
mailHopsDisplay.error(xmlhttp.status,data);
|
|
}
|
|
} catch(e){
|
|
mailHops.LOG(e);
|
|
mailHopsDisplay.error();
|
|
}
|
|
}
|
|
};
|
|
xmlhttp.send(null);
|
|
};
|
|
|
|
mailHops.saveResults = function(results,route){
|
|
|
|
if(!mailHops.msgURI)
|
|
return false;
|
|
|
|
var messenger = Components.classes["@mozilla.org/messenger;1"].createInstance().QueryInterface(Components.interfaces.nsIMessenger);
|
|
var msgHdr = messenger.messageServiceFromURI(mailHops.msgURI).messageURIToMsgHdr(mailHops.msgURI);
|
|
|
|
if(!msgHdr)
|
|
return false;
|
|
|
|
msgHdr.setStringProperty( "MH-Route", results );
|
|
|
|
//Add tag
|
|
if(!!route && !!mailHops.options.api_key){
|
|
try {
|
|
var countryCode = mailHopsUtils.getOriginatingCountryCode(route);
|
|
var msg = Components.classes["@mozilla.org/array;1"].createInstance(Components.interfaces.nsIMutableArray);
|
|
msg.clear();
|
|
msg.appendElement(msgHdr, false);
|
|
|
|
if(!!mailHops.options.country_tag){
|
|
var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"].getService(Components.interfaces.nsIMsgTagService);
|
|
if(!tagService)
|
|
return;
|
|
|
|
if(!tagService.getKeyForTag(countryCode))
|
|
tagService.addTag(countryCode,'',0);
|
|
|
|
msgHdr.folder.addKeywordsToMessages(msg, countryCode );
|
|
mailHops.LOG( "Added CountryCode tag: "+countryCode );
|
|
}
|
|
|
|
if(!!mailHops.options.country_filter && mailHops.options.country_filter.length){
|
|
if(mailHops.options.country_filter.indexOf(countryCode.toLowerCase()) !== -1){
|
|
msgHdr.folder.setJunkScoreForMessages(msg, "100");
|
|
mailHops.LOG( "Junk: Country Filter match" );
|
|
}
|
|
}
|
|
// tag as junk if travel time is longer than 10 seconds
|
|
if(!!mailHops.options.travel_time_junk && mailHops.message.time != null && mailHops.message.time > 10000){
|
|
msgHdr.folder.setJunkScoreForMessages(msg, "100");
|
|
mailHops.LOG( "Junk: Travel time match" );
|
|
}
|
|
|
|
} catch(e){
|
|
mailHops.LOG( "Error adding CountryCode tag: "+e );
|
|
}
|
|
}
|
|
};
|
|
|
|
mailHops.getResults = function(){
|
|
|
|
if(!mailHops.msgURI)
|
|
return false;
|
|
|
|
var messenger = Components.classes["@mozilla.org/messenger;1"].createInstance().QueryInterface(Components.interfaces.nsIMessenger);
|
|
var msgHdr = messenger.messageServiceFromURI(mailHops.msgURI).messageURIToMsgHdr(mailHops.msgURI);
|
|
|
|
if(!msgHdr)
|
|
return false;
|
|
|
|
return msgHdr.getStringProperty( "MH-Route" );
|
|
};
|
|
|
|
mailHops.refreshCache = function(){
|
|
mailHops.saveResults('');
|
|
mailHops.getRoute();
|
|
};
|
|
|
|
addEventListener ( "messagepane-loaded" , mailHops.setupEventListener , true ) ;
|