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 s.tx = function(z,y,x){ s.onWebsocketMessageSendExtensions.forEach(function(extender){ extender(z,y,x) }) if(x){ return x.broadcast.to(y).emit('f',z) }; io.to(y).emit('f',z); } s.txToDashcamUsers = function(data,groupKey){ if(s.group[groupKey] && s.group[groupKey].dashcamUsers){ Object.keys(s.group[groupKey].dashcamUsers).forEach(function(auth){ s.tx(data,s.group[groupKey].dashcamUsers[auth].cnid) }) } } s.txWithSubPermissions = function(z,y,permissionChoices){ if(typeof permissionChoices==='string'){ permissionChoices=[permissionChoices] } if(s.group[z.ke]){ Object.keys(s.group[z.ke].users).forEach(function(v){ var user = s.group[z.ke].users[v] if(user.details.sub){ if(user.details.allmonitors!=='1'){ var valid=0 var checked=permissionChoices.length permissionChoices.forEach(function(b){ if(user.details[b] && user.details[b].indexOf(z.mid)!==-1){ ++valid } }) if(valid===checked){ s.tx(z,user.cnid) } }else{ s.tx(z,user.cnid) } }else{ s.tx(z,user.cnid) } }) } } ////socket controller io.on('connection', function (cn) { var tx; //unique h265 socket stream cn.on('h265',function(d){ if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){ cn.disconnect();return; } cn.ip=cn.request.connection.remoteAddress; var toUTC = function(){ return new Date().toISOString(); } var tx=function(z){cn.emit('data',z);} d.failed=function(msg){ tx({f:'stop_reconnect',msg:msg,token_used:d.auth,ke:d.ke}); cn.disconnect(); } d.success=function(r){ r=r[0]; var Emitter,chunkChannel if(!d.channel){ Emitter = s.group[d.ke].activeMonitors[d.id].emitter chunkChannel = 'MAIN' }else{ Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition] chunkChannel = parseInt(d.channel)+config.pipeAddition } if(!Emitter){ cn.disconnect();return; } if(!d.channel)d.channel = 'MAIN'; cn.ke=d.ke, cn.uid=d.uid, cn.auth=d.auth; cn.channel=d.channel; cn.removeListenerOnDisconnect=true; cn.socketVideoStream=d.id; var contentWriter cn.closeSocketVideoStream = function(){ Emitter.removeListener('data', contentWriter); } Emitter.on('data',contentWriter = function(base64){ tx(base64) }) } //check if auth key is user's temporary session key if(s.group[d.ke]&&s.group[d.ke].users&&s.group[d.ke].users[d.auth]){ d.success(s.group[d.ke].users[d.auth]); }else{ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ s.sqlQuery('SELECT * FROM API WHERE ke=? AND code=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ r=r[0] r.details=JSON.parse(r.details) if(r.details.auth_socket==='1'){ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND uid=?',[r.ke,r.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ d.failed('User not found') } }) }else{ d.failed('Permissions for this key do not allow authentication with Websocket') } }else{ d.failed('Not an API key') } }) } }) } }) //unique Base64 socket stream cn.on('Base64',function(d){ if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){ cn.disconnect();return; } cn.ip=cn.request.connection.remoteAddress; var toUTC = function(){ return new Date().toISOString(); } var tx=function(z){cn.emit('data',z);} d.failed=function(msg){ tx({f:'stop_reconnect',msg:msg,token_used:d.auth,ke:d.ke}); cn.disconnect(); } d.success=function(r){ r=r[0]; var Emitter,chunkChannel if(!d.channel){ Emitter = s.group[d.ke].activeMonitors[d.id].emitter chunkChannel = 'MAIN' }else{ Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition] chunkChannel = parseInt(d.channel)+config.pipeAddition } if(!Emitter){ cn.disconnect();return; } if(!d.channel)d.channel = 'MAIN'; cn.ke=d.ke, cn.uid=d.uid, cn.auth=d.auth; cn.channel=d.channel; cn.removeListenerOnDisconnect=true; cn.socketVideoStream=d.id; var contentWriter cn.closeSocketVideoStream = function(){ Emitter.removeListener('data', contentWriter); } Emitter.on('data',contentWriter = function(base64){ tx(base64) }) } //check if auth key is user's temporary session key if(s.group[d.ke]&&s.group[d.ke].users&&s.group[d.ke].users[d.auth]){ d.success(s.group[d.ke].users[d.auth]); }else{ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ s.sqlQuery('SELECT * FROM API WHERE ke=? AND code=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ r=r[0] r.details=JSON.parse(r.details) if(r.details.auth_socket==='1'){ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND uid=?',[r.ke,r.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ d.failed('User not found') } }) }else{ d.failed('Permissions for this key do not allow authentication with Websocket') } }else{ d.failed('Not an API key') } }) } }) } }) //unique FLV socket stream cn.on('FLV',function(d){ if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){ cn.disconnect();return; } cn.ip=cn.request.connection.remoteAddress; var toUTC = function(){ return new Date().toISOString(); } var tx=function(z){cn.emit('data',z);} d.failed=function(msg){ tx({f:'stop_reconnect',msg:msg,token_used:d.auth,ke:d.ke}); cn.disconnect(); } d.success=function(r){ r=r[0]; var Emitter,chunkChannel if(!d.channel){ Emitter = s.group[d.ke].activeMonitors[d.id].emitter chunkChannel = 'MAIN' }else{ Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition] chunkChannel = parseInt(d.channel)+config.pipeAddition } if(!Emitter){ cn.disconnect();return; } if(!d.channel)d.channel = 'MAIN'; cn.ke=d.ke, cn.uid=d.uid, cn.auth=d.auth; cn.channel=d.channel; cn.removeListenerOnDisconnect=true; cn.socketVideoStream=d.id; var contentWriter cn.closeSocketVideoStream = function(){ Emitter.removeListener('data', contentWriter); } tx({time:toUTC(),buffer:s.group[d.ke].activeMonitors[d.id].firstStreamChunk[chunkChannel]}) Emitter.on('data',contentWriter = function(buffer){ tx({time:toUTC(),buffer:buffer}) }) } if(s.group[d.ke] && s.group[d.ke].users && s.group[d.ke].users[d.auth]){ d.success(s.group[d.ke].users[d.auth]); }else{ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ s.sqlQuery('SELECT * FROM API WHERE ke=? AND code=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ r=r[0] r.details=JSON.parse(r.details) if(r.details.auth_socket==='1'){ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND uid=?',[r.ke,r.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ d.failed('User not found') } }) }else{ d.failed('Permissions for this key do not allow authentication with Websocket') } }else{ d.failed('Not an API key') } }) } }) } }) //unique MP4 socket stream cn.on('MP4',function(d){ if(!s.group[d.ke]||!s.group[d.ke].activeMonitors||!s.group[d.ke].activeMonitors[d.id]){ cn.disconnect();return; } cn.ip=cn.request.connection.remoteAddress; var toUTC = function(){ return new Date().toISOString(); } var tx=function(z){cn.emit('data',z);} d.failed=function(msg){ tx({f:'stop_reconnect',msg:msg,token_used:d.auth,ke:d.ke}); cn.disconnect(); } d.success=function(r){ r=r[0]; var Emitter,chunkChannel if(!d.channel){ Emitter = s.group[d.ke].activeMonitors[d.id].emitter chunkChannel = 'MAIN' }else{ Emitter = s.group[d.ke].activeMonitors[d.id].emitterChannel[parseInt(d.channel)+config.pipeAddition] chunkChannel = parseInt(d.channel)+config.pipeAddition } if(!Emitter){ cn.disconnect();return; } if(!d.channel)d.channel = 'MAIN'; cn.ke=d.ke, cn.uid=d.uid, cn.auth=d.auth; cn.channel=d.channel; cn.socketVideoStream=d.id; var mp4frag = s.group[d.ke].activeMonitors[d.id].mp4frag[d.channel]; var onInitialized = () => { cn.emit('mime', mp4frag.mime); mp4frag.removeListener('initialized', onInitialized); }; //event listener var onSegment = function(data){ cn.emit('segment', data); }; cn.closeSocketVideoStream = function(){ if(mp4frag){ mp4frag.removeListener('segment', onSegment) mp4frag.removeListener('initialized', onInitialized) } } cn.on('MP4Command',function(msg){ switch (msg) { case 'mime' ://client is requesting mime var mime = mp4frag.mime; if (mime) { cn.emit('mime', mime); } else { mp4frag.on('initialized', onInitialized); } break; case 'initialization' ://client is requesting initialization segment cn.emit('initialization', mp4frag.initialization); break; case 'segment' ://client is requesting a SINGLE segment var segment = mp4frag.segment; if (segment) { cn.emit('segment', segment); } else { mp4frag.once('segment', onSegment); } break; case 'segments' ://client is requesting ALL segments //send current segment first to start video asap var segment = mp4frag.segment; if (segment) { cn.emit('segment', segment); } //add listener for segments being dispatched by mp4frag mp4frag.on('segment', onSegment); break; case 'pause' : mp4frag.removeListener('segment', onSegment); break; case 'resume' : mp4frag.on('segment', onSegment); break; case 'stop' ://client requesting to stop receiving segments cn.closeSocketVideoStream() break; } }) } if(s.group[d.ke]&&s.group[d.ke].users&&s.group[d.ke].users[d.auth]){ d.success(s.group[d.ke].users[d.auth]); }else{ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ s.sqlQuery('SELECT * FROM API WHERE ke=? AND code=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ r=r[0] r.details=JSON.parse(r.details) if(r.details.auth_socket==='1'){ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND uid=?',[r.ke,r.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ d.failed('User not found') } }) }else{ d.failed('Permissions for this key do not allow authentication with Websocket') } }else{ d.failed('Not an API key') } }) } }) } }) //main socket control functions cn.on('f',function(d){ if(!cn.ke&&d.f==='init'){//socket login cn.ip=cn.request.connection.remoteAddress; tx=function(z){if(!z.ke){z.ke=cn.ke;};cn.emit('f',z);} d.failed=function(){tx({ok:false,msg:'Not Authorized',token_used:d.auth,ke:d.ke});cn.disconnect();} d.success=function(r){ r=r[0];cn.join('GRP_'+d.ke);cn.join('CPU'); cn.ke=d.ke, cn.uid=d.uid, cn.auth=d.auth; if(!s.group[d.ke])s.group[d.ke]={}; // if(!s.group[d.ke].vid)s.group[d.ke].vid={}; if(!s.group[d.ke].users)s.group[d.ke].users={}; // s.group[d.ke].vid[cn.id]={uid:d.uid}; s.group[d.ke].users[d.auth] = { cnid: cn.id, uid: r.uid, mail: r.mail, details: JSON.parse(r.details), logged_in_at: s.timeObject(new Date).format(), login_type: 'Dashboard' } s.clientSocketConnection[cn.id] = cn try{s.group[d.ke].users[d.auth].details=JSON.parse(r.details)}catch(er){} if(s.group[d.ke].users[d.auth].details.get_server_log!=='0'){ cn.join('GRPLOG_'+d.ke) } s.group[d.ke].users[d.auth].lang=s.getLanguageFile(s.group[d.ke].users[d.auth].details.lang) s.userLog({ke:d.ke,mid:'$USER'},{type:s.group[d.ke].users[d.auth].lang['Websocket Connected'],msg:{mail:r.mail,id:d.uid,ip:cn.ip}}) if(!s.group[d.ke].activeMonitors){ s.group[d.ke].activeMonitors={} if(!s.group[d.ke].activeMonitors){s.group[d.ke].activeMonitors={}} } tx({f:'users_online',users:s.group[d.ke].users}) s.tx({f:'user_status_change',ke:d.ke,uid:cn.uid,status:1,user:s.group[d.ke].users[d.auth]},'GRP_'+d.ke) s.sendDiskUsedAmountToClients(d) s.loadGroupApps(d) s.sqlQuery('SELECT * FROM API WHERE ke=? AND uid=?',[d.ke,d.uid],function(err,rrr) { tx({ f:'init_success', users:s.group[d.ke].vid, apis:rrr, os:{ platform:s.platform, cpuCount:s.coreCount, totalmem:s.totalmem } }) try{ s.sqlQuery('SELECT * FROM Monitors WHERE ke=?', [d.ke], function(err,r) { if(r && r[0]){ r.forEach(function(monitor){ s.cameraSendSnapshot({mid:monitor.mid,ke:monitor.ke,mon:monitor},{useIcon: true}) }) } }) }catch(err){ console.log(err) } }) s.onSocketAuthenticationExtensions.forEach(function(extender){ extender(r,cn,d,tx) }) } s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ s.sqlQuery('SELECT * FROM API WHERE ke=? AND code=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ r=r[0] r.details=JSON.parse(r.details) if(r.details.auth_socket==='1'){ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND uid=?',[r.ke,r.uid],function(err,r) { if(r&&r[0]){ d.success(r) }else{ d.failed() } }) }else{ d.failed() } }else{ d.failed() } }) } }) return; } if((d.id||d.uid||d.mid)&&cn.ke){ try{ switch(d.f){ case'monitorOrder': if(d.monitorOrder && d.monitorOrder instanceof Object){ s.sqlQuery('SELECT details FROM Users WHERE uid=? AND ke=?',[cn.uid,cn.ke],function(err,r){ if(r && r[0]){ details = JSON.parse(r[0].details) details.monitorOrder = d.monitorOrder s.sqlQuery('UPDATE Users SET details=? WHERE uid=? AND ke=?',[s.s(details),cn.uid,cn.ke]) } }) } break; case'monitorListOrder': if(d.monitorListOrder && d.monitorListOrder instanceof Object){ s.sqlQuery('SELECT details FROM Users WHERE uid=? AND ke=?',[cn.uid,cn.ke],function(err,r){ if(r && r[0]){ details = JSON.parse(r[0].details) details.monitorListOrder = d.monitorListOrder s.sqlQuery('UPDATE Users SET details=? WHERE uid=? AND ke=?',[s.s(details),cn.uid,cn.ke]) } }) } break; case'update': if(!config.updateKey){ tx({error:lang.updateKeyText1}); return; } if(d.key===config.updateKey){ exec('chmod +x '+s.mainDirectory+'/UPDATE.sh&&'+s.mainDirectory+'/UPDATE.sh',{detached: true}) }else{ tx({error:lang.updateKeyText2}); } break; case'cron': if(s.group[cn.ke]&&s.group[cn.ke].users[cn.auth].details&&!s.group[cn.ke].users[cn.auth].details.sub){ s.tx({f:d.ff},s.cron.id) } break; case'api': switch(d.ff){ case'delete': d.set=[],d.ar=[]; d.form.ke=cn.ke;d.form.uid=cn.uid;delete(d.form.ip); if(!d.form.code){tx({f:'form_incomplete',form:'APIs',uid:cn.uid});return} d.for=Object.keys(d.form); d.for.forEach(function(v){ d.set.push(v+'=?'),d.ar.push(d.form[v]); }); s.sqlQuery('DELETE FROM API WHERE '+d.set.join(' AND '),d.ar,function(err,r){ if(!err){ tx({f:'api_key_deleted',form:d.form,uid:cn.uid}); delete(s.api[d.form.code]); }else{ s.systemLog('API Delete Error : '+e.ke+' : '+' : '+e.mid,err) } }) break; case'add': d.set=[],d.qu=[],d.ar=[]; d.form.ke=cn.ke,d.form.uid=cn.uid,d.form.code=s.gid(30); d.for=Object.keys(d.form); d.for.forEach(function(v){ d.set.push(v),d.qu.push('?'),d.ar.push(d.form[v]); }); s.sqlQuery('INSERT INTO API ('+d.set.join(',')+') VALUES ('+d.qu.join(',')+')',d.ar,function(err,r){ d.form.time=s.formattedTime(new Date,'YYYY-DD-MM HH:mm:ss'); if(!err){tx({f:'api_key_added',form:d.form,uid:cn.uid});}else{s.systemLog(err)} }); break; } break; case'settings': switch(d.ff){ case'filters': switch(d.fff){ case'save':case'delete': s.sqlQuery('SELECT details FROM Users WHERE ke=? AND uid=?',[d.ke,d.uid],function(err,r){ if(r&&r[0]){ r=r[0]; d.d=JSON.parse(r.details); if(d.form.id===''){d.form.id=s.gid(5)} if(!d.d.filters)d.d.filters={}; //save/modify or delete if(d.fff==='save'){ d.d.filters[d.form.id]=d.form; }else{ delete(d.d.filters[d.form.id]); } s.sqlQuery('UPDATE Users SET details=? WHERE ke=? AND uid=?',[JSON.stringify(d.d),d.ke,d.uid],function(err,r){ tx({f:'filters_change',uid:d.uid,ke:d.ke,filters:d.d.filters}); }); } }) break; } break; case'edit': d.cnid = cn.id s.accountSettingsEdit(d) break; } break; case'monitor': switch(d.ff){ case'get': switch(d.fff){ case'videos&events': if(!d.eventLimit){ d.eventLimit = 500 }else{ d.eventLimit = parseInt(d.eventLimit); } if(!d.eventStartDate&&d.startDate){ d.eventStartDate = s.stringToSqlTime(d.startDate) } if(!d.eventEndDate&&d.endDate){ d.eventEndDate = s.stringToSqlTime(d.endDate) } var monitorQuery = '' var monitorValues = [] var permissions = s.group[d.ke].users[cn.auth].details; if(!d.mid){ if(permissions.sub&&permissions.monitors&&permissions.allmonitors!=='1'){ try{permissions.monitors=JSON.parse(permissions.monitors);}catch(er){} var or = []; permissions.monitors.forEach(function(v,n){ or.push('mid=?'); monitorValues.push(v) }) monitorQuery += ' AND ('+or.join(' OR ')+')' } }else if(!permissions.sub||permissions.allmonitors!=='0'||permissions.monitors.indexOf(d.mid)>-1){ monitorQuery += ' and mid=?'; monitorValues.push(d.mid) } var getEvents = function(callback){ var eventQuery = 'SELECT * FROM Events WHERE ke=?'; var eventQueryValues = [cn.ke]; if(d.eventStartDate&&d.eventStartDate!==''){ if(d.eventEndDate&&d.eventEndDate!==''){ eventQuery+=' AND `time` >= ? AND `time` <= ?'; eventQueryValues.push(d.eventStartDate) eventQueryValues.push(d.eventEndDate) }else{ eventQuery+=' AND `time` >= ?'; eventQueryValues.push(d.eventStartDate) } } if(monitorValues.length>0){ eventQuery += monitorQuery; eventQueryValues = eventQueryValues.concat(monitorValues); } eventQuery+=' ORDER BY `time` DESC LIMIT '+d.eventLimit+''; s.sqlQuery(eventQuery,eventQueryValues,function(err,r){ if(err){ console.log(eventQuery) console.error('LINE 2428',err) setTimeout(function(){ getEvents(callback) },2000) }else{ if(!r){r=[]} r.forEach(function(v,n){ r[n].details=JSON.parse(v.details); }) callback(r) } }) } if(!d.videoLimit&&d.limit){ d.videoLimit=d.limit eventQuery.push() } if(!d.videoStartDate&&d.startDate){ d.videoStartDate = s.stringToSqlTime(d.startDate) } if(!d.videoEndDate&&d.endDate){ d.videoEndDate = s.stringToSqlTime(d.endDate) } var getVideos = function(callback){ var videoQuery='SELECT * FROM Videos WHERE ke=?'; var videoQueryValues=[cn.ke]; if(d.videoStartDate||d.videoEndDate){ if(!d.videoStartDateOperator||d.videoStartDateOperator==''){ d.videoStartDateOperator='>=' } if(!d.videoEndDateOperator||d.videoEndDateOperator==''){ d.videoEndDateOperator='<=' } switch(true){ case(d.videoStartDate&&d.videoStartDate!==''&&d.videoEndDate&&d.videoEndDate!==''): videoQuery+=' AND `time` '+d.videoStartDateOperator+' ? AND `end` '+d.videoEndDateOperator+' ?'; videoQueryValues.push(d.videoStartDate) videoQueryValues.push(d.videoEndDate) break; case(d.videoStartDate&&d.videoStartDate!==''): videoQuery+=' AND `time` '+d.videoStartDateOperator+' ?'; videoQueryValues.push(d.videoStartDate) break; case(d.videoEndDate&&d.videoEndDate!==''): videoQuery+=' AND `end` '+d.videoEndDateOperator+' ?'; videoQueryValues.push(d.videoEndDate) break; } } if(monitorValues.length>0){ videoQuery += monitorQuery; videoQueryValues = videoQueryValues.concat(monitorValues); } videoQuery+=' ORDER BY `time` DESC'; if(!d.videoLimit||d.videoLimit==''){ d.videoLimit='100' } if(d.videoLimit!=='0'){ videoQuery+=' LIMIT '+d.videoLimit } s.sqlQuery(videoQuery,videoQueryValues,function(err,r){ if(err){ console.log(videoQuery) console.error('LINE 2416',err) setTimeout(function(){ getVideos(callback) },2000) }else{ s.buildVideoLinks(r,{ auth : cn.auth }) callback({total:r.length,limit:d.videoLimit,videos:r}) } }) } getVideos(function(videos){ getEvents(function(events){ tx({ f: 'videos&events', id: d.mid, videos: videos, events: events }) }) }) break; } break; case'control': s.cameraControl(d,function(resp){ tx({f:'control',response:resp}) }) break; case'jpeg_off': delete(cn.jpeg_on); if(cn.monitorsCurrentlyWatching){ Object.keys(cn.monitorsCurrentlyWatching).forEach(function(n,v){ v=cn.monitorsCurrentlyWatching[n]; cn.join('MON_STREAM_'+n); }); } tx({f:'mode_jpeg_off'}) break; case'jpeg_on': cn.jpeg_on=true; if(cn.monitorsCurrentlyWatching){ Object.keys(cn.monitorsCurrentlyWatching).forEach(function(n,v){ v=cn.monitorsCurrentlyWatching[n]; cn.leave('MON_STREAM_'+n); }) } tx({f:'mode_jpeg_on'}) break; case'watch_on': if(!d.ke){d.ke=cn.ke} s.initiateMonitorObject({mid:d.id,ke:d.ke}); if(!s.group[d.ke]||!s.group[d.ke].activeMonitors[d.id]||s.group[d.ke].activeMonitors[d.id].isStarted === false){return false} cn.join('MON_'+d.ke+d.id); cn.join('DETECTOR_'+d.ke+d.id); if(cn.jpeg_on !== true){ cn.join('MON_STREAM_'+d.ke+d.id); } tx({f:'monitor_watch_on',id:d.id,ke:d.ke}) s.camera('watch_on',d,cn) break; case'watch_off': if(!d.ke){d.ke=cn.ke;}; cn.leave('MON_'+d.ke+d.id); s.camera('watch_off',d,cn); tx({f:'monitor_watch_off',ke:d.ke,id:d.id,cnid:cn.id}) break; case'start':case'stop': s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND mid=?',[cn.ke,d.id],function(err,r) { if(r && r[0]){ r = r[0] s.camera(d.ff,{type:r.type,url:s.buildMonitorUrl(r),id:d.id,mode:d.ff,ke:cn.ke}); } }) 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) } }else{ tx({ok:false,msg:lang.NotAuthorizedText1}); } }); // super page socket functions cn.on('super',function(d){ if(!cn.init&&d.f=='init'){ d.ok=s.superAuth({mail:d.mail,pass:d.pass},function(data){ cn.mail=d.mail cn.join('$'); var tempSessionKey = s.gid(30) cn.superSessionKey = tempSessionKey s.superUsersApi[tempSessionKey] = data s.superUsersApi[tempSessionKey].cnid = cn.id if(!data.$user.tokens)data.$user.tokens = {} data.$user.tokens[tempSessionKey] = {} cn.ip=cn.request.connection.remoteAddress s.userLog({ke:'$',mid:'$USER'},{type:lang['Websocket Connected'],msg:{for:lang['Superuser'],id:cn.mail,ip:cn.ip}}) cn.init='super'; s.tx({f:'init_success',mail:d.mail,superSessionKey:tempSessionKey},cn.id); }) if(d.ok===false){ cn.disconnect(); } }else{ if(cn.mail&&cn.init=='super'){ switch(d.f){ case'logs': switch(d.ff){ case'delete': //config.webPaths.superApiPrefix+':auth/logs/delete' s.sqlQuery('DELETE FROM Logs WHERE ke=?',[d.ke]) break; } break; case'system': switch(d.ff){ case'update': //config.webPaths.superApiPrefix+':auth/update' s.ffmpegKill() s.systemLog('Shinobi ordered to update',{ by:cn.mail, ip:cn.ip }) var updateProcess = spawn('sh',(s.mainDirectory+'/UPDATE.sh').split(' '),{detached: true}) updateProcess.stderr.on('data',function(data){ s.systemLog('Update Info',data.toString()) }) updateProcess.stdout.on('data',function(data){ s.systemLog('Update Info',data.toString()) }) break; case'restart': //config.webPaths.superApiPrefix+':auth/restart/:script' d.check=function(x){return d.target.indexOf(x)>-1} if(d.check('system')){ s.systemLog('Shinobi ordered to restart',{by:cn.mail,ip:cn.ip}) s.ffmpegKill() exec('pm2 restart '+s.mainDirectory+'/camera.js') } if(d.check('cron')){ s.systemLog('Shinobi CRON ordered to restart',{by:cn.mail,ip:cn.ip}) exec('pm2 restart '+s.mainDirectory+'/cron.js') } if(d.check('logs')){ s.systemLog('Flush PM2 Logs',{by:cn.mail,ip:cn.ip}) exec('pm2 flush') } break; case'configure': s.systemLog('conf.json Modified',{by:cn.mail,ip:cn.ip,old:jsonfile.readFileSync(s.location.config)}) jsonfile.writeFile(s.location.config,d.data,{spaces: 2},function(){ s.tx({f:'save_configuration'},cn.id) }) break; } break; case'accounts': switch(d.ff){ case'saveSuper': var currentSuperUserList = jsonfile.readFileSync(s.location.super) var currentSuperUser = {} var currentSuperUserPosition = -1 //find this user in current list currentSuperUserList.forEach(function(user,pos){ if(user.mail === cn.mail){ currentSuperUser = user currentSuperUserPosition = pos } }) var logDetails = { by : cn.mail, ip : cn.ip } //check if pass and pass_again match, if not remove password if(d.form.pass !== '' && d.form.pass === d.form.pass_again){ d.form.pass = s.createHash(d.form.pass) }else{ delete(d.form.pass) } //delete pass_again from object delete(d.form.pass_again) //set new values currentSuperUser = Object.assign(currentSuperUser,d.form) //reset email and log change of email if(d.form.mail !== cn.mail){ logDetails.newEmail = d.form.mail logDetails.oldEmail = cn.mail + '' cn.mail = d.form.mail } //log this change s.systemLog('super.json Modified',logDetails) //modify or add account in temporary master list if(currentSuperUserList[currentSuperUserPosition]){ currentSuperUserList[currentSuperUserPosition] = currentSuperUser }else{ currentSuperUserList.push(currentSuperUser) } //update master list in system jsonfile.writeFile(s.location.super,currentSuperUserList,{spaces: 2},function(){ s.tx({f:'save_preferences'},cn.id) }) break; case'register': if(d.form.mail!==''&&d.form.pass!==''){ if(d.form.pass===d.form.password_again){ s.sqlQuery('SELECT * FROM Users WHERE mail=?',[d.form.mail],function(err,r) { if(r&&r[0]){ //found address already exists d.msg=lang['Email address is in use.']; s.tx({f:'error',ff:'account_register',msg:d.msg},cn.id) }else{ //create new //user id d.form.uid=s.gid(); //check to see if custom key set if(!d.form.ke||d.form.ke===''){ d.form.ke=s.gid() }else{ d.form.ke = d.form.ke.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '') } //write user to db s.sqlQuery('INSERT INTO Users (ke,uid,mail,pass,details) VALUES (?,?,?,?,?)',[d.form.ke,d.form.uid,d.form.mail,s.createHash(d.form.pass),d.form.details]) s.tx({f:'add_account',details:d.form.details,ke:d.form.ke,uid:d.form.uid,mail:d.form.mail},'$'); //init user s.loadGroup(d.form) } }) }else{ d.msg=lang["Passwords Don't Match"]; } }else{ d.msg=lang['Fields cannot be empty']; } if(d.msg){ s.tx({f:'error',ff:'account_register',msg:d.msg},cn.id) } break; case'edit': s.sqlQuery('SELECT * FROM Users WHERE mail=?',[d.account.mail],function(err,r) { if(r && r[0]){ r = r[0] var details = JSON.parse(r.details) if(d.form.pass&&d.form.pass!==''){ if(d.form.pass===d.form.password_again){ d.form.pass=s.createHash(d.form.pass); }else{ s.tx({f:'error',ff:'edit_account',msg:lang["Passwords Don't Match"]},cn.id) return } }else{ delete(d.form.pass); } delete(d.form.password_again); d.keys=Object.keys(d.form); d.set=[]; d.values=[]; d.keys.forEach(function(v,n){ if(d.set==='ke'||d.set==='password_again'||!d.form[v]){return} d.set.push(v+'=?') if(v === 'details'){ d.form[v] = JSON.stringify(Object.assign(details,JSON.parse(d.form[v]))) } d.values.push(d.form[v]) }) d.values.push(d.account.mail) s.sqlQuery('UPDATE Users SET '+d.set.join(',')+' WHERE mail=?',d.values,function(err,r) { if(err){ console.log(err) s.tx({f:'error',ff:'edit_account',msg:lang.AccountEditText1},cn.id) return } s.tx({f:'edit_account',form:d.form,ke:d.account.ke,uid:d.account.uid},'$'); delete(s.group[d.account.ke].init); s.loadGroupApps(d.account) }) } }) break; case'delete': s.sqlQuery('DELETE FROM Users WHERE uid=? AND ke=? AND mail=?',[d.account.uid,d.account.ke,d.account.mail]) s.sqlQuery('DELETE FROM API WHERE uid=? AND ke=?',[d.account.uid,d.account.ke]) s.tx({f:'delete_account',ke:d.account.ke,uid:d.account.uid,mail:d.account.mail},'$'); break; } break; } } } }) // admin page socket functions cn.on('a',function(d){ if(!cn.init&&d.f=='init'){ s.sqlQuery('SELECT * FROM Users WHERE auth=? AND uid=?',[d.auth,d.uid],function(err,r){ if(r&&r[0]){ r=r[0]; if(!s.group[d.ke]){s.group[d.ke]={users:{}}} if(!s.group[d.ke].users[d.auth]){s.group[d.ke].users[d.auth]={cnid:cn.id,uid:d.uid,ke:d.ke,auth:d.auth}} try{s.group[d.ke].users[d.auth].details=JSON.parse(r.details)}catch(er){} cn.join('ADM_'+d.ke); cn.ke=d.ke; cn.uid=d.uid; cn.auth=d.auth; cn.init='admin'; }else{ cn.disconnect(); } }) }else{ s.auth({auth:d.auth,ke:d.ke,id:d.id,ip:cn.request.connection.remoteAddress},function(user){ if(!user.details.sub){ switch(d.f){ case'accounts': switch(d.ff){ case'edit': d.keys=Object.keys(d.form); d.condition=[]; d.value=[]; d.keys.forEach(function(v){ d.condition.push(v+'=?') d.value.push(d.form[v]) }) d.value=d.value.concat([d.ke,d.$uid]) s.sqlQuery("UPDATE Users SET "+d.condition.join(',')+" WHERE ke=? AND uid=?",d.value) s.tx({f:'edit_sub_account',ke:d.ke,uid:d.$uid,mail:d.mail,form:d.form},'ADM_'+d.ke); s.sqlQuery("SELECT * FROM API WHERE ke=? AND uid=?",[d.ke,d.$uid],function(err,rows){ if(rows && rows[0]){ rows.forEach(function(row){ delete(s.api[row.code]) }) } }) break; case'delete': s.sqlQuery('DELETE FROM Users WHERE uid=? AND ke=? AND mail=?',[d.$uid,d.ke,d.mail]) s.sqlQuery("SELECT * FROM API WHERE ke=? AND uid=?",[d.ke,d.$uid],function(err,rows){ if(rows && rows[0]){ rows.forEach(function(row){ delete(s.api[row.code]) }) s.sqlQuery('DELETE FROM API WHERE uid=? AND ke=?',[d.$uid,d.ke]) } }) s.tx({f:'delete_sub_account',ke:d.ke,uid:d.$uid,mail:d.mail},'ADM_'+d.ke); break; } break; } } }) } }) //functions for webcam recorder cn.on('r',function(d){ if(!cn.ke&&d.f==='init'){ s.sqlQuery('SELECT ke,uid,auth,mail,details FROM Users WHERE ke=? AND auth=? AND uid=?',[d.ke,d.auth,d.uid],function(err,r) { if(r&&r[0]){ r=r[0] cn.ke=d.ke,cn.uid=d.uid,cn.auth=d.auth; if(!s.group[d.ke])s.group[d.ke]={}; if(!s.group[d.ke].users)s.group[d.ke].users={}; if(!s.group[d.ke].dashcamUsers)s.group[d.ke].dashcamUsers={}; s.group[d.ke].users[d.auth]={ cnid:cn.id, ke : d.ke, uid:r.uid, mail:r.mail, details:JSON.parse(r.details), logged_in_at:s.timeObject(new Date).format(), login_type:'Streamer' } s.group[d.ke].dashcamUsers[d.auth] = s.group[d.ke].users[d.auth] if(s.group[d.ke].activeMonitors){ Object.keys(s.group[d.ke].activeMonitors).forEach(function(monitorId){ var dataToClient = { f : 'disable_stream', mid : monitorId, ke : d.ke } var mon = s.group[d.ke].activeMonitors[monitorId] if(s.group[d.ke].rawMonitorConfigurations[monitorId].type === 'dashcam'){ if(mon.allowStdinWrite === true){ dataToClient.f = 'enable_stream' } s.tx(dataToClient,cn.id) } }) } } }) }else{ if(s.group[d.ke] && s.group[d.ke].activeMonitors[d.mid]){ if(s.group[d.ke].activeMonitors[d.mid].allowStdinWrite === true){ switch(d.f){ case'monitor_chunk': if(s.group[d.ke].activeMonitors[d.mid].isStarted !== true || !s.group[d.ke].activeMonitors[d.mid].spawn || !s.group[d.ke].activeMonitors[d.mid].spawn.stdin){ s.tx({error:'Not Started'},cn.id); return false }; s.group[d.ke].activeMonitors[d.mid].spawn.stdin.write(new Buffer(d.chunk, "binary")); break; case'monitor_frame': if(s.group[d.ke].activeMonitors[d.mid].isStarted !== true){ s.tx({error:'Not Started'},cn.id); return false }; s.group[d.ke].activeMonitors[d.mid].spawn.stdin.write(d.frame); break; } }else{ s.tx({error:'Cannot Write Yet'},cn.id) } }else{ s.tx({error:'Non Existant Monitor'},cn.id) } } }) //embed functions cn.on('e', function (d) { tx=function(z){if(!z.ke){z.ke=cn.ke;};cn.emit('f',z);} switch(d.f){ case'init': if(!s.group[d.ke]||!s.group[d.ke].activeMonitors[d.id]||s.group[d.ke].activeMonitors[d.id].isStarted === false){return false} s.auth({auth:d.auth,ke:d.ke,id:d.id,ip:cn.request.connection.remoteAddress},function(user){ cn.embedded=1; cn.ke=d.ke; if(!cn.mid){cn.mid={}} cn.mid[d.id]={}; // if(!s.group[d.ke].embed){s.group[d.ke].embed={}} // if(!s.group[d.ke].embed[d.mid]){s.group[d.ke].embed[d.mid]={}} // s.group[d.ke].embed[d.mid][cn.id]={} s.camera('watch_on',d,cn,tx) cn.join('MON_'+d.ke+d.id); cn.join('MON_STREAM_'+d.ke+d.id); cn.join('DETECTOR_'+d.ke+d.id); cn.join('STR_'+d.ke); if(s.group[d.ke]&&s.group[d.ke].activeMonitors[d.id]&&s.group[d.ke].activeMonitors[d.id].watch){ tx({f:'monitor_watch_on',id:d.id,ke:d.ke},'MON_'+d.ke+d.id) s.tx({viewers:Object.keys(s.group[d.ke].activeMonitors[d.id].watch).length,ke:d.ke,id:d.id},'MON_'+d.ke+d.id) } }); break; } }) //functions for retrieving cron announcements cn.on('cron',function(d){ if(d.f==='init'){ if(config.cron.key){ if(config.cron.key===d.cronKey){ s.cron={started:moment(),last_run:moment(),id:cn.id}; }else{ cn.disconnect() } }else{ s.cron={started:moment(),last_run:moment(),id:cn.id}; } }else{ if(s.cron&&cn.id===s.cron.id){ delete(d.cronKey) switch(d.f){ case'filters': s.filterEvents(d.ff,d); break; case's.tx': s.tx(d.data,d.to) break; case's.deleteVideo': s.deleteVideo(d.file) break; case'start':case'end': d.mid='_cron';s.userLog(d,{type:'cron',msg:d.msg}) break; default: s.systemLog('CRON : ',d) break; } }else{ cn.disconnect() } } }) cn.on('disconnect', function () { if(cn.socketVideoStream){ cn.closeSocketVideoStream() return } if(cn.ke){ if(cn.monitorsCurrentlyWatching){ cn.monitor_count=Object.keys(cn.monitorsCurrentlyWatching) if(cn.monitor_count.length>0){ cn.monitor_count.forEach(function(v){ s.camera('watch_off',{id:v,ke:cn.monitorsCurrentlyWatching[v].ke},{id:cn.id,ke:cn.ke,uid:cn.uid}) }) } }else if(!cn.embedded){ if(s.group[cn.ke].users[cn.auth]){ if(s.group[cn.ke].users[cn.auth].login_type === 'Dashboard'){ s.tx({f:'user_status_change',ke:cn.ke,uid:cn.uid,status:0}) } s.userLog({ke:cn.ke,mid:'$USER'},{type:lang['Websocket Disconnected'],msg:{mail:s.group[cn.ke].users[cn.auth].mail,id:cn.uid,ip:cn.ip}}) delete(s.group[cn.ke].users[cn.auth]); } if(s.group[cn.ke].dashcamUsers && s.group[cn.ke].dashcamUsers[cn.auth])delete(s.group[cn.ke].dashcamUsers[cn.auth]); } } if(cn.cron){ delete(s.cron); } if(cn.superSessionKey){ delete(s.superUsersApi[cn.superSessionKey]) } s.onWebSocketDisconnectionExtensions.forEach(function(extender){ extender(cn) }) delete(s.clientSocketConnection[cn.id]) }) s.onWebSocketConnectionExtensions.forEach(function(extender){ extender(cn) }) }); }