var fs = require('fs') var exec = require('child_process').exec module.exports = function(s,config,lang,app,io){ if(config.dropInEventServer === true){ if(config.dropInEventForceSaveEvent === undefined)config.dropInEventForceSaveEvent = true if(config.dropInEventDeleteFileAfterTrigger === undefined)config.dropInEventDeleteFileAfterTrigger = true 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' fs.unlink(snapPath,function(err){ fs.createReadStream(filePath).pipe(fs.createWriteStream(snapPath)) s.triggerEvent({ 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],{ file: shinobiFilename, events: [ { id: mid, ke: ke, time: new Date(), details: { confidence: 100, name: filename, plug: "dropInEvent", reason: "ftpServer" } } ], },function(){ }) }) }) } var completeAction = function(){ s.triggerEvent({ id: mid, ke: ke, details: { confidence: 100, name: filename, plug: "dropInEvent", reason: reason }, doObjectDetection: (s.isAtleatOneDetectorPluginConnected && s.group[ke].rawMonitorConfigurations[mid].details.detector_use_detect_object === '1') },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(){ exec('rm -rf ' + filePath,function(err){ delete(fileQueue[filePath]) }) },1000 * 60 * 5) } } } if(config.dropInEventDeleteFileAfterTrigger){ clearTimeout(fileQueueForDeletion[deletionKey]) fileQueueForDeletion[deletionKey] = setTimeout(function(){ exec('rm -rf ' + deletionKey,function(err){ delete(fileQueueForDeletion[deletionKey]) }) },1000 * 60 * 5) } } }) } var createDropInEventsDirectory = function(){ 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) } } 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 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) var monitorEventDropDir = getDropInEventDir(monitorConfig) s.file('deleteFolder',monitorEventDropDir + '*') } } 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) exec('rm -rf "' + directory + '*"',function(){}) callback(err,directory) }) }) } var onMonitorInit = function(monitorConfig){ var ke = monitorConfig.ke var mid = monitorConfig.mid var groupEventDropDir = s.dir.dropInEvents + ke createDropInEventDirectory(monitorConfig,function(err,monitorEventDropDir){}) } // FTP Server if(config.ftpServer === true){ const authenticateConnection = (connection) => { return new Promise((resolve,reject) => { var username = null; s.debugLog('client connected: ' + connection.remoteAddress); connection.on('command:user', function(user, success, failure) { if (user) { username = user; success(); } else { failure(); } }) connection.on('command:pass', function(password, success, failure) { s.basicOrApiAuthentication(username,password,function(err,user){ if(user){ connection._user = user success(username); } else { failure(); } resolve({ ok: !!user, username: username, password: password }) }) }) }) } createDropInEventsDirectory() if(!config.ftpServerPort)config.ftpServerPort = 21 if(!config.ftpServerUrl)config.ftpServerUrl = `ftp://0.0.0.0:${config.ftpServerPort}` config.ftpServerUrl = config.ftpServerUrl.replace('{{PORT}}',config.ftpServerPort) const ftpd = require('shinobi-ftpd') const ftpServer = new ftpd.FtpServer(config.ftpServerUrl, Object.assign({ getInitialCwd: function(connection, callback) { callback(null, s.dir.dropInEvents + '/' + connection._user.ke) }, getRoot: function() { return s.dir.dropInEvents }, pasvPortRangeStart: 1025, pasvPortRangeEnd: 1050, allowUnauthorizedTls: true, uploadMaxSlurpSize: 7000 },config.ftpServerOptions || {})) ftpServer.on('error', function(error) { s.debugLog(['FTP Server error:', error]); }) ftpServer.on('client:connected', async (connection) => { const response = await authenticateConnection(connection) if(connection._user){ connection.cwd = s.dir.dropInEvents + connection._user.ke connection.on('file:stor', async (eventType, data) => { const fileName = data.file const pathPieces = fileName.substr(1).split('/') const groupKey = connection._user.ke const monitorId = pathPieces[0] const firstDroppedPart = pathPieces[1] const monitorEventDropDir = s.dir.dropInEvents + groupKey + '/' + monitorId + '/' const deleteKey = monitorEventDropDir + firstDroppedPart onFileOrFolderFound( monitorEventDropDir + firstDroppedPart, deleteKey, { ke: groupKey, mid: monitorId } ) }) }else{ s.systemLog(`Failed FTP Login Attempt : ${response.username}/${response.password}`) throw `Failed to Authenticate FTP : ${response.username}/${response.password}`; } }) ftpServer.listen(config.ftpServerPort) s.systemLog(`FTP Server running on port ${config.ftpServerPort}...`) } //add extensions s.onMonitorInit(onMonitorInit) s.onMonitorStop(onMonitorStop) } // SMTP Server // allow starting SMTP server without dropInEventServer if(config.smtpServer === true){ if(config.smtpServerHideStartTls === undefined)config.smtpServerHideStartTls = null 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} config.smtpServerOptions = config.smtpServerOptions ? config.smtpServerOptions : {} var smtpOptions = Object.assign({ logger: config.debugLog || config.smtpServerLog, hideSTARTTLS: config.smtpServerHideStartTls, onAuth(auth, session, callback) { var username = auth.username var password = auth.password s.basicOrApiAuthentication(username,password,function(err,user){ if(user){ callback(null, {user: user.ke}) }else{ callback(new Error(lang.failedLoginText2)) } }) }, onRcptTo(address, session, callback) { var split = address.address.split('@') var monitorId = split[0] var ke = session.user if(s.group[ke] && s.group[ke].rawMonitorConfigurations[monitorId] && s.group[ke].activeMonitors[monitorId].isStarted === true){ session.monitorId = monitorId }else{ return callback(new Error(lang['No Monitor Exists with this ID.'])) } callback() }, onData(stream, session, callback) { if(session.monitorId){ var ke = session.user var monitorId = session.monitorId var details = s.group[ke].rawMonitorConfigurations[monitorId].details var reasonTag = 'smtpServer' 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) } if(parsed['alarm event']){ reasonTag = parsed['alarm event'] }else if(parsed.subject){ reasonTag = parsed.subject } }) s.triggerEvent({ id: monitorId, ke: ke, details: { confidence: 100, name: 'smtpServer', plug: "dropInEvent", reason: reasonTag }, doObjectDetection: (s.isAtleatOneDetectorPluginConnected && details.detector_use_detect_object === '1') },config.dropInEventForceSaveEvent) callback() }) }else{ callback() } } },config.smtpServerOptions) 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 }) } var server = new SMTPServer(smtpOptions) server.listen(config.smtpServerPort,function(){ s.systemLog(`SMTP Server running on port ${config.smtpServerPort}...`) }) } }