Move Probe and ONVIF functions to separate lib (scanners.js)

merge-requests/210/head
Moe 2020-06-06 18:55:01 -07:00
parent 6a41e27908
commit a0f07406cc
4 changed files with 267 additions and 295 deletions

View File

@ -81,6 +81,8 @@ require('./libs/ffmpeg.js')(s,config,lang,function(ffmpeg){
require('./libs/customAutoLoad.js')(s,config,lang,app,io)
//scheduling engine
require('./libs/shinobiHub.js')(s,config,lang,app,io)
//onvif, ffprobe engine
require('./libs/scanners.js')(s,config,lang,app,io)
//scheduling engine
require('./libs/scheduler.js')(s,config,lang,app,io)
//on-start actions, daemon(s) starter

265
libs/scanners.js Normal file
View File

@ -0,0 +1,265 @@
var os = require('os');
var exec = require('child_process').exec;
var onvif = require("node-onvif");
module.exports = function(s,config,lang,app,io){
const activeProbes = {}
const runFFprobe = (url,auth,callback) => {
var endData = {ok: false}
if(!url){
endData.error = 'Missing URL'
callback(endData)
return
}
if(activeProbes[auth]){
endData.error = 'Account is already probing'
callback(endData)
return
}
activeProbes[auth] = 1
const probeCommand = s.splitForFFPMEG(`-v quiet -print_format json -show_format -show_streams -i "${url}"`).join(' ')
exec('ffprobe ' + probeCommand,function(err,stdout,stderr){
delete(activeProbes[auth])
if(err){
endData.error = (err)
}else{
endData.ok = true
endData.result = s.parseJSON(stdout)
}
endData.probe = probeCommand
callback(endData)
})
}
const runOnvifScanner = (options,foundCameraCallback) => {
var ip = options.ip.replace(/ /g,'')
var ports = options.port.replace(/ /g,'')
if(options.ip === ''){
var interfaces = os.networkInterfaces()
var addresses = []
for (var k in interfaces) {
for (var k2 in interfaces[k]) {
var address = interfaces[k][k2]
if (address.family === 'IPv4' && !address.internal) {
addresses.push(address.address)
}
}
}
const addressRange = []
addresses.forEach(function(address){
if(address.indexOf('0.0.0')>-1){return false}
var addressPrefix = address.split('.')
delete(addressPrefix[3]);
addressPrefix = addressPrefix.join('.')
addressRange.push(`${addressPrefix}1-${addressPrefix}254`)
})
ip = addressRange.join(',')
}
if(ports === ''){
ports = '80,8080,8000,7575,8081,554'
}
if(ports.indexOf('-') > -1){
ports = ports.split('-')
var portRangeStart = ports[0]
var portRangeEnd = ports[1]
ports = s.portRange(portRangeStart,portRangeEnd);
}else{
ports = ports.split(',')
}
var ipList = options.ipList
var onvifUsername = options.user || ''
var onvifPassword = options.pass || ''
ip.split(',').forEach(function(addressRange){
var ipRangeStart = addressRange[0]
var ipRangeEnd = addressRange[1]
if(addressRange.indexOf('-')>-1){
addressRange = addressRange.split('-');
ipRangeStart = addressRange[0]
ipRangeEnd = addressRange[1]
}else{
ipRangeStart = addressRange
ipRangeEnd = addressRange
}
if(!ipList){
ipList = s.ipRange(ipRangeStart,ipRangeEnd);
}else{
ipList = ipList.concat(s.ipRange(ipRangeStart,ipRangeEnd))
}
})
var hitList = []
ipList.forEach((ipEntry,n) => {
ports.forEach((portEntry,nn) => {
hitList.push({
xaddr : 'http://' + ipEntry + ':' + portEntry + '/onvif/device_service',
user : onvifUsername,
pass : onvifPassword,
ip: ipEntry,
port: portEntry,
})
})
})
var responseList = []
hitList.forEach(async (camera) => {
try{
var device = new onvif.OnvifDevice(camera)
var info = await device.init()
var date = await device.services.device.getSystemDateAndTime()
var stream = await device.services.media.getStreamUri({
ProfileToken : device.current_profile.token,
Protocol : 'RTSP'
})
var cameraResponse = {
ip: camera.ip,
port: camera.port,
info: info,
date: date,
uri: stream.data.GetStreamUriResponse.MediaUri.Uri
}
responseList.push(cameraResponse)
if(foundCameraCallback)foundCameraCallback(Object.assign(cameraResponse,{f: 'onvif'}))
}catch(err){
console.log(err)
s.debugLog(err)
}
})
return responseList
}
const runOnvifMethod = (onvifOptions,callback) => {
var response = {ok: false}
console.log(onvifOptions)
var errorMessage = function(msg,error){
response.ok = false
response.msg = msg
response.error = error
callback(response)
}
var actionCallback = function(onvifActionResponse){
response.ok = true
if(onvifActionResponse.data){
response.responseFromDevice = onvifActionResponse.data
}else{
response.responseFromDevice = onvifActionResponse
}
if(onvifActionResponse.soap)response.soap = onvifActionResponse.soap
callback(response)
}
var isEmpty = function(obj) {
for(var key in obj) {
if(obj.hasOwnProperty(key))
return false;
}
return true;
}
var doAction = function(Camera){
var completeAction = function(command){
if(command && command.then){
command.then(actionCallback).catch(function(error){
errorMessage('Device Action responded with an error',error)
})
}else if(command){
response.ok = true
response.repsonseFromDevice = command
callback(response)
}else{
response.error = 'Big Errors, Please report it to Shinobi Development'
callback(response)
}
}
var action
if(onvifOptions.auth.service){
if(Camera.services[onvifOptions.auth.service] === undefined){
return errorMessage('This is not an available service. Please use one of the following : '+Object.keys(Camera.services).join(', '))
}
if(Camera.services[onvifOptions.auth.service] === null){
return errorMessage('This service is not activated. Maybe you are not connected through ONVIF. You can test by attempting to use the "Control" feature with ONVIF in Shinobi.')
}
action = Camera.services[onvifOptions.auth.service][onvifOptions.auth.action]
}else{
action = Camera[onvifOptions.auth.action]
}
if(!action || typeof action !== 'function'){
errorMessage(onvifOptions.auth.action+' is not an available ONVIF function. See https://github.com/futomi/node-onvif for functions.')
}else{
var argNames = s.getFunctionParamNames(action)
var options
var command
if(argNames[0] === 'options' || argNames[0] === 'params'){
options = onvifOptions.options || {}
}
if(onvifOptions.auth.service){
command = Camera.services[onvifOptions.auth.service][onvifOptions.auth.action](options)
}else{
command = Camera[onvifOptions.auth.action](options)
}
console.log(Camera)
completeAction(command)
}
}
if(!s.group[onvifOptions.auth.ke].activeMonitors[onvifOptions.auth.id].onvifConnection){
//prepeare onvif connection
var controlURL
var monitorConfig = s.group[onvifOptions.auth.ke].rawMonitorConfigurations[onvifOptions.auth.id]
if(!monitorConfig.details.control_base_url||monitorConfig.details.control_base_url===''){
controlURL = s.buildMonitorUrl(monitorConfig, true)
}else{
controlURL = monitorConfig.details.control_base_url
}
var controlURLOptions = s.cameraControlOptionsFromUrl(controlURL,monitorConfig)
//create onvif connection
s.group[onvifOptions.auth.ke].activeMonitors[onvifOptions.auth.id].onvifConnection = new onvif.OnvifDevice({
xaddr : 'http://' + controlURLOptions.host + ':' + controlURLOptions.port + '/onvif/device_service',
user : controlURLOptions.username,
pass : controlURLOptions.password
})
var device = s.group[onvifOptions.auth.ke].activeMonitors[onvifOptions.auth.id].onvifConnection
device.init().then((info) => {
if(info)doAction(device)
}).catch(function(error){
return errorMessage('Device responded with an error',error)
})
}else{
doAction(s.group[onvifOptions.auth.ke].activeMonitors[onvifOptions.auth.id].onvifConnection)
}
}
const onWebSocketConnection = async (cn) => {
const tx = function(z){if(!z.ke){z.ke=cn.ke;};cn.emit('f',z);}
cn.on('f',(d) => {
switch(d.f){
case'onvif':
runOnvifScanner(d,tx)
break;
}
})
}
s.onWebSocketConnection(onWebSocketConnection)
/**
* API : ONVIF Method Controller
*/
app.all([
config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:action',
config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:service/:action'
],function (req,res){
s.auth(req.params,function(user){
runOnvifMethod({
auth: {
ke: req.params.ke,
id: req.params.id,
auth: req.params.auth,
action: req.params.action,
service: req.params.service,
},
options: s.getPostData(req,'options',true) || s.getPostData(req,'params',true),
},(endData) => {
s.closeJsonResponse(res,endData)
})
},res,req);
})
/**
* API : FFprobe
*/
app.get(config.webPaths.apiPrefix+':auth/probe/:ke',function (req,res){
s.auth(req.params,function(user){
runFFprobe(req.query.url,req.params.auth,(endData) => {
s.closeJsonResponse(res,endData)
})
},res,req);
})
}

View File

@ -1,10 +1,8 @@
var os = require('os');
var moment = require('moment');
var execSync = require('child_process').execSync;
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var jsonfile = require("jsonfile");
var onvif = require("node-onvif");
module.exports = function(s,config,lang,io){
s.clientSocketConnection = {}
//send data to socket client function
@ -787,134 +785,6 @@ module.exports = function(s,config,lang,io){
break;
}
break;
// case'video':
// switch(d.ff){
// case'fix':
// s.video('fix',d)
// break;
// }
// break;
case'ffprobe':
if(s.group[cn.ke].users[cn.auth]){
switch(d.ff){
case'stop':
exec('kill -9 '+s.group[cn.ke].users[cn.auth].ffprobe.pid,{detatched: true})
break;
default:
if(s.group[cn.ke].users[cn.auth].ffprobe){
return
}
s.group[cn.ke].users[cn.auth].ffprobe=1;
tx({f:'ffprobe_start'})
exec('ffprobe '+('-v quiet -print_format json -show_format -show_streams '+d.query),function(err,data){
tx({f:'ffprobe_data',data:data.toString('utf8')})
delete(s.group[cn.ke].users[cn.auth].ffprobe)
tx({f:'ffprobe_stop'})
})
//auto kill in 30 seconds
setTimeout(function(){
exec('kill -9 '+d.pid,{detached: true})
},30000)
break;
}
}
break;
case'onvif':
d.ip=d.ip.replace(/ /g,'');
d.port=d.port.replace(/ /g,'');
if(d.ip===''){
var interfaces = os.networkInterfaces();
var addresses = [];
for (var k in interfaces) {
for (var k2 in interfaces[k]) {
var address = interfaces[k][k2];
if (address.family === 'IPv4' && !address.internal) {
addresses.push(address.address);
}
}
}
d.arr=[]
addresses.forEach(function(v){
if(v.indexOf('0.0.0')>-1){return false}
v=v.split('.');
delete(v[3]);
v=v.join('.');
d.arr.push(v+'1-'+v+'254')
})
d.ip=d.arr.join(',')
}
if(d.port===''){
d.port='80,8080,8000,7575,8081,554'
}
d.ip.split(',').forEach(function(v){
if(v.indexOf('-')>-1){
v=v.split('-');
d.IP_RANGE_START = v[0],
d.IP_RANGE_END = v[1];
}else{
d.IP_RANGE_START = v;
d.IP_RANGE_END = v;
}
if(!d.IP_LIST){
d.IP_LIST = s.ipRange(d.IP_RANGE_START,d.IP_RANGE_END);
}else{
d.IP_LIST=d.IP_LIST.concat(s.ipRange(d.IP_RANGE_START,d.IP_RANGE_END))
}
//check port
if(d.port.indexOf('-')>-1){
d.port=d.port.split('-');
d.PORT_RANGE_START = d.port[0];
d.PORT_RANGE_END = d.port[1];
d.PORT_LIST = s.portRange(d.PORT_RANGE_START,d.PORT_RANGE_END);
}else{
d.PORT_LIST=d.port.split(',')
}
//check user name and pass
d.USERNAME='';
if(d.user){
d.USERNAME = d.user
}
d.PASSWORD='';
if(d.pass){
d.PASSWORD = d.pass
}
})
d.cams=[]
d.IP_LIST.forEach(function(ip_entry,n) {
d.PORT_LIST.forEach(function(port_entry,nn) {
var device = new onvif.OnvifDevice({
xaddr : 'http://' + ip_entry + ':' + port_entry + '/onvif/device_service',
user : d.USERNAME,
pass : d.PASSWORD
})
device.init().then((info) => {
var data = {
f : 'onvif',
ip : ip_entry,
port : port_entry,
info : info
}
device.services.device.getSystemDateAndTime().then((date) => {
data.date = date
device.services.media.getStreamUri({
ProfileToken : device.current_profile.token,
Protocol : 'RTSP'
}).then((stream) => {
data.uri = stream.data.GetStreamUriResponse.MediaUri.Uri
tx(data)
}).catch((error) => {
// console.log(error)
});
}).catch((error) => {
// console.log(error)
});
}).catch(function(error){
// console.log(error)
})
});
});
// tx({f:'onvif_end'})
break;
}
}catch(er){
s.systemLog('ERROR CATCH 1',er)

View File

@ -8,7 +8,6 @@ var execSync = require('child_process').execSync;
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
var httpProxy = require('http-proxy');
var onvif = require('node-onvif');
var proxy = httpProxy.createProxyServer({})
var ejs = require('ejs');
var fileupload = require("express-fileupload");
@ -1911,170 +1910,6 @@ module.exports = function(s,config,lang,app,io){
}
})
/**
* API : FFprobe
*/
var activeProbes = {}
app.get(config.webPaths.apiPrefix+':auth/probe/:ke',function (req,res){
var endData = {ok: false}
s.auth(req.params,function(user){
var url = req.query.url
if(!url){
endData.error = 'Missing URL'
s.closeJsonResponse(res,endData)
return
}
if(activeProbes[req.params.auth]){
endData.error = 'Account is already probing'
s.closeJsonResponse(res,endData)
return
}
activeProbes[req.params.auth] = 1
const probeCommand = s.splitForFFPMEG(`-v quiet -print_format json -show_format -show_streams -i "${url}"`).join(' ')
exec('ffprobe ' + probeCommand,function(err,stdout,stderr){
delete(activeProbes[req.params.auth])
if(err){
endData.error = (err)
}else{
endData.ok = true
endData.result = s.parseJSON(stdout)
}
endData.probe = probeCommand
s.closeJsonResponse(res,endData)
})
},res,req);
})
/**
* API : ONVIF Method Controller
*/
app.all([
config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:action',
config.webPaths.apiPrefix+':auth/onvif/:ke/:id/:service/:action'
],function (req,res){
var response = {ok:false};
res.setHeader('Content-Type', 'application/json');
s.auth(req.params,function(user){
var errorMessage = function(msg,error){
response.ok = false
response.msg = msg
response.error = error
res.end(s.prettyPrint(response))
}
var actionCallback = function(onvifActionResponse){
response.ok = true
if(onvifActionResponse.data){
response.responseFromDevice = onvifActionResponse.data
}else{
response.responseFromDevice = onvifActionResponse
}
if(onvifActionResponse.soap)response.soap = onvifActionResponse.soap
res.end(s.prettyPrint(response))
}
var isEmpty = function(obj) {
for(var key in obj) {
if(obj.hasOwnProperty(key))
return false;
}
return true;
}
var doAction = function(Camera){
var completeAction = function(command){
if(command.then){
command.then(actionCallback).catch(function(error){
errorMessage('Device Action responded with an error',error)
})
}else if(command){
response.ok = true
response.repsonseFromDevice = command
res.end(s.prettyPrint(response))
}else{
response.error = 'Big Errors, Please report it to Shinobi Development'
res.end(s.prettyPrint(response))
}
}
var action
if(req.params.service){
if(Camera.services[req.params.service] === undefined){
return errorMessage('This is not an available service. Please use one of the following : '+Object.keys(Camera.services).join(', '))
}
if(Camera.services[req.params.service] === null){
return errorMessage('This service is not activated. Maybe you are not connected through ONVIF. You can test by attempting to use the "Control" feature with ONVIF in Shinobi.')
}
action = Camera.services[req.params.service][req.params.action]
}else{
action = Camera[req.params.action]
}
// console.log(s.parseJSON(req.query.options))
if(!action || typeof action !== 'function'){
errorMessage(req.params.action+' is not an available ONVIF function. See https://github.com/futomi/node-onvif for functions.')
}else{
var argNames = s.getFunctionParamNames(action)
var options
var command
if(argNames[0] === 'options' || argNames[0] === 'params'){
options = {}
if(req.query.options){
var jsonRevokedText = 'JSON not formated correctly'
try{
options = JSON.parse(req.query.options)
}catch(err){
return errorMessage(jsonRevokedText,err)
}
}else if(req.body.options){
try{
options = JSON.parse(req.body.options)
}catch(err){
return errorMessage(jsonRevokedText,err)
}
}else if(req.query.params){
try{
options = JSON.parse(req.query.params)
}catch(err){
return errorMessage(jsonRevokedText,err)
}
}else if(req.body.params){
try{
options = JSON.parse(req.body.params)
}catch(err){
return errorMessage(jsonRevokedText,err)
}
}
}
if(req.params.service){
command = Camera.services[req.params.service][req.params.action](options)
}else{
command = Camera[req.params.action](options)
}
completeAction(command)
}
}
if(!s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection){
//prepeare onvif connection
var controlURL
var monitorConfig = s.group[req.params.ke].rawMonitorConfigurations[req.params.id]
if(!monitorConfig.details.control_base_url||monitorConfig.details.control_base_url===''){
controlURL = s.buildMonitorUrl(monitorConfig, true)
}else{
controlURL = monitorConfig.details.control_base_url
}
var controlURLOptions = s.cameraControlOptionsFromUrl(controlURL,monitorConfig)
//create onvif connection
s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection = new onvif.OnvifDevice({
xaddr : 'http://' + controlURLOptions.host + ':' + controlURLOptions.port + '/onvif/device_service',
user : controlURLOptions.username,
pass : controlURLOptions.password
})
var device = s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection
device.init().then((info) => {
if(info)doAction(device)
}).catch(function(error){
return errorMessage('Device responded with an error',error)
})
}else{
doAction(s.group[req.params.ke].activeMonitors[req.params.id].onvifConnection)
}
},res,req);
})
/**
* API : Account Edit from Dashboard
*/
app.all(config.webPaths.apiPrefix+':auth/accounts/:ke/edit',function (req,res){