vipi
Version:
Visitor IP Information using Maxmind DB's to list geographic info of queried IPv4 & IPv6.
1,307 lines (1,205 loc) • 57.3 kB
JavaScript
#!/usr/bin/env node
var UID = undefined; /**shorthand undefined*/
var EOL="\n"; var TAB="\t";
var f_SysT1=Date.now()/1000; //start time for measuring init times
var sPROJECT="vipi";
var sVERSION = "v0.0.4";
var bQuiet = false;
var bNoReturn = false; /* MODE: -dno || --dnooutput (7) */
var mVIPIFS, mVIPIFILES, mMAX, mMAXTZ, mPATH, mFS, mHTTP, mURL, mOS, mPS; // npm module references
try
{
mPATH = require("path");
mOS = require("os");
mFS = require("fs");
mURL = require("url");
mMAX = require("maxmind");
mMAXTZ = require("maxmind/lib/time_zone");
mVIPIFILES = require("./vipi_files");
mPS = require("child_process");
exports = module.exports =
{
"lookup" : lookup, "enableupdates" : enableupdates,
"quiet" : bQuiet, "noreturn" : bNoReturn
};
}
catch (e){ console.log("\nERROR: loading: "+sPROJECT+"\n"+e.toString()+"\nDid you NPM install? - Or are you loading DB files from correct path?"); }
/** Regular Expression IndexOf for Arrays or String
* Iterates array & returns the index(s) of matches as an array or single integer position; otherwise -1 if not found.
* @param reg RegEx reg regular expression to test with. E.g. /-ba/gim
* @return Array|Number position of all occurrence(s) or -1 means not found anywhere. */
Object.defineProperty(Array.prototype, 'indexOfAll',
{
value: function indexOfAll(reg)
{
var a2Return = [];
for (var i in this) { if (this.hasOwnProperty(i)) {if(this[i].toString().match(reg)){ a2Return.push(i);} } }
var iL = a2Return.length;
return (0 === iL) ? Number(-1) : (1 === iL) ? Number(a2Return[0]) : a2Return;
}
});
/** Zero pads numeric value to required length.
* @param num Number|String the numeric value to be padded.
* @param size Number the required length
* @param signed true|false optional indicator for +/- signed
* @return String padded value with or without sign
* */
function pad(num, size, signed) { var s = num+""; while (s.length < size) s = "0" + s; return signed ? s : (s > 0 ? "+"+s : "-"+s);}
/** 3 Lettered Zero (0) base array of months. */
var aMonths = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
/** var mNET = require("net"); could use net.isIP(), net.isIPv4() & net.isIPv6() instead of regex */
/* IP:PORT regex for minimal expected addresses */
var rgxIP = /^([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])$/;
var rgxIPPORT = /^([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.([1-9]?\d|1\d\d|2[0-4]\d|25[0-5]):([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
var rgxIPv6 = /^\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][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){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][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){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][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){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][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:)(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*$/;
var rgxIPv6PORT = /^\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][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){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][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){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][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){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][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(([0-9A-Fa-f]{1,4}:)(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}))|:)))(%.+)?\s*:([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
/** Checks if shell is colour capable. */
function isTerminal()
{ //noinspection JSUnresolvedVariable
return Boolean(process.stdout.isTTY) || (UID !== process.env.TERM && "xterm-256color" === process.env.TERM);
}
/** true|false whether environment output (eg stdio) supports colours */
var bTTY = isTerminal();
/** strip TTY ANSI colours for no TTY */
function sRaw(msg) { return bTTY ? msg : msg.replace( /\033\[[0-9;]*m/g, "" ); }
/** Colour strings: Byte escaped (shell) or Stripped for HTTP plain mode - may be extended for HTML colours. */
/**Red*/var sCR=""; /**Cyan*/ var sCC=""; /**Dark Gray*/ var sCDG=""; /**Green*/ var sCG="";
/**Natural*/ var sCN=""; /**Natural Bold*/ var sCNB=""; /**Purple*/ var sCP="";
/**Yellow*/var sCY=""; /**White*/ var sCW=""; /**Red BG + White Text*/ var sCRBG="";
function setColours()
{
/**Red*/ sCR=sRaw("\033[31m"); /**Cyan*/ sCC=sRaw("\033[36m");
/**Dark Gray*/ sCDG=sRaw("\033[90m"); /**Green*/ sCG=sRaw("\033[32m");
/**Natural*/ sCN=sRaw("\033[0m"); /**Natural Bold*/ sCNB=sRaw("\033[1m");
/**Purple*/ sCP=sRaw("\033[35m"); /**Yellow*/ sCY=sRaw("\033[33m");
/**White*/ sCW=sRaw("\x1b[37m"); /** Red BG + White Text*/ sCRBG=""+sRaw("\033[41");
}
setColours();
/** general log substitute for console.log and response to HTTP client as well as process exit support */
function log(msg, exit, res)
{
if (!bHTTP){ if (!bQuiet) {console.log(msg);} }
else
{
if (UID !== res)
{
if (false === res.hasheaderset)
{ //noinspection JSUnresolvedFunction
res.writeHead(200, oRHeader); res.hasheaderset = true;
}
//noinspection JSUnresolvedFunction
res.end(bNoReturn ? 0 : msg);
}
else { if (!bQuiet){ console.log(msg); } }
}
if (UID !== exit) {process.exit(1);}
///process.on('exit', function() { process.exit(UID === exit ? 0 : exit); });
}
var sVOBJ = "_vipi.obj";
var sVCLF = "_vipi.clf";
var sUOBJ = "_user.obj";
var sUCLF = "_user.clf";
var sDB = process.cwd()+"/vipi_dbs/";
var aDBs = ["GeoIPASNum.dat", "GeoIPASNumv6.dat", "GeoLiteCity.dat", "GeoLiteCityv6.dat", "GeoIP.dat", "GeoIPv6.dat"];
var aDBsVs = ["0", "0", "0", "0", "0", "0"];
var sArgs = ""; /** string presentation of arguments */
var sUage= "";
var bHTTP=false;
var sIP = "127.0.0.1";
var sPORT = "59999";
var aH = process.argv;
/** CLI specific arrays */
var aObjSaveKeys = [];
var aClfSaveKeys = [];
var iArgsIn = 0;
var IPs = [];
var aIPs = [];
var oHTTP;
var sFileDefaultObj = "";
var oRHeader = {};
var bJson = false; /* MODE: -djs" || --djson (6) */
var bReadAccess = true; /* MODE: -dnr || --dnoread (8) */
var bWriteAccess = true; /* MODE: -dnw || --dnowrite (9) */
var bLogToFile = true; /* MODE: -dsa || --dsaveauto (10) */
var bLogAll = true; /* MODE: -dur || --dsaverefering (16) */
var bDeleteAccess = true; /* MODE: -dad || --dallowdelete (4) */
var bNoASN = false; /* MODE: -dxa ||--dxasnumber (11) */
var bNoLocation = false; /* MODE: -dxl || --dxlocation (12) */
var bNoTimeZone = false; /* MODE: -dxt || --dxtimezone(13) */
var bNoIPQuiry = false; /* MODE: -dxq" || --dxquery (14) */
var bNoUA = false; /* MODE: -dxu" || --dxua (15) */
var bNoCLF = false; /* MODE: -nc || --noclf (17) */
var sDBPath; /* Custome DB PATHS */
var sL = sCNB + "\n@========================================@" + sCN; /* Line for TUI */
var sMsgWelcome = sCG + "\nSTARTED " + sCN+sCNB + sPROJECT + " " + sVERSION + sCN + " @ " + new Date();
var sMsgInit = sCP + "System ININT " + sCN+sCNB + "in: " + sCG + "%s" + sCN + " seconds";
var sEXITs = ["exit", "SIGHUP", "SIGUSR1", "SIGTERM", "SIGPIPE", "SIGINT", "SIGBREAK", "SIGWINCH", "uncaughtException"];
var args =
[
/*0*/[["-h", " --help", "/?"], TAB+"Shows this screen."],
/*1*/[["-as", "--asnumber", "/as"], "Includes ASN number of IP queried."],
/*2*/[["-b=", "--backups=", "/b="], "DB (default: ./dbs) directory / path to store all MaxMind files."],
/*3*/[["-cd", "--cdistance", "/cd"], "Calculate & Include approximate distances in Kilometres between two IPs."],
/*4*/[["-d", "--daemon", "/d"], "Daemon HTTP mode on "+sIP+":"+sPORT+" by default."],
/*5*/[["-dad", "--dadelete", "/dad"], "(daemon) Allow deletion CLF & JSON files."],
/*6*/[["-dp=", "--dipport=", "/dp="], "(daemon) IP:PORT address of the adaptor to bind to."],
/*7*/[["-djs", "--djson", "/djs"], "(daemon) 'application/json' Content-Type instead of default 'text/plain'."],
/*8*/[["-dno", "--dnoutput", "/dno"], "(daemon) No output or return for any query ignoring all parameters."],
/*9*/[["-dnr", "--dnread", "/dnr"], "(daemon) No Read Access ignoring !_rf / !_ra options."],
/*10*/[["-dnw", "--dnwrite", "/dnw"], "(daemon) No write or !_skc, !_sko or !_sf functionality."],
/*11*/[["-dsa", "--dsaveauto", "/dsa"], "(daemon) Save to automatic date schemed filename (yyyy-mm-dd_mmm) for .clf & .json file."],
/*12*/[["-dxa", "--dxasn", "/dxa"], "(daemon) Exclude ASN from queries or saves by default."],
/*13*/[["-dxl", "--dxlocate", "/dxl"], "(daemon) Exclude Location info from queries or saves by default."],
/*14*/[["-dxt", "--dxtime", "/dxt"], "(daemon) Exclude timezone from queries or saves by default."],
/*15*/[["-dxq", "--dxquery", "/dxq"], "(daemon) Used with -dur to disable !_= functionality for ip lookup string."],
/*16*/[["-dxu", "--dxua", "/dxu"], "(daemon) Exclude User-Agents from queries or saves by default."],
/*17*/[["-dur", "--duserefer", "/dur"], "(daemon) Use details from referal / dialling cURL."],
/*18*/[["-nc", "--noclf", "/nc"], "(daemon & cli) Disables CLF related saves with -sa & -sf parameters."],
/*19*/[["-nu", "--noupdates", "/nu"], "No Maxmind DB updates use existing backup files / path."],
/*20*/[["-sa", "--saveauto", "/sa"], "Save to file named using date: 'IP [DATE] KEY' (KEY optional) in .clf & .json file."],
/*21*/[["-sf=", "--savefile=", "/sf="], "Save like -sa: using specified file prefix name for .clf & .json saves."],
/*22*/[["-skc=", "--skclf=", "/skc="], "Save KEY clf string shell-escaped string eg: '200 \"GET / HTTP/1.1\"' to save."],
/*23*/[["-sko=", "--skobj=", "/sko="], "Save KEY object json string shell-escaped eg '{\"eg\":1}' to save."],
/*24*/[["-tz", "--timezone", "/tz"], "Includes time-zone of IP location queried."],
/*25*/[["-udb", "--updatedb", "/udb"], "Update Maxmind DB's."],
/*26*/[["-q", "--quiet", "/q"], TAB+"Quiet mode with no error or default header output."],
/*27*/[["-v", "--version", "/v"], "Output version information & exit."],
/*28*/[["-xi", "--xinfo", "/xi"], "Show OS / Node.js / Maxmind DB related info & exit."]
];
/** Header String appropriately set for CLI & HTTP Plain mode. Can be extended for HTML as well.*/
function stringHeader()
{
var sR = "";
sR+= sCDG+"|\\¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯/|"+EOL+sCN;
sR+= sCDG+"| ==== "+sCNB+sCY+"Visitor"+sCNB+sCC+" IP"+sCG+" Info"+sCN+sCDG+" ==== |"+EOL;
sR+= sCDG+"|/_________________________\\|"+EOL+sCN;
sR+= sCN+TAB+sCNB+sCW+sPROJECT+sCN+" - "+sCDG+sVERSION+EOL+sCN;
return sR;
}
/** HTTP daemon mode header string adjuster for query-string parameter addition and removal of non-related args.*/
function appHeaderString()
{ // adjust colours to current mode.? offset to init // setColours();
sArgs ="";
sUage = EOL + stringHeader();
if (bHTTP)
{
var cpya = JSON.parse(JSON.stringify(args));
var a1 = cpya.splice(20, 5);
a1.push(args[args.length-1]);
args = a1;
args.unshift([["-tp", "--textplain", "/tp"], "Return content-type: 'text/plain'."]);
args.unshift([["-sua", "--saveua", "/sua"], "Include requesting users agent in save."]);
args.unshift([["-rf=", "--readfile=", "/rf="], "Contents of log file to read & return."]);
args.unshift([["-ra", "--readauto", "/ra"], "Return content-type: 'application/json'"]);
args.unshift([["-nc", "--noclf", "/nc"], "Include requesting users agent in save."]);
args.unshift([["-nr", "--noreturn", "/nr"], "No output or return."]);
args.unshift([["-nu", "--noua", "/nu"], TAB+"Include requesting users agent in save."]);
args.unshift([["-ls", "--listsaves", "/ls"], "Lists all available user obj json files."]);
args.unshift([["-js", "--json", "/j"], TAB+"Return content-type: 'application/json' instead of 'text/plain'."]);
args.unshift([["-h", "--help", "/h"], TAB+"Shows this help page."]);
args.unshift([["-da", "--deleteall", "/da"], "Deletes all clf & obj file(s) resetting to new."]);
args.unshift([["-df=", "--deletefile=", "/df="], "Delete obj & clf named file(s)."]);
args.unshift([["-cd", "--cdistance", "/cd"], "Calculate & Include approximate distances in Kilometres between IPs pairs."]);
args.unshift([["-as", "--asnumber", "/as"], "Includes ASN number of IP queried."]);
// a1.push([["-", "--E", "/" ], "Example"]);
}
for (var iX=0; iX < args.length; ++iX)
{
if (bHTTP)
{
args[iX][0][0] = args[iX][0][0].replace(/-/g, "!_");
args[iX][0][1] = args[iX][0][1].replace(/--/g, "!_");
}
sArgs+=TAB+args[iX][0][0]+", "+args[iX][0][1]+(bHTTP?"":", "+args[iX][0][2])+TAB+args[iX][1]+TAB+" "+EOL;
sArgs = sArgs.replace(/shell-escaped/g, "URL-Encoded");
}
if (bHTTP)
{
sUage+= EOL+"Usage (?!_=...):"+EOL;
sUage+= TAB+"http://"+sCNB+sIP+":"+sPORT+sCN+"/?!_=ua"+TAB+TAB+"# use the inquiring address instead of ip."+EOL;
sUage+= TAB+"http://"+sCNB+sIP+":"+sPORT+sCN+"/?!_=208.67.222.222"+TAB+TAB+"# query & get results in text/plain"+EOL;
sUage+= TAB+"http://"+sCNB+sIP+":"+sPORT+sCN+"/?!_=208.67.222.222,8.8.8.8,8.8.4.4"+TAB+"# multiple ip loolup."+EOL;
sUage+= TAB+"http://"+sCNB+sIP+":"+sPORT+sCN+"/?!_=8.8.8.8&!_sa&!_sko=%7B%7D"+TAB+"# Auto save queried IP + key to file."+EOL;
sUage+= EOL+"(&) Query String / Get-Parameters:"+EOL+sArgs+EOL;
}
else
{
sUage+= EOL+"Usage:"+EOL;
sUage+= TAB+sCNB+sPROJECT+sCN+" [options] 208.67.222.222 "+EOL;
sUage+= EOL+"Options:"+EOL+sArgs;
}
if (bHTTP)
{
sUage = sUage.replace(/, /g, ","+TAB+" ");
sUage = sUage.replace(/¯/g, "-");
}
else{ sUage+= EOL+"See: "+TAB+sCW+sCNB+"man "+sPROJECT+sCN+TAB+"# for full details"+EOL; }
}
function showHelp(msgExtra, iExitCode, res)
{
if (UID === sUage || "" === sUage) { appHeaderString(); }
log(sUage+ ((UID !== msgExtra)?msgExtra:""), iExitCode, res);
}
/** closure callback that reads tail of file to extrapolate version info */
function cbcFileDBRead(sFile)
{
return function (err, bytesRead, sTail)
{
var sVer = sTail.toString();
var aClean = sVer.split(" Copyright (c) ")[0].split(" ");
aDBsVs[aDBs.indexOf(sFile)] = "GEO"+aClean[0].split("GEO")[1]+" "+aClean[1]+" "+aClean[2]+" "+aClean[3];
};
}
/** closure callback open file for subsequent read of tail */
function cbcFileReadTail(sFile, iFileSize)
{
return function(err, fd)
{
var sTail = new Buffer(81);
var iB = iFileSize-88;
//noinspection JSUnresolvedFunction
mFS.read(fd, sTail, 0, 81, iB, cbcFileDBRead(sFile));
};
}
/** closure callback to get FileStatus for DB related reads */
function cbcFileStat(sFile)
{
return function(err, stats)
{
if (err) { log(sCR+"ERROR:"+sCN+" unable to get DB version for :"+sFile+" "+sCDG+err.toString()+sCN); return; }
var iFileSize = stats["size"];
//var fd =
mFS.open(sFile, "r", cbcFileReadTail(sFile, iFileSize));
};
}
/** asynchronously read all DB files */
function UpdateDBVersions()
{ /* async invoke all reads needed to get files */
for (var iX=0; iX < aDBs.length; ++iX)
{ //noinspection JSUnresolvedFunction
mFS.stat(aDBs[iX], cbcFileStat(aDBs[iX]));
}
}
/** Returns All Version info for Maxmind files which should have already been gathered */
function ReturnMaxMindDBVersions()
{
var sReturn = [];
for (var iX=0; iX < aDBs.length; ++iX)
{
var x = aDBs[iX].split("/")[aDBs[iX].split("/").length-1];
var oP=[];
oP.push(x);
oP.push(aDBsVs[iX]);
sReturn.push(oP);
}
return sReturn;
}
/** @return {string} */
function XInfo()
{
/* TSR MODE get this async for later use */
var sDbs = ReturnMaxMindDBVersions();
//noinspection JSUnresolvedVariable
var sReturn = "node.js: "+process.versions.node + EOL;
//noinspection JSUnresolvedVariable
sReturn+= "v8: "+process.versions.v8 + EOL;
//noinspection JSValidateTypes,JSUnresolvedFunction
sReturn+=mOS.type()+" ("+mOS.platform()+") "+mOS.release()+EOL;
//noinspection JSUnresolvedFunction,JSUnresolvedVariable
sReturn+="CPU: " + mOS.cpus().length+"x "+mOS.cpus()[0].model+" "+mOS.cpus()[0].speed+" MHz"+EOL;
//noinspection JSUnresolvedFunction
sReturn+="memory-all: "+mOS.totalmem()+" (bytes)"+EOL;
//noinspection JSUnresolvedFunction
sReturn+="Memory-free: "+mOS.freemem()+" (bytes)"+EOL;
sReturn+=sDbs.join(EOL)+EOL;
return sReturn;
}
/** Quit / Ending Message to display for time taken, etc. @param Number integer exit code. */
function clDestruct(iCode)
{ /* on definable exit codes show msg */
return function(i)
{
var sQUIT = "";
if (UID === iCode) return;
if (0 !== iCode && 0 !== i)
{
bTTY = isTerminal();
setColours();
sQUIT= 0 !== iCode ? EOL+"EXITING with: "+sCC+sEXITs[iCode]+sCN+" code: "+sCC+iCode : ""+sCN;
sQUIT+=EOL+"TSR "+sCNB+"Time"+sCN+" in Seconds: " +sCNB+(Date.now()/1000-f_SysT1).toString()+sCN;
}
if (!bQuiet && bHTTP) { log(sQUIT); }
process.exit(0);
}
}
/** approximate seperation between to IP's based on locations */
function calculateDistances(aRet)
{
for (var iX=0; iX < aRet.length; ++iX)
{
if (0 === iX%2)
{
if (UID !== aRet[iX + 1])
{
var sKm = aRet[iX].location.distance(aRet[iX+1].location);
aRet[iX].distance = { "km" : sKm, "to_ip" : aRet[iX+1].ip };
aRet[iX+1].distance = { "km" : sKm, "to_ip" : aRet[iX].ip };
}
}
}
}
/**---------------------------------------
* MAIN: Initialiser Function.
* loads former files collected on last run
* or before stop. @param Object request.
*---------------------------------------*/
function initLoad()
{
var a2Check = [];
var iX; // GENERAL COUNTER
var sPathSaveFile;
var bNewDBPath = false;
for (iX = 0; iX < sEXITs.length; ++iX) { process.on(sEXITs[iX], clDestruct(iX) ); }
/* where -v || --version || /? is present */
if (-1 !== aH.indexOf("-v") || -1 !== aH.indexOf("--version") || -1 !==aH.indexOf("/v")) { log(sVERSION, 0); }
if (2 === aH.length && !module.parent){ showHelp("", 0); }
/* where '-h' or '--help' or '/?' is present */
if (-1 !== aH.indexOf("-h") || -1 !== aH.indexOf("--help") || -1 !==aH.indexOf("/?")) { showHelp("", 0); }
for (iX=2; iX < aH.length; ++iX)
{
// IPv4 or IPv6 for match ealier - if not then gibberish so exit
if ( rgxIP.test(aH[iX]) || rgxIPv6.test(aH[iX]) )
{
a2Check.push({ "ip": aH[iX], "v6": rgxIPv6.test(aH[iX])});
continue;
}
var iBeforeCheck = iArgsIn;
for (var iY=0; iY < args.length; ++iY)
{
var sArgument = aH[iX];
/* check for equal (=) type arguments */
if (-1 !== sArgument.indexOf("="))
{
sArgument = aH[iX].split("=")[0]+"=";
if (sArgument === "-b=" || sArgument === "--backups=" || sArgument === "/b=")
{
sDBPath = aH[iX].split("=")[1];
if (UID === sDBPath)
{ log(sCR+EOL+sArgument+sCN+" <- is empty! MUST refernce valid path eg: -b=/home/user/vipi_db/"+EOL, 11); }
else
{
if ("/" !== sDBPath[sDBPath.length-1])
{ //noinspection JSUnresolvedVariable
sDBPath+="/";
}
sDB = sDBPath; // set path for DB to whats prefered.
}
++iArgsIn; break;
}
else if (sArgument === "-sf=" || sArgument === "--savefile=" || sArgument === "/sf=")
{
sPathSaveFile = aH[iX].split("=")[1];
if (UID === sPathSaveFile)
{ log(sCR+EOL+sArgument+sCN+" <- is empty! MUST refernce valid file eg: -sf=visitor_id1.json"+EOL, 13); }
++iArgsIn; break;
}
else if (sArgument === "-skc=" || sArgument === "--skclf=" || sArgument === "/skc=")
{
var sKeyClf = aH[iX].split("=")[1];
if (UID === sKeyClf)
{ log(sCR+EOL+sArgument+sCN+" <- is empty! MUST contain valid string."+EOL, 13); }
aClfSaveKeys.push(sKeyClf);
++iArgsIn; break;
}
else if (sArgument === "-sko=" || sArgument === "--skobj=" || sArgument === "/sko=")
{
var sKeyObj = aH[iX].split("=")[1];
if (UID === sKeyObj || "" === sKeyObj)
{ log(EOL+sArgument+sCN+" <- is empty! MUST contain valid object json string."+EOL, 14); }
try
{
var oKeyObj = JSON.parse(sKeyObj);
if ( "object" !== typeof(oKeyObj) || null === oKeyObj )
{ log(sCR+EOL+"ERROR: "+sCN+sArgument+"'"+sCNB+sKeyObj+sCN+"' is NOT a valid JSON object."+EOL, 15); }
aObjSaveKeys.push(oKeyObj);
++iArgsIn; break;
}
catch(e) { log(sCR+EOL+"ERROR: "+sCN+sArgument+"'"+sCNB+sKeyObj+sCN+"' is NOT a valid JSON object."+EOL, 16); }
}
else if (sArgument === "-dp=" || sArgument === "--dipport=" || sArgument === "/dp=")
{
var sExts = aH[iX].split("=")[1];
if (UID === sExts)
{ log(sCR+EOL+sArgument+sCN+" <- is empty! MUST reference a valid IP:PORT of an adaptor to bind to."+EOL, 17); }
else
{ // check whether valid IPv4 or IPv6 address is passed
var bIPv4 = rgxIPPORT.test(sExts);
var bIPv6 = rgxIPv6PORT.test(sExts);
if (!bIPv4 && !bIPv6)
{
log(sCR+EOL+"Invalid Address: "+ sCN + sCNB + sExts + sCN + " <- Require VALID IPv4 or IPv6."+EOL+sCN, 18);
}
sPORT = sExts.split(":")[sExts.split(":").length-1];
if (!bIPv6) { sIP = sExts.split(":").splice(0, sExts.split(":").length-1).join(""); }
else { sIP = sExts.substring(0, sExts.indexOf(":"+sPORT)); }
args[6].push(true);
}
++iArgsIn; break;
}
else { log(EOL+sCR+sArgument+sCN + sCNB + "<- equative argument not supported!\n"+sCN, 19); }
}
if (-1 !== args[iY][0].indexOf(sArgument)) { args[iY].push(true); ++iArgsIn; break; }
}
if (iBeforeCheck === iArgsIn)
{ //process.argv.splice(2, 7, 0)
log(EOL+"Invalid parameter: '"+sCR+sCNB+ process.argv.splice(2, 7, 0)+sCN+"'"+EOL, 1);
}
}
/* MODE: -nu && --dbu CLASHING Update & no update flags*/
if (UID !== args[25][2] && UID !== args[19][2])
{
var msg = sCR+EOL+"Invalid / Clashing options: "+sCN+sCNB+args[25][0]+ " , " +args[19][0] + sCN + "; can NOT be in NO-Update & Update modes at once."+EOL+sCN;
log(msg, 20);
}
/* MODE: -q || --quiet (18) */
if (UID !== args[26][2]) { bQuiet = true; }
try
{ //noinspection JSUnresolvedFunction
mFS.accessSync(sDB);
//noinspection JSUnresolvedFunction
var files = mFS.readdirSync(sDB);
var Found = files.indexOfAll(".dat.gz");
if ("object" === typeof(Found) && UID !== Found.length)
{
if (6 !== Found.length) { bNewDBPath = true; }
else
{
if
(
!(
-1<files.indexOf(aDBs[0]) || -1<files.indexOf(aDBs[1])||-1<files.indexOf(aDBs[2]) ||
-1<files.indexOf(aDBs[3])|| -1<files.indexOf(aDBs[4]) || -1<files.indexOf(aDBs[5])
)
)
{ bNewDBPath = true; }
}
}
else { bNewDBPath = true; }
}
catch(e)
{ // in CLI mode with single lookups
if (!(a2Check.length !== 0 && UID === args[4][2] && UID === args[6][2] && UID === sDBPath))
{
if (!(3 === aH.length && UID !== args[25][2]) || (UID !== args[4][2] || UID !== args[6][2]))
{
try
{ //noinspection JSUnresolvedFunction
mFS.mkdirSync(sDB);
bNewDBPath = true;
}
catch(e2)
{
log(sCR+EOL+sDB+sCN+" <- Maxmind DB Path is not valid or accessible!"+EOL+sCDG+e.toString()+sCN, 12);
}
}
}
}
// when not in deamon modes and not using custom paths use default dbs shipped with module.
if (a2Check.length !== 0 && UID === args[4][2] && UID === args[6][2] && UID === sDBPath || (3 === aH.length && UID !== args[25][2]))
{
sDB= __dirname+"/vipi_dbs/";
}
for (iX=0; iX < aDBs.length; ++iX) { aDBs[iX] = sDB+aDBs[iX]; }
try
{ /* MODE: -d || --daemon || -dp || --dipport modes (3,4) */
if (UID !== args[4][2] || UID !== args[6][2]) { log(stringHeader()+sMsgWelcome); }
// Custom path & noupdate disabled by default.
if (UID !== args[25][2] || (UID === args[19][2] && bNewDBPath && UID !== sDBPath) || (UID !== args[4][2] || UID !== args[6][2]))
{
var sExec = "vipi_files -i "+sDB;
if (bQuiet) { sExec+=" -q"; }
var psOptions = { stdio: [0, 1, process.stdout], cwd : process.cwd(), env: {"LC_ALL":"C"} };
try
{ //noinspection JSUnresolvedFunction
mPS.execSync(sExec, psOptions);
if (UID !== args[4][2] || UID !== args[6][2] || UID !== sDBPath || (3 === aH.length && UID !== args[25][2]))
{
log("Successfully installed / updated files at path: "+sDB);
}
}
catch(e)
{
bQuiet = false;
log(EOL+sCR+"ERROR: "+sCN+"Could not initiate DB files at: "+sDBPath+EOL+sCDG+e.toString()+sCN, 13);
}
}
/* MODE: Update / Custome install only */
if (UID === args[19][2] && (3 === aH.length || (4 === aH.length && UID !== sDBPath)))
{
if (UID !== args[4][2] && UID !== args[6][2] ) { log("\nInstalled / Updated Files to: "+sDB+EOL); }
}
if (0 !== a2Check.length || (UID !== args[4][2] || UID !== args[6][2]) || module.parent)
{
//noinspection JSUnresolvedFunction
mMAX.init(aDBs, {"indexCache": true, "checkForUpdates": true});
if (UID !== sDBPath) { log(EOL+"Using custom DB path: "+sDB+EOL); }
}
}
catch(err)
{
bQuiet = false;
log(EOL+sCR+"ERROR: "+sCN+"loading Maxmind DB iles!"+EOL+sCDG+err.toString()+EOL+sCN, 20);
}
if ( 3 === aH.length && UID !== args[2][2] ){ log(EOL+"Succesfully tested Maxmind DB loading from custom path: "+sCNB+sDBPath+sCN+EOL, 0); }
/* MODE: -tz || --timezone (27) */ // if (UID !== args[27][2]) { bNoTimeZone = false; }
/* MODE: -asn || --asnumber (2) */ // if (UID !== args[1][2]) { bNoASN = false; }
/* MODE: -xi | --xinfo modes (20) */
if (UID !== args[28][2]){ UpdateDBVersions(); setTimeout(function (){ log(EOL+XInfo(), 0); }, 48); }
if (0 < a2Check.length)
{
var aRet = [];
for (iX=0; iX < a2Check.length; ++iX)
{
var oRet = {};
oRet.ip = a2Check[iX].ip;
oRet.date = new Date();
if (a2Check[iX].v6)
{ //noinspection JSUnresolvedFunction
oRet.location = mMAX.getLocationV6(a2Check[iX].ip);
}
else
{ //noinspection JSUnresolvedFunction
oRet.location = mMAX.getLocation(a2Check[iX].ip);
}
if (null === oRet.location || UID === oRet.location)
{ /* Dont write ... return. */
log(sCR+"ERROR: "+sCN+a2Check[iX].ip+sCNB+" invalid address."+EOL+sCN, UID, 1);
}
if (!bNoASN)
{ //noinspection JSUnresolvedFunction
oRet.asn = (a2Check[iX].v6) ? mMAX.getOrganizationV6(oRet.ip) : mMAX.getOrganization(oRet.ip);
}
if (!bNoTimeZone)
{ //noinspection JSUnresolvedVariable
oRet.timeZone = mMAXTZ(oRet.location.countryCode, oRet.location.region);
if (UID === oRet.timeZone) { oRet.timeZone="unrecognised-timezone"; }
}
if (UID !== typeof(aObjSaveKeys[iX])) { oRet.keyobject = aObjSaveKeys[iX]; }
if (UID !== typeof(aClfSaveKeys[iX])) { oRet.keyclf = aClfSaveKeys[iX]; }
aRet.push(oRet);
}
/* MODE: -cd || --cdistance (2) */
if (UID !== args[3][2]) { calculateDistances(aRet); }
/* MODE: -sa || --saveauto (19) | -sf || --savefile (20) */
if (UID !== args[20][2] || (UID !== args[21][2]))
{
var aCLF = [];
if (0 < aRet.length)
{
aCLF = [aRet.length+1];
for (iX=0; iX < aRet.length; ++iX)
{
var dX = aRet[iX].date;
var sDate = dX.getDate() + "/" + aMonths[dX.getMonth()]+ "/" + dX.getFullYear();
sDate+= ":" + dX.getHours() + ":" + pad(dX.getMinutes(), 2, true) + ":" + pad(dX.getSeconds(), 2, true);
sDate+= " " + pad(dX.getTimezoneOffset()/60*-100, 4, false);
if (UID !== a2Check[iX].keyclf) { aRet[iX].keyclf = a2Check[iX].keyclf; }
var sCLF = aRet[iX].keyclf ? " " + aRet[iX].keyclf : "";
//noinspection JSUnresolvedVariable
var sLocation = UID !== aRet[iX].location ?
" \"" + aRet[iX].location.countryCode + "/" + aRet[iX].location.city+"/" +
aRet[iX].location.latitude + "/" +aRet[iX].location.longitude+"\""
: "";
aCLF[iX] = aRet[iX].ip + " ["+sDate+"]" + sCLF;
aCLF[iX]+= UID !== aRet[iX].ua ? " \"" + aRet[iX].ua +"\"" : "";
aCLF[iX]+= UID !== sLocation ? sLocation : "";
aCLF[iX]+= UID !== aRet[iX].timeZone ? " \""+aRet[iX].timeZone+"\"" : "";
aCLF[iX]+= UID !== aRet[iX].asn ? " \""+aRet[iX].asn+"\"" : "";
aCLF[iX]+= "\n";
}
}
// -sa || --saveauto
if (UID !== args[20][2])
{
if (!args[17][2]) { writeCLFFile(aCLF); }
/** remove keyclf as its redundant in object */
for (iY=0; iY < aRet.length; ++iY) { delete(aRet[iY].keyclf); }
writeOBJFile(aRet);
}
else
{
if (!args[18][2]) { writeCLFFile(aCLF, sPathSaveFile); }
/** remove keyclf as its redundant in object */
for (iY=0; iY < aRet.length; ++iY) { delete(aRet[iY].keyclf); }
writeOBJFile(aRet, sPathSaveFile);
}
}
bQuiet = false;
// REMOVE EXIT CODE FOR COMBINED MODE
log(JSON.stringify(aRet, null, " "));
}
/* MODE: -d || --daemon || -dp || --dipport modes (3,4) */
if (UID !== args[4][2] || UID !== args[6][2] )
{
bHTTP = true;
bTTY = false; //appHeaderString();
/** MODE: -dad" || --dallowdelete */
bDeleteAccess = UID !== args[5][2];
/** MODE: -dnw || --dnowrite */
bWriteAccess = UID === args[10][2];
/** MODE: -dsa || --dsaveauto */
bLogToFile = UID !== args[11][2];
/** MODE: -dur || --dsaverefering */
bLogAll = UID !== args[17][2];
/** MODE: -dnr || --dnoread */
bReadAccess = UID === args[9][2];
/** MODE: -dxq || --dxquery */
bNoIPQuiry = UID !== args[15][2];
try
{
mHTTP = require("http");
//noinspection JSUnresolvedFunction
oHTTP = mHTTP.createServer(HttpHandle).listen(sPORT, sIP);
oHTTP.on("error", function(e)
{
bHTTP = false;
if ("EACCES" === e.code || "EADDRINUSE" === e.code)
{
log(sCR+"ERROR:"+sCN+" Can not bind to requested: "+sCNB+sIP+":"+sPORT+sCN+EOL+sCDG+e.toString()+sCN, 1);
}
});
UpdateDBVersions();
if (UID === args[19][2]) { enableupdates(); }
if (!bQuiet)
{
var fSecs=Date.now()/1000-f_SysT1;
console.log(sMsgInit, fSecs, sL);
//noinspection JSUnusedAssignment
bTTY = isTerminal();
setColours();
log("Server running at "+sCW+sCNB+"http://"+sIP+sCN+":"+sCNB+sCW+sPORT+"/"+sCN);
bTTY=false;
setColours();
}
}
catch (e) { log(e.toString()); }
}
}
function HttpHandle(req, res)
{ /* 200 back if requested method is not GET /... */
res.hasheaderset = false;
//noinspection JSUnresolvedVariable
var _GET = mURL.parse(req.url,true).query;
/* MODE: -djs || --djson - OVERLOOK RIGHTS */
bJson = (UID === _GET["!_js"] && UID === _GET["!_json"]) ? UID !== args[6][2] : true;
if (bJson) { bJson = (UID === _GET["!_pt"] && UID === _GET["!_plaintext"]) ? UID !== args[6][2] : false; }
/* MODE: -dno || --dnooutput - OVERLOOK RIGHTS */
bNoReturn = UID === _GET["!_nr"] && UID === _GET["!_noreturn"] ? UID !== args[8][2] : true;
/* MODE: -nc || --noclf (9) */
bNoCLF = UID !== bWriteAccess ? !(UID === _GET["!_nc"] && UID === _GET["!_noclf"]) : UID !== args[18][2];
/* MODE: -dxa ||--dxasnumber (11) */
bNoASN = bWriteAccess ? !(UID === _GET["!_as"] && UID === _GET["!_asnumber"]) : UID !== args[12][2];
/* MODE: -dxl || --dxlocation (12) */
bNoLocation = bWriteAccess ? !(UID === _GET["!_l"] && UID === _GET["!_location"]) : UID !== args[13][2];
/* MODE: -dxt || --dxtimezone(13) */
bNoTimeZone = bWriteAccess ? !(UID === _GET["!_tz"] && UID === _GET["!_timezone"]) : UID !== args[14][2];
/* MODE: -dxu" || --dxua (15) */
bNoUA = bWriteAccess ? !(UID === _GET["!_ua"] && UID === _GET["!_useragent"]) : UID !== args[16][2];
/** indicate all deletion action */
var bDeleteAll = bDeleteAccess && (UID !== _GET["!_da"] || UID !== _GET["!_deleteall"]);
var bOUTPUT = false;
var bObjFileWrite = false;
var aCLF = [];
var iX, iY=0; //GENERAL COUNTERS
var a2s;
IPs = bNoIPQuiry ? UID : _GET["!_"];
aIPs = [];
var a2Check = [];
/** CLI specific arrays */
aObjSaveKeys = [];
var xff = req.headers["x-forwarded-for"];
var xc = req.headers["x-client"];
oRHeader =
{
"Content-Type": bJson ? "application/json" : "text/plain",
"Cache-Control": "no-cache", "Transfer-Encoding": "chunked", "Connection": "close"
};
if (UID !== req.headers["host"])
{
sIP = req.headers["host"];
var iPortPos = sIP.lastIndexOf(":"+sPORT);
if (-1 !== iPortPos && (sPORT.length === sIP.length-(iPortPos+1))) { sIP = sIP.substring(0, iPortPos); }
}
if ("GET" !== req.method)
{ /* reduce header chunk indicator not needed. */
res.hasheaderset = true;
//noinspection JSUnresolvedFunction
res.removeHeader("transfer-encoding");
res.end(0);
}
/* default GET / then show help. */
if (!bNoReturn && 0 === Object.keys(_GET).length || (!bLogAll && (UID !== _GET["!_h"] || UID !== _GET["!_help"])))
{
showHelp(UID, UID, res); return;
}
/* !_xi or !_xinfo return enviroment info discarding all else. */
if (bReadAccess && UID !== _GET["!_xi"] || UID !== _GET["!_xinfo"])
{
var sReturn = XInfo();
delete(oRHeader["Transfer-Encoding"]);
oRHeader["Content-Length"] = sReturn.length;
log(sReturn, UID, res);
return;
}
/* !_ls or !_listsaves pass to function to return listing. */
if (bReadAccess && UID !== _GET["!_ls"] || UID !== _GET["!_listsaves"]) { returnFilesList(res); return; }
if (bReadAccess && UID !== _GET["!_ra"] || UID !== _GET["!_readauto"])
{
if ("" !== _GET["!_ra"] && "" !== _GET["!_readauto"])
{
log(sCR+"ERROR: "+sCN+sCNB+"!_ra="+_GET["!_ra"]+sCN+" <- equative argument not valid."+EOL, UID, res);
}
else if (UID !== _GET["!_rf"] || UID !== _GET["!_readfile"])
{
log(sCR+"ERROR: "+sCN+"Can not have both !_rf & !_ra"+EOL, UID, res);
}
else { bOUTPUT = bReadAccess; }
}
if (bReadAccess && UID !== _GET["!_rf"] || UID !== _GET["!_readfile"])
{
if
(
("" === _GET["!_rf"] || "string" !== typeof(_GET["!_rf"])) &&
("" === _GET["!_readfile"] || "string" !== typeof(_GET["!_readfile"]))
)
{
log(sCR+"ERROR: "+sCN+"MUST provide filename with !_rf of log eg !_rf=bla.log"+EOL, UID, res);
}
else { bOUTPUT = bReadAccess; }
}
if (bWriteAccess && UID !== _GET["!_sa"] || UID !== _GET["!_saveauto"])
{
if ("" !== _GET["!_sa"] && "" !== _GET["!_saveauto"] )
{
log(sCR+"ERROR: "+sCN+"!_sa=... <- equative argument not valid."+EOL, UID, res);
}
else { bLogToFile = bWriteAccess; }
}
if (bWriteAccess && UID !== _GET["!_sf"] || UID !== _GET["!_savefile"])
{
if
(
("" === _GET["!_sf"] || "string" !== typeof(_GET["!_sf"])) &&
("" === _GET["!_savefile"] || "string" !== typeof(_GET["!_savefile"]))
)
{
log(sCR+"ERROR: "+sCN+"MUST provide filename with !_sf of log eg !_sf=SESSION-ID"+EOL, UID, res);
}
else { bObjFileWrite = bWriteAccess; }
}
if (bDeleteAll && ( bOUTPUT && ( UID === _GET["!_nr"] && UID === _GET["!_noreturn"])))
{
log(sCR+"ERROR: "+sCN+"Can not DELETE-ALL & perform other operations inline."+EOL, UID, res);
}
if (bLogAll)
{
if ((UID === IPs || "" === IPs) && true !== bOUTPUT)
{
if (UID === xff && UID === xc)
{ //noinspection JSUnresolvedVariable
IPs = req.connection.remoteAddress;
}
else { IPs = UID === xc ? xff : xc; }
}
}
if ((UID === IPs || "" === IPs) && true !== bOUTPUT)
{ log(sCR+"ERROR: "+sCN+"Can not have blank / empty request. IP's eg: !_=8.8.8.8", UID, res); }
/* single or comma separated */
else if ("string" === typeof(IPs))
{
if ("ua" === IPs)
{
if (UID === xff && UID === xc)
{ //noinspection JSUnresolvedVariable
IPs = req.connection.remoteAddress;
}
else { IPs = UID === xc ? xff : xc; }
}
if (-1 !== IPs.indexOf(","))
{
a2s = IPs.split(",");
for (iX=0; iX < a2s.length; ++iX)
{
if (rgxIP.test(a2s[iX]) || rgxIPv6.test(a2s[iX]))
{
a2Check.push({ "ip": a2s[iX], "v6": rgxIPv6.test(a2s[iX])});
}
else { log(sCR+"ERROR: "+sCN+sCNB+a2s[iX]+sCN+" invalid IPv4/IPv6 address."+EOL, UID, res); }
}
}
else
{ //noinspection JSCheckFunctionSignatures
if (rgxIP.test(IPs) || rgxIPv6.test(IPs))
{ //noinspection JSCheckFunctionSignatures
a2Check.push({"ip": IPs, "v6": rgxIPv6.test(IPs)});
}
else { log(sCR+"ERROR: "+sCN+sCNB+IPs+sCN+" invalid IPv4/IPv6 address."+EOL, UID, res); }
}
}
else if ("object" === typeof(IPs))
{
for (var iI in IPs)
{
if (!IPs.hasOwnProperty(iI)) { continue; }
if (rgxIP.test(IPs[iI]) || rgxIPv6.test(IPs[iI]))
{
a2Check.push({"ip":IPs[iI], "v6": rgxIPv6.test(IPs[iI])});
}
else
{
log("ERROR: "+IPs[iI]+" invalid IPv4/IPv6 address."+EOL, UID, res);
}
}
}
if (bWriteAccess && UID !== _GET["!_skc"] || UID !== _GET["!_skclf"])
{
if (0 === a2Check.length)
{
log(sCR+"ERROR:"+sCN+" MUST specify IP address with !_=... currently: !_ is invalid or empty."+EOL, UID, res);
}
else
{
var SKs = UID !== _GET["!_skc"] ? _GET["!_skc"] : _GET["!_savekeyclf"];
if (("" === SKs || ("string" !== typeof(SKs)) && "object" !== typeof(SKs)))
{ log(sCR+"ERROR:"+sCN+" MUST provide key value eg: !_skc=something"+EOL, UID, res); }
else
{
if ("string" === typeof(SKs))
{
if (-1 !== SKs.indexOf(","))
{
a2s = SKs.split(",");
for (iX=0; iX < a2s.length; ++iX)
{
if (UID !== a2Check[iX]) { a2Check[iX].keyclf = decodeURIComponent(a2s[iX]); }
else { log(sCR+"ERROR: "+sCN+sCNB+a2s[iX]+sCN+" key invalid to index: "+iX+EOL, UID, res); }
}
}
else { a2Check[0].keyclf = decodeURIComponent(SKs); }
}
else
{
a2s = SKs;
for (iX=0; iX < a2s.length; ++iX)
{
if (UID !== a2Check[iX]) { a2Check[iX].keyclf = decodeURIComponent(a2s[iX]); }
else { log(sCR+"ERROR: "+sCN+sCNB+a2s[iX]+sCN+" key invalid to index: "+iX+EOL, UID, res); }
}
}
}
}
}
if (bWriteAccess && UID !== _GET["!_sko"] || UID !== _GET["!_skobj"])
{
if (0 === a2Check.length)
{
log(sCR+"ERROR:"+sCN+" MUST specify IP address with !_=... currently: !_ is invalid or empty."+EOL, UID, res);
}
else
{
var SKo = UID !== _GET["!_sko"] ? _GET["!_sko"] : _GET["!_skobj"];
if (("" === SKo || ("string" !== typeof(SKo)) && "object" !== typeof(SKo)))
{ log(sCR+"ERROR:"+sCN+" MUST provide key value eg: !_skc=something"+EOL, UID, res); }
if ("string" === typeof(SKo))
{
if (-1 !== SKo.indexOf(","))
{
a2s = SKo.split(",");
for (iX=0; iX < a2s.length; ++iX)
{
if (UID !== a2Check[iX]) { a2Check[iX].keyobject = a2s[iX]; }
else { log(sCR+"ERROR: "+sCN+sCNB+a2s[iX]+sCN+" key invalid to index: "+iX+EOL, UID, res); }
}
}
else { a2Check[0].keyobject = SKo; }
}
else
{
a2s = SKo;
for (iX=0; iX < a2s.length; ++iX)
{
if (UID !== a2Check[iX]) { a2Check[iX].keyobject = a2s[iX]; }
else { log(sCR+"ERROR: "+sCN+sCNB+a2s[iX]+sCN+" key invalid to index: "+iX+EOL, UID, res); }
}
}
}
}
/* Do Lookup storing !_sk, !_sa or !_sf if applicable */
var aRet = [];
for (iX=0; iX < a2Check.length; ++iX)
{
var oRet = {};
oRet.ip = a2Check[iX].ip;
oRet.date = new Date();
if (a2Check[iX].v6)
{ //noinspection JSUnresolvedFunction
oRet.location = mMAX.getLocationV6(a2Check[iX].ip);
}
else
{ //noinspection JSUnresolvedFunction
oRet.location = mMAX.getLocation(a2Check[iX].ip);
}
if (null === oRet.location)
{ /* Dont write ... return. */
log(sCR+"ERROR: "+sCN+sCNB+a2Check[iX].ip+sCN+" invalid address."+EOL, UID, res);
break;
}
if
(
UID !== _GET["!_sua"] || UID !== _GET["!_saveua"] || !bNoUA &&
((bWriteAccess && (UID === _GET["!_nu"] && UID === _GET["!_noua"])) || !bWriteAccess)
)
{
oRet.ua = UID === req.headers["user-agent"] ? "" : req.headers["user-agent"];
}
else
{
if (!bNoUA && bWriteAccess && (UID === _GET["!_nu"] && UID === _GET["!_noua"]))
{
oRet.ua = UID === oRet.ua ? "" : oRet.ua;
}
}
if (!bNoTimeZone || (bWriteAccess && (UID !== _GET["!_tz"] || UID !== _GET["!_timezone"])))
{ //noinspection JSUnresolvedVariable
oRet.timeZone = mMAXTZ(oRet.location.countryCode, oRet.location.region);
if (UID === oRet.timeZone) { oRet.timeZone="unrecognised-timezone"; }
}
if (!bNoASN || (bWriteAccess && (UID !== _GET["!_as"] || UID !== _GET["!_asnumber"])))
{ //noinspection JSUnresolvedFunction
oRet.asn = (a2Check[iX].v6) ? mMAX.getOrganizationV6(oRet.ip) : mMAX.getOrganization(oRet.ip);
}
if (a2Check[iX].keyobject)
{
try
{
oRet.keyobject = JSON.parse(decodeURIComponent(a2Check[iX].keyobject));
if ("object" !== typeof(oRet.keyobject) || null === oRet.keyobject)
{ log(sCR+"ERROR: "+sCN+sCNB+a2Check[iX].keyobject+sCN+" is NOT a valid JSON object."+EOL, UID, res); }
// else { aObjSaveKeys.push(oRet.keyobject); }
}
catch(e)
{
log(sCR+"ERROR: "+sCN+sCNB+a2Check[iX].keyobject+sCN+" is NOT a valid JSON object."+EOL, UID, res);
break;
}
}
if (bNoLocation) { delete(oRet.location); }
if (a2Check[iX].keyclf) { oRet.keyclf = a2Check[iX].keyclf; }
aRet.push(oRet);
}
if (bWriteAccess && (UID === _GET["!_cd"] || UID === _GET["!_cdistance"])) { calculateDistances(aRet); }
if (0 < aRet.length && (bLogToFile || bWriteAccess || bObjFileWrite))
{
aCLF = [aRet.length+1];
for (iX=0; iX < aRet.length; ++iX)
{
var dX = aRet[iX].date; //console.log("%s/%s/%s:%s:%s:%s %s", dX.getDate(), aMonths[dX.getMonth()], dX.getFullYear(), dX.getHours(), dX.getMinutes(), dX.getSeconds(), pad(dX.getTimezoneOffset()/60*-100, 4) );
var sDate = dX.getDate() + "/" + aMonths[dX.getMonth()]+ "/" + dX.getFullYear();
sDate+= ":" + dX.getHours() + ":" + pad(dX.getMinutes(), 2, true) + ":" + pad(dX.getSeconds(), 2, true);
sDate+= " " + pad(dX.getTimezoneOffset()/60*-100, 4, false);
if (UID !== a2Check[iX].keyclf) { aRet[iX].keyclf = a2Check[iX].keyclf; }
var sCLF = aRet[iX].keyclf ? " " + aRet[iX].keyclf : "";
//noinspection JSUnresolvedVariable
var sLocation = UID !== aRet[iX].location ?
" \"" + aRet[iX].location.countryCode + "/" + aRet[iX].location.city+"/" +
aRet[iX].location.latitude + "/" +aRet[iX].location.longitude+"\""
: "";
aCLF[iX] = aRet[iX].ip + " ["+sDate+"]" + sCLF;
aCLF[iX]+= UID !== aRet[iX].ua ? " \"" + aRet[iX].ua +"\"" : "";
aCLF[iX]+= UID !== sLocation ? sLocation : "";
aCLF[iX]+= UID !== aRet[iX].timeZone ? " \""+aRet[iX].timeZone+"\"" : "";
aCLF[iX]+= UID !== aRet[iX].asn ? " \""+aRet[iX].asn+"\"" : "";
aCLF[iX]+= "\n";
}
}
if (bDeleteAll || (bDeleteAccess && (UID !== _GET["!_df"] || UID !== _GET["!_deletefile"])) )
{
if (bDeleteAll)
{
if (bNoReturn) { deleteOBJFile(UID); log("", UID, res); }
else
{
if (!bOUTPUT){ deleteOBJFile(res); }
else { deleteOBJFile(UID); }
}
}
else
{
var toDel = UID !== _GET["!_df"] ? _GET["!_df"] : _GET["!_deletefile"];
deleteOBJFile(res, toDel);
}
}
else
{ // CUSTOME LOGS FOR USER DEFINED SESSIONS
if (0 < aRet.length && bWriteAccess && bObjFileWrite)
{
if (UID === _GET["!_nc"] && UID === _GET["!_noclf"] && !bNoCLF)
{ writeCLFFile(aCLF, (UID !== _GET["!_sf"] ? _GET["!_sf"] : _GET["!_savefile"]) + sUCLF); }
/** remove keyclf as its redudant in object */
if (UID !== _GET["!_sko"] || UID !== _GET["!_skobj"]) { for (iY=0; iY < aRet.length; ++iY) { delete(aRet[iY].keyclf); }}
writeOBJFile(aRet, (UID !== _GET["!_sf"] ? _GET["!_sf"] : _GET["!_savefile"]) + sUOBJ);
}
if (0 < aRet.length && bLogToFile)
{
if (!bWriteAccess) { writeCLFFile(aCLF); }
else { if (UID === _GET["!_nc"] && UID === _GET["!_noclf"] && !bNoCLF) { writeCLFFile(aCLF); } }
/** remove keyclf as its redundant in object */
for (iY=0; iY < aRet.length; ++iY) { delete(aRet[iY].keyclf); }
writeOBJFile(aRet); // if (UID !== _GET["!_sko"] || UID !== _GET["!_skobj"]) { }
}
if (UID !== aRet[0] && UID !== aRet[0].keyclf) { for (iY=0; iY < aRet.length; ++iY) { delete(aRet[iY].keyclf); } }
// where there are no-return parameters
if (0 < aRet.length && !bNoReturn)
{
if (!bOUTPUT) { log( (0 < aRet.length) ? JSON.stringify(aRet) : "", UID, res); }
else
{
if (UID !== _GET["!_ra"] || UID !== _GET["!_readauto"]) { returnOBJAutoFile(res); }
else { returnOBJFile(res, (UID !== _GET["!_rf"] ? _GET["!_rf"] : _GET["!_readfile"]) + sUOBJ); }
}
}
else
{
if (bNoReturn) { log("", UID, res); }
else
{
if (!bOUTPUT && 0 === aRet.length) { showHelp(UID, UID, res); }
else
{
if (UID !== _GET["!_ra"] || UID !== _GET["!_readauto"]) { returnOBJAutoFile(res); }
else if (UID !== _GET["!_rf"] || UID !== _GET["!_readfile"])
{
returnOBJFile(res, (UID !== _GET["!_rf"] ? _GET["!_rf"] : _GET["!_readfile"]) + sUOBJ);
}
else
{
if (!bDeleteAccess && ( UID !== _GET["!_df"] && UID !== _GET["!_deletefile"]))
{ log( (0 < aRet.length) ? JSON.stringify(aRet)+EOL : "", UID, res); }
}
}
}
}
}
}
/** Deletes JSON save sessions files based on the requested file or all available files.*/
function deleteOBJFile(res, file)
{
var iX=0; //GEMERAL COUNTER
try
{ //noinspection JSUnresolvedFunction
mFS.readdir(process.cwd(), function (err, files)
{
if (null !== err) { log ("ERROR: "+err.toString(), UID, res); }
else
{ /* log("Files list in current path "); log(files); */
var FoundOBJ = files.indexOfAll(UID === file ? sVOBJ : file+sUOBJ);
var FoundCLF = files.indexOfAll(UID === file ? sVCLF : file+sUCLF);
var FoundOBJUser = -1;
var FoundCLFUser = -1;
if (UID === file)
{
FoundOBJUser = files.indexOfAll(sUOBJ);
FoundCLFUser = files.indexOfAll(sUCLF);
}
if (-1 === FoundCLF && -1 === FoundOBJ && -1 === FoundCLFUser && -1 === FoundOBJUser )
{
log("No matching file(s) to delete.", UID, res);
}
else
{
var aDelete=[];
if ("number" === typeof(FoundOBJ) && -1 !== FoundOBJ) { aDelete.push(files[FoundOBJ]); }
if ("number" === typeof(FoundCLF) && -1 !== FoundCLF) { aDelete.push(files[FoundCLF]); }
if ("number" === typeof(FoundOBJUser) && -1 !== FoundOBJUser) { aDelete.push(files[FoundOBJUser]); }
if ("number" === typeof(FoundCLFUser) && -1 !== FoundCLFUser) { aDelete.push(files[FoundCLFUser]); }
if ("object" === typeof(FoundOBJ))
{
for (iX=0; iX < FoundOBJ.length; ++iX) { aDelete.push(files[FoundOBJ[iX]]); }
}
if ("object" === typeof(FoundCLF))
{
for (iX=0; iX < FoundCLF.length; ++iX) { aDelete.push(files[FoundCLF[iX]]); }
}
if ("object" === typeof(FoundOBJUser))
{
for (iX=0; iX < FoundOBJUser.length; ++iX) { aDelete.push(files[FoundOBJUser[iX]]); }
}
if ("object" === typeof(FoundCLFUser))
{
for (iX=0; iX < FoundCLFUser.length; ++iX) { aDelete.push(files[FoundCLFUser[iX]]); }
}
for (iX=0; iX < aDelete.length; ++iX)
{
try
{ //noinspection JSUnresolvedFunction
mFS.unlink(aDelete[iX], clDelete(aDelete.toString(), aDelete.length-1 === iX ? res : UID));
}
catch(err) { log("ERROR: "+err.toString()); }
}
}
}
});
}
catch (e2) { log(e2.toString()); log ("ERROR: could not get path - "+e2.toString(), UID, res); }
}
/** closure function for deleting a set.*/
function clDelete(sFiles, res)
{
return function(e)
{ log( (null !== e && UID !== e) ? "ERROR: deleting: "+ e.toString(): "Deleted file(s): "+sFiles, UID, res); };
}
/** List available JSON files related to user save sessions.*/
function returnFilesList(res)
{
try
{ //noinspection JSUnresolvedFunction
mFS.readdir(".", function (e, files)
{
if (null !== e) { log (e.toString(), UID, res); }
else
{
var aToReturn = [];
var Found = files.indexOfAll(sUOBJ);
if (-1 === Found) { aToReturn = "No User-Save files available."; }
else if ("number" === typeof(Found)) { aToReturn.push(files[Found]); }
else
{
for (var iX=0; iX < Found.length; ++iX) { aToReturn.push(files[Found]); }
}
delete(oRHeader["Transfer-Encoding"]);
oRHeader["Content-Length"] = JSON.stringify(aToReturn).length;
log(JSON.stringify(aToReturn), UID, res);
}
});
}
catch (ec2) { log("ERROR: could not obtain listing of user files.", UID, res); }
}
/** outputs as response the request JSON file if present - otherwise an error message */
function returnOBJFile(res, sFile)
{
try
{ //noinspection JSUnresolvedFunction
mFS.readFile(sFile, { "encoding" : "utf8" }, function(e2, d2)
{
if (null !== e2) { log("ERROR: "+sFile+ " could not be returned."+EOL+ e2.toString(), UID, res); }
else
{
var sReturn = "";
if (d2.length-1 === d2.lastIndexOf(",")) { sReturn = d2.substr(0, d2.length-1)+"]"; }
else if (d2.length-2 === d2.lastIndexOf(",")) { sReturn = d2.substr(0, d2.length-2)+"]"; }
else { sReturn = d2; }
log(sReturn, UID, res);
}
});
}
catch (ec2) { log("ERROR: "+sFile+ " could not be returned.", UID