1
0
mirror of https://github.com/MailHops/mailhops-node.git synced 2025-05-16 11:50:12 -07:00

Compare commits

...

18 Commits

Author SHA1 Message Date
Andrew Van Tassel
86b12cd5e0 Updated IPV6 regex 2018-06-24 10:45:53 -06:00
Andrew Van Tassel
c2811b7afa Added IP colon fix 2017-07-25 10:34:48 -06:00
Andrew Van Tassel
38a0863a1a Fixed IP order when Received header has multiple IPs. 2017-02-21 20:08:48 -07:00
Andrew Van Tassel
d399507458 Added id check for getIPsFromMailParser
Added more tests for getIPsFromMailParser
2017-01-08 12:14:44 -07:00
Andrew Van Tassel
f242c6f101 Added a space to the id check 2017-01-08 11:58:00 -07:00
Andrew Van Tassel
19c5472484 Bump 2.0.4 2017-01-07 12:45:26 -07:00
Andrew Van Tassel
3f35f7c77a Added received header id check 2017-01-07 12:44:18 -07:00
Andrew Van Tassel
bb628e4030 Bump 2.0.3 2016-12-09 18:55:30 -07:00
Andrew Van Tassel
da82583dd4 Fixed start stop methods param 2016-12-09 18:49:34 -07:00
Andrew Van Tassel
14239e568d Bump v2.0.2 2016-12-09 18:41:24 -07:00
Andrew Van Tassel
aa234fccfb Updated tests 2016-12-09 18:40:46 -07:00
Andrew Van Tassel
1723b319fb Added start/end hop methods
Fixed getIPsFromMailParser
2016-12-09 18:39:33 -07:00
Andrew Van Tassel
99505c4066 Added getIPsFromMailParser 2016-12-09 17:37:30 -07:00
Andrew Van Tassel
5bc259523a Cleaned up readme 2016-10-12 21:18:42 -06:00
Andrew Van Tassel
7ba7fc498e Merge branch 'master' of github.com:avantassel/mailhops-node 2016-10-12 21:16:43 -06:00
Andrew Van Tassel
df6ad59b69 Fixed date logic 2016-10-04 09:58:07 -06:00
Andrew Van Tassel
6ee6c3d673 Added more tests for v2 2016-09-27 00:09:18 -06:00
Andrew Van Tassel
bfb4b247b4 Update README.md 2015-11-04 10:53:28 -07:00
12 changed files with 454 additions and 111 deletions

View File

