2019-02-23 17:50:03 +00:00
var fs = require ( 'fs' )
2019-12-25 07:00:34 +00:00
var exec = require ( 'child_process' ) . exec
2019-02-23 06:59:06 +00:00
module . exports = function ( s , config , lang , app , io ) {
2022-11-07 22:35:25 +00:00
const base64Prefix = '=?UTF-8?B?' ;
function isBase64String ( theString ) {
return theString . startsWith ( base64Prefix )
}
function convertBase64ToTextString ( theString ) {
let data = theString . replace ( base64Prefix , '' ) ;
let buff = Buffer . from ( data , 'base64' ) ;
let text = buff . toString ( 'ascii' ) ;
return text
}
2024-02-06 18:54:28 +00:00
async function copyFileAsync ( filePath , snapPath ) {
return new Promise ( ( resolve , reject ) => {
const readStream = fs . createReadStream ( filePath ) ;
const writeStream = fs . createWriteStream ( snapPath ) ;
readStream . on ( 'error' , reject ) ;
writeStream . on ( 'error' , reject ) ;
writeStream . on ( 'finish' , resolve ) ;
readStream . pipe ( writeStream ) ;
} ) ;
}
2021-01-02 18:49:05 +00:00
const {
triggerEvent ,
} = require ( './events/utils.js' ) ( s , config , lang )
2024-09-28 16:55:50 +00:00
const {
deleteFilesInFolder ,
} = require ( './basic/utils.js' ) ( process . cwd ( ) , config )
2019-02-23 06:59:06 +00:00
if ( config . dropInEventServer === true ) {
2019-08-12 03:42:42 +00:00
if ( config . dropInEventForceSaveEvent === undefined ) config . dropInEventForceSaveEvent = true
2019-02-23 06:59:06 +00:00
if ( config . dropInEventDeleteFileAfterTrigger === undefined ) config . dropInEventDeleteFileAfterTrigger = true
2020-02-19 04:54:48 +00:00
var fileQueueForDeletion = { }
var fileQueue = { }
var search = function ( searchIn , searchFor ) {
return searchIn . indexOf ( searchFor ) > - 1
}
var getFileNameFromPath = function ( filePath ) {
fileParts = filePath . split ( '/' )
return fileParts [ fileParts . length - 1 ]
}
var clipPathEnding = function ( filePath ) {
var newPath = filePath + ''
if ( newPath . substring ( newPath . length - 1 ) == "/" ) {
newPath = newPath . substring ( 0 , newPath . length - 1 ) ;
}
return newPath ;
}
var processFile = function ( filePath , monitorConfig ) {
var ke = monitorConfig . ke
var mid = monitorConfig . mid
var filename = getFileNameFromPath ( filePath )
if ( search ( filename , '.jpg' ) || search ( filename , '.jpeg' ) ) {
var snapPath = s . dir . streams + ke + '/' + mid + '/s.jpg'
2024-02-06 18:54:28 +00:00
fs . rm ( snapPath , async function ( err ) {
await copyFileAsync ( filePath , snapPath )
2021-01-02 18:49:05 +00:00
triggerEvent ( {
2020-02-19 04:54:48 +00:00
id : mid ,
ke : ke ,
details : {
confidence : 100 ,
name : filename ,
plug : "dropInEvent" ,
reason : "ftpServer"
} ,
} , config . dropInEventForceSaveEvent )
} )
} else {
var reason = "ftpServer"
if ( search ( filename , '.mp4' ) ) {
fs . stat ( filePath , function ( err , stats ) {
if ( err ) return ;
var startTime = stats . ctime
var endTime = stats . mtime
var shinobiFilename = s . formattedTime ( startTime ) + '.mp4'
var recordingPath = s . getVideoDirectory ( monitorConfig ) + shinobiFilename
var writeStream = fs . createWriteStream ( recordingPath )
fs . createReadStream ( filePath ) . pipe ( writeStream )
writeStream . on ( 'finish' , ( ) => {
s . insertCompletedVideo ( s . group [ monitorConfig . ke ] . rawMonitorConfigurations [ monitorConfig . mid ] , {
2020-03-03 15:14:32 +00:00
file : shinobiFilename ,
events : [
{
id : mid ,
ke : ke ,
time : new Date ( ) ,
details : {
confidence : 100 ,
name : filename ,
plug : "dropInEvent" ,
reason : "ftpServer"
}
}
] ,
2020-02-19 04:54:48 +00:00
} , function ( ) {
} )
} )
} )
}
var completeAction = function ( ) {
2021-01-02 18:49:05 +00:00
triggerEvent ( {
2020-02-19 04:54:48 +00:00
id : mid ,
ke : ke ,
details : {
confidence : 100 ,
name : filename ,
plug : "dropInEvent" ,
reason : reason
} ,
} , config . dropInEventForceSaveEvent )
}
if ( search ( filename , '.txt' ) ) {
fs . readFile ( filePath , { encoding : 'utf-8' } , function ( err , data ) {
if ( data ) {
reason = data . split ( '\n' ) [ 0 ] || filename
} else if ( filename ) {
reason = filename
}
completeAction ( )
} )
} else {
completeAction ( )
}
}
}
var onFileOrFolderFound = function ( filePath , deletionKey , monitorConfig ) {
fs . stat ( filePath , function ( err , stats ) {
if ( ! err ) {
if ( stats . isDirectory ( ) ) {
fs . readdir ( filePath , function ( err , files ) {
if ( files ) {
files . forEach ( function ( filename ) {
onFileOrFolderFound ( clipPathEnding ( filePath ) + '/' + filename , deletionKey , monitorConfig )
} )
} else if ( err ) {
console . log ( err )
}
} )
} else {
if ( ! fileQueue [ filePath ] ) {
processFile ( filePath , monitorConfig )
if ( config . dropInEventDeleteFileAfterTrigger ) {
clearTimeout ( fileQueue [ filePath ] )
fileQueue [ filePath ] = setTimeout ( function ( ) {
2024-09-28 16:55:50 +00:00
fs . rm ( filePath , { recursive : true } , ( err ) => {
2020-02-19 04:54:48 +00:00
delete ( fileQueue [ filePath ] )
} )
} , 1000 * 60 * 5 )
}
}
}
if ( config . dropInEventDeleteFileAfterTrigger ) {
clearTimeout ( fileQueueForDeletion [ deletionKey ] )
fileQueueForDeletion [ deletionKey ] = setTimeout ( function ( ) {
2024-09-28 16:55:50 +00:00
fs . rm ( filePath , { recursive : true } , ( err ) => {
2020-02-19 04:54:48 +00:00
delete ( fileQueueForDeletion [ deletionKey ] )
} )
} , 1000 * 60 * 5 )
}
}
} )
}
2020-05-14 01:07:54 +00:00
var createDropInEventsDirectory = function ( ) {
2024-10-06 00:15:22 +00:00
try {
if ( ! config . dropInEventsDir ) {
config . dropInEventsDir = s . dir . streams + 'dropInEvents/'
}
s . dir . dropInEvents = s . checkCorrectPathEnding ( config . dropInEventsDir )
//dropInEvents dir
if ( ! fs . existsSync ( s . dir . dropInEvents ) ) {
fs . mkdirSync ( s . dir . dropInEvents )
}
} catch ( err ) {
console . error ( err )
2019-02-23 06:59:06 +00:00
}
}
2019-02-28 19:27:20 +00:00
var getDropInEventDir = function ( monitorConfig ) {
var ke = monitorConfig . ke
var mid = monitorConfig . mid
var groupEventDropDir = s . dir . dropInEvents + ke
var monitorEventDropDir = groupEventDropDir + '/' + mid + '/'
return monitorEventDropDir
}
var onMonitorStop = function ( monitorConfig ) {
var ke = monitorConfig . ke
var mid = monitorConfig . mid
2019-07-08 03:39:41 +00:00
if ( s . group [ monitorConfig . ke ] . activeMonitors [ monitorConfig . mid ] . dropInEventWatcher ) {
s . group [ monitorConfig . ke ] . activeMonitors [ monitorConfig . mid ] . dropInEventWatcher . close ( )
delete ( s . group [ monitorConfig . ke ] . activeMonitors [ monitorConfig . mid ] . dropInEventWatcher )
2019-09-07 19:49:02 +00:00
var monitorEventDropDir = getDropInEventDir ( monitorConfig )
s . file ( 'deleteFolder' , monitorEventDropDir + '*' )
2019-02-28 19:27:20 +00:00
}
2019-09-07 19:49:02 +00:00
}
var createDropInEventDirectory = function ( e , callback ) {
var directory = s . dir . dropInEvents + e . ke + '/'
fs . mkdir ( directory , function ( err ) {
s . handleFolderError ( err )
directory = s . dir . dropInEvents + e . ke + '/' + ( e . id || e . mid ) + '/'
fs . mkdir ( directory , function ( err ) {
s . handleFolderError ( err )
2024-09-28 16:55:50 +00:00
deleteFilesInFolder ( directory )
2019-09-07 19:49:02 +00:00
callback ( err , directory )
} )
} )
2019-02-28 19:27:20 +00:00
}
2019-02-23 06:59:06 +00:00
var onMonitorInit = function ( monitorConfig ) {
var ke = monitorConfig . ke
var mid = monitorConfig . mid
var groupEventDropDir = s . dir . dropInEvents + ke
2020-02-19 04:54:48 +00:00
createDropInEventDirectory ( monitorConfig , function ( err , monitorEventDropDir ) { } )
2019-02-23 06:59:06 +00:00
}
2019-02-28 04:17:49 +00:00
// FTP Server
2024-10-06 00:15:22 +00:00
createDropInEventsDirectory ( )
2019-02-23 17:50:03 +00:00
if ( config . ftpServer === true ) {
2024-10-06 00:15:22 +00:00
try {
const FtpSrv = require ( 'ftp-srv' )
console . error ( 'WARNING : FTP Server is enabled.' )
if ( ! config . ftpServerPort ) config . ftpServerPort = 21
if ( ! config . ftpServerUrl ) config . ftpServerUrl = ` ftp://0.0.0.0: ${ config . ftpServerPort } `
if ( ! config . ftpServerPasvUrl ) config . ftpServerPasvUrl = config . ftpServerUrl . replace ( /.*:\/\// , '' ) . replace ( /:.*/ , '' ) ;
if ( ! config . ftpServerPasvMinPort ) config . ftpServerPasvMinPort = 10050 ;
if ( ! config . ftpServerPasvMaxPort ) config . ftpServerPasvMaxPort = 10100 ;
config . ftpServerUrl = config . ftpServerUrl . replace ( '{{PORT}}' , config . ftpServerPort )
2022-01-26 19:45:15 +00:00
2024-10-06 00:15:22 +00:00
const ftpServer = new FtpSrv ( {
url : config . ftpServerUrl ,
// pasv_url must be set to enable PASV; ftp-srv uses its known IP if given 127.0.0.1,
// and smart clients will ignore the IP anyway. Some Dahua IP cams require PASV mode.
// ftp-srv just wants an IP only (no protocol or port)
pasv _url : config . ftpServerPasvUrl ,
pasv _min : config . ftpServerPasvMinPort ,
pasv _max : config . ftpServerPasvMaxPort ,
greeting : "Shinobi FTP dropInEvent Server says hello!" ,
log : require ( 'bunyan' ) . createLogger ( {
name : 'ftp-srv' ,
level : 100
} ) ,
} )
2019-02-23 17:50:03 +00:00
2024-10-06 00:15:22 +00:00
ftpServer . on ( 'login' , ( { connection , username , password } , resolve , reject ) => {
s . basicOrApiAuthentication ( username , password , function ( err , user ) {
if ( user ) {
connection . on ( 'STOR' , ( error , fileName ) => {
if ( ! fileName ) return ;
var pathPieces = fileName . replace ( s . dir . dropInEvents , '' ) . split ( '/' )
var ke = pathPieces [ 0 ]
var mid = pathPieces [ 1 ]
var firstDroppedPart = pathPieces [ 2 ]
var monitorEventDropDir = s . dir . dropInEvents + ke + '/' + mid + '/'
var deleteKey = monitorEventDropDir + firstDroppedPart
onFileOrFolderFound ( monitorEventDropDir + firstDroppedPart , deleteKey , Object . assign ( { } , s . group [ ke ] . rawMonitorConfigurations [ mid ] ) )
} )
resolve ( { root : s . dir . dropInEvents + user . ke } )
} else {
// reject(new Error('Failed Authorization'))
}
} )
2020-09-06 18:55:42 +00:00
} )
2024-10-06 00:15:22 +00:00
ftpServer . on ( 'client-error' , ( { connection , context , error } ) => {
console . log ( 'client-error' , error )
} )
ftpServer . listen ( ) . then ( ( ) => {
s . systemLog ( ` FTP Server running on port ${ config . ftpServerPort } ... ` )
} ) . catch ( function ( err ) {
s . systemLog ( err )
} )
} catch ( err ) {
console . error ( err . message )
console . error ( 'Could not start FTP Server, please run "npm install ftp-srv" inside the Shinobi folder.' )
console . error ( 'The ftp-srv Module is known to have possible vulnerabilities. Due to the nature of the vulnerability you should be unaffected unless the FTP Port is public facing. Use at your own risk.' )
}
2019-02-27 02:34:04 +00:00
}
2019-02-28 04:17:49 +00:00
//add extensions
s . onMonitorInit ( onMonitorInit )
2019-02-28 19:27:20 +00:00
s . onMonitorStop ( onMonitorStop )
2019-02-28 04:17:49 +00:00
}
// SMTP Server
// allow starting SMTP server without dropInEventServer
if ( config . smtpServer === true ) {
2019-08-13 23:21:39 +00:00
if ( config . smtpServerHideStartTls === undefined ) config . smtpServerHideStartTls = null
2019-02-28 04:17:49 +00:00
var SMTPServer = require ( "smtp-server" ) . SMTPServer ;
if ( ! config . smtpServerPort && ( config . smtpServerSsl && config . smtpServerSsl . enabled !== false || config . ssl ) ) { config . smtpServerPort = 465 } else if ( ! config . smtpServerPort ) { config . smtpServerPort = 25 }
2020-08-11 16:16:53 +00:00
config . smtpServerOptions = config . smtpServerOptions ? config . smtpServerOptions : { }
var smtpOptions = Object . assign ( {
2020-06-02 05:17:36 +00:00
logger : config . debugLog || config . smtpServerLog ,
2019-08-13 23:21:39 +00:00
hideSTARTTLS : config . smtpServerHideStartTls ,
2019-02-28 04:17:49 +00:00
onAuth ( auth , session , callback ) {
var username = auth . username
var password = auth . password
2019-04-06 05:27:22 +00:00
s . basicOrApiAuthentication ( username , password , function ( err , user ) {
2019-02-28 04:17:49 +00:00
if ( user ) {
callback ( null , { user : user . ke } )
2019-02-27 02:34:04 +00:00
} else {
2019-02-28 04:17:49 +00:00
callback ( new Error ( lang . failedLoginText2 ) )
2019-02-27 02:34:04 +00:00
}
} )
2019-02-28 04:17:49 +00:00
} ,
onRcptTo ( address , session , callback ) {
var split = address . address . split ( '@' )
var monitorId = split [ 0 ]
var ke = session . user
2022-09-24 21:19:25 +00:00
if ( s . group [ ke ] && s . group [ ke ] . activeMonitors [ monitorId ] && s . group [ ke ] . activeMonitors [ monitorId ] . isStarted === true ) {
2019-08-13 23:21:39 +00:00
session . monitorId = monitorId
2019-02-28 04:17:49 +00:00
} else {
return callback ( new Error ( lang [ 'No Monitor Exists with this ID.' ] ) )
}
callback ( )
2019-08-13 23:21:39 +00:00
} ,
onData ( stream , session , callback ) {
if ( session . monitorId ) {
var ke = session . user
var monitorId = session . monitorId
2020-01-18 07:20:16 +00:00
var details = s . group [ ke ] . rawMonitorConfigurations [ monitorId ] . details
2022-11-07 22:35:25 +00:00
var reasonTag = ''
2019-08-13 23:21:39 +00:00
var text = ''
stream . on ( 'data' , function ( data ) {
text += data . toString ( )
} ) // print message to console
stream . on ( "end" , function ( ) {
var contentPart = text . split ( '--PartBoundary12345678' )
contentPart . forEach ( function ( part ) {
var parsed = { }
var lines = part . split ( /\r?\n/ )
lines . forEach ( function ( line , n ) {
var pieces = line . split ( ':' )
if ( pieces [ 1 ] ) {
var nextLine = lines [ n + 1 ]
var keyName = pieces [ 0 ] . trim ( ) . toLowerCase ( )
pieces . shift ( )
var parsedValue = pieces . join ( ':' )
parsed [ keyName ] = parsedValue
}
} )
if ( parsed [ 'content-type' ] && parsed [ 'content-type' ] . indexOf ( 'image/jpeg' ) > - 1 ) {
// console.log(lines)
}
2022-11-07 22:35:25 +00:00
if ( reasonTag ) return ;
2019-08-13 23:21:39 +00:00
if ( parsed [ 'alarm event' ] ) {
reasonTag = parsed [ 'alarm event' ]
} else if ( parsed . subject ) {
2022-11-07 22:35:25 +00:00
const subjectString = parsed . subject ;
reasonTag = isBase64String ( subjectString ) ? convertBase64ToTextString ( subjectString ) : subjectString
2019-08-13 23:21:39 +00:00
}
} )
2021-01-02 18:49:05 +00:00
triggerEvent ( {
2019-08-13 23:21:39 +00:00
id : monitorId ,
ke : ke ,
details : {
confidence : 100 ,
name : 'smtpServer' ,
plug : "dropInEvent" ,
2022-11-07 22:35:25 +00:00
reason : reasonTag || 'smtpServer'
2020-01-18 07:20:16 +00:00
} ,
2019-08-13 23:21:39 +00:00
} , config . dropInEventForceSaveEvent )
callback ( )
} )
} else {
callback ( )
}
2019-02-27 02:34:04 +00:00
}
2020-08-11 16:16:53 +00:00
} , config . smtpServerOptions )
2019-02-28 04:17:49 +00:00
if ( config . smtpServerSsl && config . smtpServerSsl . enabled !== false || config . ssl && config . ssl . cert && config . ssl . key ) {
var key = config . ssl . key || fs . readFileSync ( config . smtpServerSsl . key )
var cert = config . ssl . cert || fs . readFileSync ( config . smtpServerSsl . cert )
smtpOptions = Object . assign ( smtpOptions , {
secure : true ,
key : config . ssl . key ,
cert : config . ssl . cert
2019-02-23 17:50:03 +00:00
} )
}
2019-02-28 04:17:49 +00:00
var server = new SMTPServer ( smtpOptions )
server . listen ( config . smtpServerPort , function ( ) {
s . systemLog ( ` SMTP Server running on port ${ config . smtpServerPort } ... ` )
} )
2019-02-23 06:59:06 +00:00
}
2021-11-14 16:38:22 +00:00
require ( './dropInEvents/mqtt.js' ) ( s , config , lang , app , io )
2019-02-23 06:59:06 +00:00
}