@ -1,12 +1,49 @@
# Change Log # Change Log
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [0.0.3] - 2015-06-15 ## 2.1.1 - 2017-07-25
### Fixed
- IP test when IP regex finds a semantic version with a trailing colon
## 2.1.0 - 2017-02-21
### Fixed
- IP order when Received header has multiple IPs.
## 2.0.2 - 2016-12-09
### Added
- Get Start Hop method
- Get End Hop method
### Fixed
- getIPsFromMailParser when only one received header that is a string.
## 2.0.1 - 2016-12-08
### Added
- getIPsFromMailParser method for parsed message from [mailparser](https://www.npmjs.com/package/mailparser)
## 2.0.0 - 2016-09-09
### Added
- MailHops SSL
- MailHops API v2
- Test for no IPs in header
- Time Traveled, total time it took for the email to reach you
- Bump version 2.0 to match API v2
### Fixed
- Received IP sorting
- Received IP duplicates
## 0.0.3 - 2015-06-15
### Added ### Added
- Header IP parsing - Header IP parsing
## [0.0.1] - 2015-06-14 ## 0.0.1 - 2015-06-14
### Added ### Added
- MailHops API lookup endpoint call - MailHops API lookup endpoint call

View File

@ -1,22 +1,23 @@
# MailHops API node # MailHops API node
[www.MailHops.com](http://www.mailhops.com) [www.MailHops.com](https://www.mailhops.com)
[![Build Status](https://travis-ci.org/avantassel/mailhops-node.svg)](https://travis-ci.org/avantassel/mailhops-node) <img src="https://www.mailhops.com/images/logos/logo.png" alt="MailHops logo" title="MailHops" align="right" />
<img src="http://www.mailhops.com/images/logos/mailhops395.png" width="200" alt="MailHops logo" title="MailHops" align="right" /> [![Build Status](https://travis-ci.org/MailHops/mailhops-node.svg)](https://travis-ci.org/MailHops/mailhops-node)
[![NPM version](https://img.shields.io/npm/v/mailhops.svg)](https://www.npmjs.com/package/mailhops)
A nodejs module for interacting with the MailHops API. A nodejs module for interacting with the MailHops API.
##Getting Started ## Getting Started
###Installation ### Installation
``` ```
$ npm install mailhops $ npm install mailhops
``` ```
###Configuration ### Configuration
Simply require the mailhops module, instantiate a new MailHops object, configure it if necessary, and start making calls. Simply require the mailhops module, instantiate a new MailHops object.
New MailHops objects can be instantiated with configuration parameters. Here is an example: New MailHops objects can be instantiated with configuration parameters. Here is an example:
@ -24,15 +25,14 @@ New MailHops objects can be instantiated with configuration parameters. Here is
var MailHops = require("mailhops"); var MailHops = require("mailhops");
var mailhops = new MailHops({ var mailhops = new MailHops({
api_key: "aWN8Pb27Xj6GfV8D6ARsjokonYwbWUNbz9rM", api_key: "aWN8Pb27Xj6GfV8D6ARsjokonYwbWUNbz9rM",
api_version: 1, api_version: 2,
proxy: "http://myproxy:3128", proxy: "http://myproxy:3128",
app_name: "Node App v1.0.0", app_name: "Node App v2.0.1",
forecastio_api_key: "", forecastio_api_key: ""
show_client: 1
}); });
``` ```
MailHops objects can also be configured via the ```.configure(options)``` method. Here is an exmaple: MailHops objects can also be configured via the `.configure(options)` method. Here is an example:
```javascript ```javascript
var MailHops = require("mailhops"); var MailHops = require("mailhops");
@ -43,25 +43,27 @@ var options = {
} }
mailhops.configure(options); mailhops.configure(options);
// get IPs from a full header where headerText is the full header // get IPs from a full header where headerText is the full header
var ips = mailhops.getIPsFromHeader(headerText); var ips = mailhops.getIPsFromHeader(headerText);
// or pass in an array of IP addresses // or pass in an array of IP addresses
var ips = ['216.58.217.46','98.138.253.109']; var ips = ['216.58.217.46','98.138.253.109'];
mailhops.lookup(ips,function(err,response){ mailhops.lookup(ips,function(err, res, body){
console.log(response); if(err)
console.log('MailHops Error',err);
console.log(body);
}); });
var mapUrl = mailhops.mapUrl('216.58.217.46,98.138.253.109'); var mapUrl = mailhops.mapUrl('216.58.217.46,98.138.253.109');
``` ```
###Running Tests ### Running Tests
``` ```
$ npm test $ npm test
``` ```
## Other MailHops projects ## Other MailHops projects
- [API](https://github.com/avantassel/mailhops-api) - https://github.com/mailhops
- [Postbox & Thunderbird plugin](https://github.com/avantassel/mailhops-plugin)

View File

@ -1,8 +1,7 @@
{ {
"base_uri": "http://api.mailhops.com" "base_uri": "https://api.mailhops.com"
,"app_name": "Node App v1.0.0" ,"app_name": "Node App v2.0.0"
,"api_version": 1 ,"api_version": 2
,"api_key": "" ,"api_key": ""
,"forecastio_api_key": "" ,"forecastio_api_key": ""
,"show_client": 1
} }

View File

@ -5,6 +5,14 @@ var request = require([__dirname, "request"].join("/"));
module.exports = { module.exports = {
time_traveled: null,
timeTraveled: function(time){
if(typeof time != 'undefined')
this.time_traveled = time;
return this.time_traveled;
},
lookup: function(route, options, fn){ lookup: function(route, options, fn){
if(_.isFunction(options) && _.isUndefined(fn)){ if(_.isFunction(options) && _.isUndefined(fn)){
fn = options; fn = options;
@ -12,12 +20,13 @@ module.exports = {
} }
var qs = options; var qs = options;
qs.api_key = this.api_key || ''; qs.api_key = qs.api_key || this.api_key || '';
qs.c = this.show_client; qs.c = this.show_client || 1;
qs.r = Array.isArray(route) ? route.join(',').replace(" ", "") : route.replace(" ", ""); qs.r = Array.isArray(route) ? route.join(',').replace(" ", "") : route.replace(" ", "");
if(!!this.forecastio_api_key)
if(this.forecastio_api_key)
qs.fkey = this.forecastio_api_key; qs.fkey = this.forecastio_api_key;
if(!!this.timeTraveled())
qs.t = this.timeTraveled();
var config = { var config = {
uri: [this.api_version, "lookup"].join("/"), uri: [this.api_version, "lookup"].join("/"),
@ -31,11 +40,11 @@ module.exports = {
//just returns a map url that can be used as an iframe src //just returns a map url that can be used as an iframe src
mapUrl: function(route, options){ mapUrl: function(route, options){
var qs = options || {}; var qs = options || {};
qs.api_key = this.api_key || ''; qs.api_key = qs.api_key || this.api_key || '';
qs.c = this.show_client; qs.c = this.show_client || 1;
qs.r = Array.isArray(route) ? route.join(',').replace(" ", "") : route.replace(" ", ""); qs.r = Array.isArray(route) ? route.join(',').replace(" ", "") : route.replace(" ", "");
if(this.forecastio_api_key) if(!!this.forecastio_api_key)
qs.fkey = this.forecastio_api_key; qs.fkey = this.forecastio_api_key;
return [this.base_uri, this.api_version, "map", '?'+querystring.stringify(qs)].join("/"); return [this.base_uri, this.api_version, "map", '?'+querystring.stringify(qs)].join("/");
@ -43,24 +52,33 @@ module.exports = {
//parse the whole email header //parse the whole email header
getIPsFromHeader: function(header){ getIPsFromHeader: function(header){
var receivedHeaders = this.getReceivedHeaders(header) var receivedHeaders = this.getReceivedHeaders(header);
,ips = [] var ips = [];
,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 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}$/;
,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 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;
,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 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;
_.each(receivedHeaders, function(line){ _.each(receivedHeaders, function(line){
//IPV6 check //IPV6 check
line = line.replace('[IPv6:','[');
if(line.match(regexIPV6)){ if(line.match(regexIPV6)){
ips.push( line.match(regexIPV6)[0] ); ips.unshift( line.match(regexIPV6)[0] );
return; return;
} }
var received_ips = line.match(regexAllIp); var received_ips = line.match(regexAllIp);
if(!received_ips)
return;
//get unique IPs for each Received header
received_ips = received_ips.filter(function(item, pos) {
return received_ips.indexOf(item) == pos;
});
//maybe multiple IPs in one Received: line //maybe multiple IPs in one Received: line
_.each(received_ips, function(ip){ _.eachRight(received_ips, function(ip){
var firstchar = line.substring(line.indexOf(ip)-1).substring(0,1); var firstchar = line.substring(line.indexOf(ip)-1).substring(0,1);
var lastchar = line.substring(line.indexOf(ip)+ip.length).substring(0,1); var lastchar = line.substring(line.indexOf(ip)+ip.length).substring(0,1);
@ -70,11 +88,18 @@ module.exports = {
if(!firstchar.match(/\.|\d|\-/) if(!firstchar.match(/\.|\d|\-/)
&& !lastchar.match(/\.|\d|\-/) && !lastchar.match(/\.|\d|\-/)
&& ( firstchar != '?' && lastchar != '?' ) && ( firstchar != '?' && lastchar != '?' )
&& ( firstchar != ':' && lastchar != ':' )
&& lastchar != ';' && lastchar != ';'
&& regexIp.test(ip) && line.toLowerCase().indexOf(' id '+ip) === -1
&& ips.indexOf(ip)===-1){ && parseInt(ip.substring(0,ip.indexOf('.'))) < 240 //IANA-RESERVED
&& regexIp.test(ip)){
ips.push( ip ); ips.unshift( ip );
} else if(regexIp.test(ip)
&& line.indexOf(ip) !== line.lastIndexOf(ip)){
//check for duplicate IPs in one line
ips.unshift( ip );
} }
}); });
@ -83,16 +108,91 @@ module.exports = {
return ips; return ips;
}, },
getIPsFromMailParser: function(parsedmail){
var ips = [];
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;
if(typeof parsedmail.headers.received == 'undefined')
return ips;
else if(typeof parsedmail.headers.received == 'string')
parsedmail.headers.received = [parsedmail.headers.received];
_.each(parsedmail.headers.received,function(line){
//IPV6 check
line = line.replace('[IPv6:','[');
if(line.match(regexIPV6)){
ips.unshift( line.match(regexIPV6)[0] );
return;
}
var received_ips = line.match(regexAllIp);
if(!received_ips)
return;
//get unique IPs for each Received header
received_ips = received_ips.filter(function(item, pos) {
return received_ips.indexOf(item) == pos;
});
//maybe multiple IPs in one Received: line
_.eachRight(received_ips, function(ip){
var firstchar = line.substring(line.indexOf(ip)-1).substring(0,1);
var lastchar = line.substring(line.indexOf(ip)+ip.length).substring(0,1);
//do some checks on the first and last characters surrounding the IP
// Microsoft SMTP Server id 14.3.195.1; something like this should not be included
if(!firstchar.match(/\.|\d|\-/)
&& !lastchar.match(/\.|\d|\-/)
&& ( firstchar != '?' && lastchar != '?' )
&& ( firstchar != ':' && lastchar != ':' )
&& lastchar != ';'
&& line.toLowerCase().indexOf(' id '+ip) === -1
&& parseInt(ip.substring(0,ip.indexOf('.'))) < 240 //IANA-RESERVED
&& regexIp.test(ip)){
ips.unshift( ip );
} else if(regexIp.test(ip)
&& line.indexOf(ip) !== line.lastIndexOf(ip)){
//check for duplicate IPs in one line
ips.unshift( ip );
}
});
});
if(parsedmail.date && parsedmail.receivedDate){
try {
var firstDate = new Date(parsedmail.date);
var lastDate = new Date(parsedmail.receivedDate);
this.timeTraveled(lastDate - firstDate);
} catch(e){
this.timeTraveled(null);
}
} else {
this.timeTraveled(null);
}
return ips;
},
getReceivedHeaders: function(header){ getReceivedHeaders: function(header){
var receivedHeaders = []; var receivedHeaders = [];
var rline = ''; var rline = '',firstDate,lastDate;
if ( header ){ if ( header ){
var headers = header.split("\n"); var headers = header.split("\n");
_.each(headers,function(line){ _.each(headers,function(line){
//if the header line begins with Received, X-Received or X-Originating-IP then push that to our array //if the header line begins with Received, X-Received or X-Originating-IP then push that to our array
if(line.indexOf('Received:')===0 || line.indexOf('X-Received:')===0){ if(line.indexOf('Date:')===0){
firstDate = line.substring(line.indexOf('Date:')+5).trim();
}
else if(line.indexOf('Received:')===0 || line.indexOf('X-Received:')===0){
if(rline != ''){ if(rline != ''){
receivedHeaders.push(rline); receivedHeaders.push(rline);
rline = ''; rline = '';
@ -111,6 +211,15 @@ module.exports = {
} else { } else {
rline += line; rline += line;
receivedHeaders.push(rline); receivedHeaders.push(rline);
// first and last dates are used to calculate time_traveled Traveled
if(rline.indexOf(';')!==-1){
if(!firstDate)
firstDate = rline.substring(rline.indexOf(';')+1).trim();
if(!lastDate)
lastDate = rline.substring(rline.indexOf(';')+1).trim();
}
rline = ''; rline = '';
return; return;
} }
@ -119,6 +228,42 @@ module.exports = {
if(rline != '') if(rline != '')
receivedHeaders.push(rline); receivedHeaders.push(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.timeTraveled(lastDate - firstDate);
} catch(e){
this.timeTraveled(null);
}
} else {
this.timeTraveled(null);
}
return receivedHeaders; return receivedHeaders;
},
getStartHop: function(routeResponse){
var route = _.filter(routeResponse, function(h){
return ((!!h.lat && !!h.lng) || h.coords);
});
if(route.length)
return route[0];
return '';
},
getEndHop: function(routeResponse){
var route = _.filter(routeResponse, function(h){
return ((!!h.lat && !!h.lng) || h.coords);
});
if(route.length)
return route[route.length-1];
return '';
} }
} }

View File

@ -7,10 +7,11 @@ exports.create = function(config, fn){
uri: [configuration.base_uri, config.uri].join("/"), uri: [configuration.base_uri, config.uri].join("/"),
method: "GET", method: "GET",
qs: config.qs || {}, qs: config.qs || {},
proxy: config.proxy timeout: 4000
} };
if(config.proxy)
request(options, function(err, response, body){ options.proxy = config.proxy;
fn(err, JSON.parse(body)); request(options, function(error, response, body){
fn(error, response, JSON.parse(body));
}); });
} }

View File

@ -6,10 +6,10 @@ function MailHops(config){
} }
MailHops.prototype.configure = function(config){ MailHops.prototype.configure = function(config){
this.base_uri = config.base_uri; this.base_uri = config.base_uri || 'https://api.mailhops.com';
this.api_key = config.api_key || undefined; this.api_key = config.api_key || undefined;
this.proxy = config.proxy || undefined; this.proxy = config.proxy || undefined;
this.api_version = config.api_version || 1; this.api_version = config.api_version || 2;
this.api_version = ["v", this.api_version].join(""); this.api_version = ["v", this.api_version].join("");
this.app_name = config.app_name; this.app_name = config.app_name;
this.forecastio_api_key = config.forecastio_api_key || undefined; this.forecastio_api_key = config.forecastio_api_key || undefined;

View File

@ -1,22 +1,14 @@
{ {
"name": "mailhops", "name": "mailhops",
"version": "0.0.4", "version": "2.1.2",
"description": "A nodejs module for interacting with the MailHops API.", "description": "A nodejs module for interacting with the MailHops API.",
"main": "main.js", "main": "main.js",
"dependencies": {
"assert": "^1.1.1",
"async": "^0.6.2",
"lodash": "^2.4.1",
"mocha": "^2.2.4",
"qs": "^3.1.0",
"request": "https://registry.npmjs.org/request/-/request-2.34.0.tgz"
},
"scripts": { "scripts": {
"test": "mocha" "test": "mocha"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/avantassel/mailhops-node.git" "url": "git://github.com/mailhops/mailhops-node.git"
}, },
"keywords": [ "keywords": [
"mailhops", "mailhops",
@ -30,11 +22,11 @@
"name": "Andrew Van Tassel", "name": "Andrew Van Tassel",
"email": "andrew@mailhops.com" "email": "andrew@mailhops.com"
}, },
"license": "GPLv2", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/avantassel/mailhops-node/issues" "url": "https://github.com/mailhops/mailhops-node/issues"
}, },
"homepage": "https://github.com/avantassel/mailhops-node", "homepage": "https://mailhops.com",
"_npmUser": { "_npmUser": {
"name": "avantassel", "name": "avantassel",
"email": "andrew@andrewvantassel.com" "email": "andrew@andrewvantassel.com"
@ -44,5 +36,15 @@
"name": "avantassel", "name": "avantassel",
"email": "andrew@mailhops.com" "email": "andrew@mailhops.com"
} }
] ],
"dependencies": {
"async": "^2.0.1",
"lodash": "^4.15.0",
"qs": "^6.2.1",
"request": "^2.7.4"
},
"devDependencies": {
"assert": "^1.4.1",
"mocha": "^3.0.2"
}
} }

View File

@ -0,0 +1,5 @@
Received: (maildir_expire.pl 32008); Sat, 3 Sep 2016 22:21:29 -0800
From: Inbox Archiver <root@localhost>
Date: Sat, 3 Sep 2016 22:21:29 -0800
To: x15007238
Subject: [Inbox archiver: 2 old messages moved to .old-messages/]

View File

@ -2,11 +2,11 @@ Return-Path: <bounces+1352097-7fe0-andrew=mailhops.com@delivery2.klaviyomail.com
X-Originating-IP: [2607:fb90:50f:5547:0:46:e46a:bd01] X-Originating-IP: [2607:fb90:50f:5547:0:46:e46a:bd01]
X-Original-To: andrew@mailhops.com X-Original-To: andrew@mailhops.com
Delivered-To: x12669597@homiemail-mx32.g.dreamhost.com Delivered-To: x12669597@homiemail-mx32.g.dreamhost.com
Received: from o2.delivery2.klaviyomail.com (o2.delivery2.klaviyomail.com [198.21.5.108]) Received: from 198.21.5.108.o2.delivery2.klaviyomail.com (o2.delivery2.klaviyomail.com [198.21.5.108])
(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))
(No client certificate requested) (No client certificate requested)
by homiemail-mx32.g.dreamhost.com (Postfix) with ESMTPS id 4BE0510190916 by homiemail-mx32.g.dreamhost.com (Postfix) with ESMTPS id 4BE0510190916
for <andrew@mailhops.com>; Mon, 15 Jun 2015 10:38:03 -0700 (PDT) for <andrew@mailhops.com>; Thu, 3 Sep 2014 16:38:48 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; DKIM-Signature: v=1; a=rsa-sha1; c=relaxed;
d=delivery.klaviyomail.com; d=delivery.klaviyomail.com;
h=content-type:mime-version:from:to:subject; s=smtpapi; h=content-type:mime-version:from:to:subject; s=smtpapi;
@ -14,14 +14,14 @@ DKIM-Signature: v=1; a=rsa-sha1; c=relaxed;
4DAOoP00xozjkYc6KMkyOLFUrfRrZEpBYXQC0m6IXDsyiTuk8SkopTpcc8YYitoa 4DAOoP00xozjkYc6KMkyOLFUrfRrZEpBYXQC0m6IXDsyiTuk8SkopTpcc8YYitoa
yv4eaW1h9XRG0VYvl3RbBX5QFZsikRmReBO9Yu8gdLizZ8X83SF5TCwDLU99V/OL yv4eaW1h9XRG0VYvl3RbBX5QFZsikRmReBO9Yu8gdLizZ8X83SF5TCwDLU99V/OL
F6oLELBhbNFezk= F6oLELBhbNFezk=
Received: by filter0500p1mdw1.sendgrid.net with SMTP id filter0500p1mdw1.14444.557F0D731E Received: by filter0500p1mdw1.sendgrid.net with SMTP id 15.1.225.42
2015-06-15 17:37:59.069788226 +0000 UTC 2015-06-15 17:37:59.069788226 +0000 UTC
Received: from queue-worker-166.servers.clovesoftware.com (ec2-54-157-138-253.compute-1.amazonaws.com [54.157.138.253]) Received: from queue-worker-166.servers.clovesoftware.com (ec2-54-157-138-253.compute-1.amazonaws.com [54.157.138.253])
by ismtpd-013 (SG) with ESMTP id 14df84c98b9.582.18b0cc by ismtpd-013 (SG) with ESMTP id 14df84c98b9.582.18b0cc
for <andrew@mailhops.com>; Mon, 15 Jun 2015 17:37:58 +0000 (UTC) for <andrew@mailhops.com>; Thu, 4 Sep 2014 17:37:58 +0000 (UTC)
Received: from ip-10-157-90-72.ec2.internal (localhost [127.0.0.1]) Received: from ip-10-157-90-72.ec2.internal (localhost [127.0.0.1])
by queue-worker-166.servers.clovesoftware.com (Postfix) with ESMTP id E2343271B8 by queue-worker-166.servers.clovesoftware.com (Postfix) with ESMTP id E2343271B8
for <andrew@mailhops.com>; Mon, 15 Jun 2015 17:37:58 +0000 (UTC) for <andrew@mailhops.com>; Thu, 4 Sep 2014 17:37:58 +0000 (UTC)
Received: from edge01.net.lu.se (130.235.56.196) by uwcas04.uw.lu.se Received: from edge01.net.lu.se (130.235.56.196) by uwcas04.uw.lu.se
(130.235.59.236) with Microsoft SMTP Server (TLS) id 14.3.195.1; Thu, 4 Sep (130.235.59.236) with Microsoft SMTP Server (TLS) id 14.3.195.1; Thu, 4 Sep
2014 11:38:30 +0200 2014 11:38:30 +0200
@ -31,14 +31,22 @@ Received: from muon.isy.liu.se (130.236.48.25) by edge01.net.lu.se
Received: from ip227-36.wireless.lu.se (ip227-36.wireless.lu.se Received: from ip227-36.wireless.lu.se (ip227-36.wireless.lu.se
[130.235.227.36]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No [130.235.227.36]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No
client certificate requested) by muon.isy.liu.se (Postfix) with ESMTPSA id client certificate requested) by muon.isy.liu.se (Postfix) with ESMTPSA id
D4B3D802; Thu, 4 Sep 2014 11:38:28 +0200 (MEST) D4B3D802; Thu, 3 Sep 2014 16:38:38 -0700 (PDT)
Received: from mail-vk0-x231.google.com ([IPv6:2001:4860:4860::8888])
by smtpin.rzone.de (RZmta 43.10 REPLY)
with ESMTPS id S05391u5CBmerDX
(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (curve secp384r1 with 384 ECDH bits, eq. 7680 bits RSA))
(Client CN "smtp.gmail.com", Issuer "Google Internet Authority G2" (verified OK))
(Client hostname verification FAILED)
for <cron@mailhops.com>;
Tue, 12 Jun 2018 13:48:40 +0200 (CEST)
Content-Type: multipart/alternative; Content-Type: multipart/alternative;
boundary="===============2767014682564602329==" boundary="===============2767014682564602329=="
MIME-Version: 1.0 MIME-Version: 1.0
From: ProgrammableWeb Today <editor@programmableweb.com> From: ProgrammableWeb Today <editor@programmableweb.com>
To: Andrew Van Tassel <andrew@mailhops.com> To: Andrew Van Tassel <andrew@mailhops.com>
Subject: Is It Time to Move On from .NET? Subject: Is It Time to Move On from .NET?
Date: Mon, 15 Jun 2015 17:37:58 GMT Date: Thu, 3 Sep 2014 16:38:38 -0700 (PDT)
X-Kmail-Relay: [gmh6f2.krelaymail.com]:587 X-Kmail-Relay: [gmh6f2.krelaymail.com]:587
Message-Id: <20150615173758.E2343271B8@queue-worker-166.servers.clovesoftware.com> Message-Id: <20150615173758.E2343271B8@queue-worker-166.servers.clovesoftware.com>
X-SG-EID: z/7dFXGDBkx/fUP8kk8C2xB01JCsbu4AgSgValkgiTccFIBxI9L4Y1qe6fbe+Uf2YVSIXGTcm+Mu+n X-SG-EID: z/7dFXGDBkx/fUP8kk8C2xB01JCsbu4AgSgValkgiTccFIBxI9L4Y1qe6fbe+Uf2YVSIXGTcm+Mu+n

View File

@ -14,30 +14,35 @@ describe("mailhops", function(){
it("required api methods exist", function(){ it("required api methods exist", function(){
var required_keys = [ var required_keys = [
"configure", "configure",
"time_traveled",
"timeTraveled",
"lookup", "lookup",
"mapUrl", "mapUrl",
"getIPsFromHeader", "getIPsFromHeader",
"getReceivedHeaders" "getIPsFromMailParser",
"getReceivedHeaders",
"getStartHop",
"getEndHop"
] ]
assert.deepEqual(_.keys(mailhops.__proto__), required_keys); assert.deepEqual(_.keys(mailhops.__proto__), required_keys);
}); });
it("default config parameters are set correctly", function(){ it("uses default config parameters", function(){
assert.equal(mailhops.api_version, "v1"); assert.equal(mailhops.api_version, "v2");
assert.equal(mailhops.api_key, undefined); assert.equal(mailhops.api_key, undefined);
}); });
}); });
describe("configure()", function(){ describe("configure()", function(){
it("sets config parameters correctly", function(){ it("sets config parameters with configure method", function(){
mailhops.configure({ mailhops.configure({
api_version: 1, api_version: 2,
api_key: "aWN8Pb27Xj6GfV8D6ARsjokonYwbWUNbz9rM", api_key: "aWN8Pb27Xj6GfV8D6ARsjokonYwbWUNbz9rM",
app_name: "Node App" app_name: "Node App"
}); });
assert.equal(mailhops.api_version, "v1"); assert.equal(mailhops.api_version, "v2");
assert.equal(mailhops.api_key, "aWN8Pb27Xj6GfV8D6ARsjokonYwbWUNbz9rM"); assert.equal(mailhops.api_key, "aWN8Pb27Xj6GfV8D6ARsjokonYwbWUNbz9rM");
assert.equal(mailhops.app_name, "Node App"); assert.equal(mailhops.app_name, "Node App");
}); });

35
test/mailparser.json Normal file
View File

@ -0,0 +1,35 @@
{
"text": "Ok test it then\n",
"headers": {
"return-path": "<andrew@showhops.com>",
"x-original-to": "andrew@mailhops.com",
"delivered-to": "x12669597@homiemail-mx18.g.dreamhost.com",
"received": ["from homiemail-a110.g.dreamhost.com (agjbgdcfdbdd.dreamhost.com [69.163.253.130]) (using TLSv1.1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by homiemail-mx18.g.dreamhost.com (Postfix) with ESMTPS id 5895D9004034 for <andrew@mailhops.com>; Thu, 8 Dec 2016 16:02:34 -0800 (PST)"
, "from homiemail-a110.g.dreamhost.com (localhost [127.0.0.1]) by homiemail-a110.g.dreamhost.com (Postfix) with ESMTP id 15.1.225.42 for <andrew@mailhops.com>; Thu, 8 Dec 2016 16:02:34 -0800 (PST)"
, "from Andrews-MacBook-Pro-4.local (c-67-177-226-240.hsd1.co.comcast.net [67.177.226.240]) (Authenticated sender: andrew@showhops.com) by homiemail-a110.g.dreamhost.com (Postfix) with ESMTPA id F349520047638 for <andrew@mailhops.com>; Thu, 8 Dec 2016 16:02:33 -0800 (PST)"
],
"dkim-signature": "v=1; a=rsa-sha1; c=relaxed; d=showhops.com; h=message-id :date:from:mime-version:to:subject:content-type :content-transfer-encoding; s=showhops.com; bh=gXjUxcC3bG8U+A3N7 7ELq45N68U=; b=U09iG7hIF9CmUxHZSxHJekgSgfApMkJmTTMDeyXsVSJlMQish ElW8P2UfAd1k2+x+PlrV7vDIxr2NSyWI/qDSuYuQt4GStsfJnS2LVEodzPM0Mfsx yAK16/iGNu87UCnYo6v3xvilyW6D0L4P4RM/NB7ib7NinOUIaWJiMJ/hAk=",
"message-id": "<5849F498.3080807@showhops.com>",
"date": "Thu, 08 Dec 2016 17:02:32 -0700",
"from": "Andrew Van Tassel <andrew@showhops.com>",
"user-agent": "Postbox 4.0.8 (Macintosh/20151105)",
"mime-version": "1.0",
"to": "Andrew Van Tassel <andrew@mailhops.com>",
"subject": "tomorrow ok",
"content-type": "text/plain; charset=ISO-8859-1; format=flowed",
"content-transfer-encoding": "7bit"
},
"subject": "tomorrow ok",
"messageId": "5849F498.3080807@showhops.com",
"priority": "normal",
"from": [{
"address": "andrew@showhops.com",
"name": "Andrew Van Tassel"
}],
"to": [{
"address": "andrew@mailhops.com",
"name": "Andrew Van Tassel"
}],
"date": "2016-12-09T00:02:32.000Z",
"receivedDate": "2016-12-09T00:02:34.000Z"
}

View File

@ -3,15 +3,14 @@ var assert = require("assert");
var request = require("request"); var request = require("request");
var fs = require('fs'); var fs = require('fs');
var configuration = require([__dirname, "..", "config"].join("/")); var configuration = require([__dirname, "..", "config"].join("/"));
var pkg = require([__dirname, "..", "package"].join("/"));
var MailHops = require([__dirname, "..", "main"].join("/")); var MailHops = require([__dirname, "..", "main"].join("/"));
var mailhops = new MailHops(configuration);
console.log('Using %s', mailhops.base_uri);
describe("main", function(){ describe("main", function(){
describe("new MailHops()", function(){ describe("new MailHops()", function(){
var mailhops = new MailHops(configuration);
it("api_version parameter exists", function(){ it("api_version parameter exists", function(){
assert.ok(_.has(mailhops, "api_version")); assert.ok(_.has(mailhops, "api_version"));
}); });
@ -28,26 +27,31 @@ describe("main", function(){
assert.ok(_.has(mailhops, "version")); assert.ok(_.has(mailhops, "version"));
}); });
it("version parameter equals that defined in package.json", function(){
assert.equal(mailhops.version, pkg.version);
});
}); });
describe("lookup endpoint", function(){ describe("lookup endpoint", function(){
var mailhops = new MailHops(configuration);
it('string route should return a 200 response with private ip', function(done){ it('string route should return a 200 response with private ip', function(done){
mailhops.lookup('127.0.0.1', function(err, response){ mailhops.lookup('127.0.0.1', function(err, res, body){
assert.equal(response.meta['code'],200); assert.equal(res.statusCode,200);
assert.equal(response.response.route[0]['private'],true); assert.equal(body.response.route[0]['private'],true);
done(); done();
}); });
}); });
it('array route should return a 200 response with private ip', function(done){ it('array route should return a 200 response with private ip', function(done){
mailhops.lookup(['127.0.0.1','216.58.217.46','98.138.253.109'], function(err, response){ mailhops.lookup(['127.0.0.1','216.58.217.46','98.138.253.109'], function(err, res, body){
assert.equal(response.meta['code'],200); assert.equal(res.statusCode,200);
assert.equal(response.response.route[0]['private'],true); assert.equal(body.response.route[0]['private'],true);
done();
});
});
it('string route should return a 401 response for invalid api_key', function(done){
mailhops.lookup('127.0.0.1', {'api_key':'aWN8Pb27Xj6GfV8D6ARsjokonYwbWUNbz9rM'}, function(err, res, body){
assert.equal(res.statusCode,401);
done(); done();
}); });
}); });
@ -56,9 +60,60 @@ describe("main", function(){
describe("map endpoint", function(){ describe("map endpoint", function(){
var mailhops = new MailHops(configuration);
it('should return a 200 response', function(done){ it('should return a 200 response', function(done){
request(mailhops.mapUrl('127.0.0.1'), function (error, response, body) { request(mailhops.mapUrl('127.0.0.1'), function (err, res, body) {
assert.equal(response.statusCode,200); assert.equal(res.statusCode,200);
done();
});
});
});
describe("parse header and lookup", function(){
var mailhops = new MailHops(configuration);
//read header form file
var header = fs.readFileSync(__dirname+'/header-test.eml',{ encoding: 'utf8' });
var ips = mailhops.getIPsFromHeader(header);
it('should return an array of 10 Received IPs', function(done){
assert.equal(ips.length,10);
done();
});
it('should return a time of 10000 milliseconds', function(done){
var ips = mailhops.getIPsFromHeader(header);
assert.equal(mailhops.time_traveled,10000);
done();
});
it('should find 11 Received IPs', function(done){
assert.deepEqual(ips,['2001:4860:4860::8888',
'130.235.227.36',
'130.236.48.25',
'130.235.56.196',
'130.235.56.196',
'130.235.59.236',
'127.0.0.1',
'54.157.138.253',
'198.21.5.108',
'2607:fb90:50f:5547:0:46:e46a:bd01'
]);
done();
});
it('should not find id 15.1.225.42', function(done){
assert.equal(ips.indexOf('15.1.225.42'),-1);
done();
});
it('should return a 200 response and route of 11 hops', function(done){
mailhops.lookup(mailhops.getIPsFromHeader(header), function(err, res, body){
assert.equal(res.statusCode,200);
assert.equal(body.response['route'].length,11);
done(); done();
}); });
}); });
@ -67,13 +122,62 @@ describe("main", function(){
describe("parse header", function(){ describe("parse header", function(){
it('should return an array of 8 IP addresses', function(done){ var mailhops = new MailHops(configuration);
//read header form file //read header form file
var header = fs.readFileSync(__dirname+'/header-test.eml',{ encoding: 'utf8' }); var header = fs.readFileSync(__dirname+'/header-test-no-ips.eml',{ encoding: 'utf8' });
assert.equal(mailhops.getIPsFromHeader(header).length,8); var ips = mailhops.getIPsFromHeader(header);
it('should return an array of 0 IP addresses', function(done){
assert.equal(ips.length,0);
done();
});
it('should return a time of 0 milliseconds', function(done){
assert.equal(mailhops.timeTraveled(),null);
done(); done();
}); });
}); });
describe("get IPs form mailparser", function(){
var mailhops = new MailHops(configuration);
//read header form file
var message = fs.readFileSync(__dirname+'/mailparser.json',{ encoding: 'utf8' });
var ips = mailhops.getIPsFromMailParser(JSON.parse(message));
it('should return an array of 3 IP addresses', function(done){
assert.equal(ips.length,3);
done();
});
it('should return a time of 2000 milliseconds', function(done){
assert.equal(mailhops.timeTraveled(),2000);
done();
});
it('should find 3 Received IPs', function(done){
assert.deepEqual(ips,['67.177.226.240',
'127.0.0.1',
'69.163.253.130']);
done();
});
it('should not find id 15.1.225.42', function(done){
assert.equal(ips.indexOf('15.1.225.42'),-1);
done();
});
it('should return a 200 response and route of 4 hops', function(done){
mailhops.lookup(ips, function(err, res, body){
assert.equal(res.statusCode,200);
assert.equal(body.response['route'].length,4);
done();
});
});
});
}); });